Send to OneNote from Windows Explorer – Sample App
It has been awhile since I have blogged about the OneNote API so I thought I would post a nice long article showing a sample app I made awhile ago.
Goal: Write an app that allows you to send a file from the Windows shell directly to OneNote, and it looks like this:
High-level: We will write an app that takes command line args that are paths to files. We will then create XML for a page in OneNote which contain embedded files pointing to these source files. Import this all into OneNote and success.
Requirements: Visual Studio 2005 & OneNote 2007 (this XML works with the RTM version)
Steps:
Create a new Windows application. Even though my app is 100% console based and doesn't need the Windows UI it is just good to create this so that you don't see the app's window on the Taskbar.
Add a reference to OneNote's COM object model. In the Solution Explorer find References, right-click on it and choose Add Reference. Go to the COM tab and hit "m" and look for Microsoft OneNote 12.0 Object Library, select it and choose OK.
Add this line for your using statements:
using OneNote = Microsoft.Office.Interop.OneNote;
For this sample I will create a new class to do all of the work required for this, and I will call it Send2OneNote. Send2OneNote will contain a reference to OneNote and it will also have a importFiles method that will create XML and import it into OneNote's Unfiled Notes section.
This is the class' constructor and destructor:
class
Send2OneNote{
private OneNote.Application onApp;
public Send2OneNote()
{
try
{
onApp = new Microsoft.Office.Interop.OneNote.Application();
}
catch(Exception e)
{
MessageBox.Show("Please install Micorosft Office OneNote 2007");
onApp = null;
Environment.Exit(-99);
}
}
~Send2OneNote()
{
onApp = null;
}
As you can see when you first instantiate the class it will get a COM reference to OneNote, if OneNote isn't installed then we will throw a dialog box @ the user and we exit. Don't ask me why I use exit error code -99, heck I imagine that most of you will say that you don't even need an error exit code, but whatever : )
Now we will want to start writing importFiles, but we need to think about what it needs. I mentioned earlier that we will be add a new page to the unfiled notes section. And if you are familiar with the OneNote API you can see from the CreateNewPage() call that we need an ID to pass in, so I will write a small helper function called findUnfiledID() which returns the ID to the Unfiled Notes section; here it is:
private
string findUnfiledID(){
string unfiledID = "";
try
{
string unfiledPath;
onApp.GetSpecialLocation(Microsoft.Office.Interop.OneNote.SpecialLocation.slUnfiledNotesSection, out unfiledPath);
onApp.OpenHierarchy(unfiledPath, null, out unfiledID, Microsoft.Office.Interop.OneNote.CreateFileType.cftNone);
}
catch (Exception e)
{
MessageBox.Show("Couldn't get your Unfiled Notes Location");
onApp = null;
Environment.Exit(-99);
}
return unfiledID;
}
What does this do? I call OneNote and I ask it for the path to the Unfiled Notes section (that is the GetSpecialLocation call) and then I ask OneNote to open that path and give me back the ID. You might ask why would I do this if OneNote already has the Unfiled Notes open, well this is an easy way for me to get the ID instead of needing to parse through XML or anything else. Anytime you ask OneNote to open something (with OpenHierarchy) then OneNote will return the ID back to you.
Okay so now we have the Unfiled Notes' ID and now we just need to create the XML and insert it into OneNote. Let's look @ a page that contains an embedded file, just so we can get an idea of what we will need to create ourselves:
<?xml
version="1.0"?><one:Page
xmlns:one="https://schemas.microsoft.com/office/onenote/2007/onenote"
ID="{9DF3CC2B-E016-4F0F-AB50-E9B62532839A}{1}{B0}"
name="Sent Files"><one:Title>
<one:OE
author="Daniel Escapa"
lastModifiedBy="Daniel Escapa"
creationTime="2006-10-04T02:13:43.000Z"
lastModifiedTime="2006-10-04T02:13:44.000Z"
objectID="{7F6FE609-34C0-4D0B-9A39-AB52DADC2BC2}{31}{B0}"
alignment="left"><one:T><![CDATA[Sent Files]]></one:T>
</one:OE>
</one:Title>
<one:InsertedFile
pathCache="D:\Documents and Settings\descapa\Local Settings\Application Data\Microsoft\OneNote\12.0\OneNoteOfflineCache_Files\Sample file.one"
pathSource="D:\Documents and Settings\descapa\Desktop\Sample file.one"
preferredName="Sample file.one"
lastModifiedTime="2006-10-04T02:13:44.000Z"
objectID="{7F6FE609-34C0-4D0B-9A39-AB52DADC2BC2}{36}{B0}"><one:Position
x="36.0"
y="86.4000015258789"
z="0" /><one:Size
width="54.0"
height="63.0" /></one:InsertedFile>
</one:Page>
I did take out some of the XML but you can see the basics of the page here. I have a title which says "Sent Files" and then there is an embedded file on the page which is for the file "Sample file.one". You can see that we have a pathSource and a pathCache attributes on the InsertedFile element. The IDs & objectIDs will be filled in by OneNote once the XML has been imported and there are other special items like exact positioning that we can ignore when we import content. If you really wanted to place this in a certain location we could do so, but let's just deal with the basics now.
You can easily embed a file in OneNote because you just need to specify the path via the XML and then OneNote will do the rest. You don't need to actually break down the file and put it on the page, just give it a path. OneNote will then take this, insert it into the file, and put a cached copy in the OneNote cache.
I think we can go and write insertFiles().
I will just paste the code for insertFiles and then we can discuss it and why I did it the way that I did:
public
void importFiles(string[] args){
string unfiledID = findUnfiledID();
string importedPageID = "";
try
{
onApp.CreateNewPage(unfiledID, out importedPageID, Microsoft.Office.Interop.OneNote.NewPageStyle.npsDefault);
}
catch (Exception e)
{
MessageBox.Show("Could not create a new page! Error code: " + e.Message);
onApp = null;
Environment.Exit(-99);
}
string xml2importBase = "<?xml version=\"1.0\"?>\n<one:Section xmlns:one=\"https://schemas.microsoft.com/office/onenote/2007/onenote\" ID=\"";
xml2importBase += unfiledID + "\">\n";
xml2importBase += "<one:Page ID=\"" + importedPageID + "\">\n";
xml2importBase += "<one:Title>\n<one:OE>\n<one:T><![CDATA[Sent Files]]></one:T>\n</one:OE>\n</one:Title>\n";
string tailXml = "</one:Page>\n</one:Section>";
bool succeeded = false;
foreach (string s in args)
{
string xml2import = xml2importBase + "<one:InsertedFile pathSource=\"" + s + "\"></one:InsertedFile>\n";
xml2import += tailXml;
try
{
onApp.UpdateHierarchy(xml2import);
succeeded = true;
}
catch(Exception e)
{
if (args.Length == 1)
{
try
{
onApp.DeleteHierarchy(importedPageID, DateTime.MinValue);
}
catch (Exception e1)
{
MessageBox.Show("Could not delete page and error on insert, bailing! Error: " + e1.Message);
onApp = null;
Environment.Exit(-99);
}
MessageBox.Show("You cannot import a folder into OneNote!");
onApp = null;
Environment.Exit(-99);
}
else
{
//supressed, we just hit a folder but we will jsut keep on going...
//MessageBox.Show("Couldn't import " + s + " into OneNote. Error code: " + e.Message);
}
}
}
if (!succeeded)
{
try
{
onApp.DeleteHierarchy(importedPageID, DateTime.MinValue);
}
catch (Exception e)
{
MessageBox.Show("Failed to delete the page with nothing on it. Error: " + e.Message);
//add code to remove this stuff!
}
MessageBox.Show("You cannot send folders to OneNote!");
}
else
{
onApp.NavigateTo(importedPageID, null, false);
}
}
*wipes brow* Okay lots going on here so I will take look @ each part.
Creating a new page in the Unfiled Notes section:
public
void importFiles(string[] args){
string unfiledID = findUnfiledID();
string importedPageID = "";
try
{
onApp.CreateNewPage(unfiledID, out importedPageID, Microsoft.Office.Interop.OneNote.NewPageStyle.npsDefault);
}
catch (Exception e)
{
MessageBox.Show("Could not create a new page! Error code: " + e.Message);
onApp = null;
Environment.Exit(-99);
}
This code will create a new page, with CreateNewPage, in the section that I supply with unfiledID and then it will output the new pageID. If it fails then we capture that error code but in most cases this should never fail, but you _just_ never know.
Now I have the main XML that I am importing into OneNote:
string xml2importBase = "<?xml version=\"1.0\"?>\n<one:Section xmlns:one=\"https://schemas.microsoft.com/office/onenote/2007/onenote\" ID=\"";
xml2importBase += unfiledID + "\">\n";
xml2importBase += "<one:Page ID=\"" + importedPageID + "\">\n";
xml2importBase += "<one:Title>\n<one:OE>\n<one:T><![CDATA[Sent Files]]></one:T>\n</one:OE>\n</one:Title>\n";
string tailXml = "</one:Page>\n</one:Section>";
The next part is the main part of code which inserts this into OneNote:
bool succeeded = false;
foreach (string s in args)
{
string xml2import = xml2importBase + "<one:InsertedFile pathSource=\"" + s + "\"></one:InsertedFile>\n";
xml2import += tailXml;
try
{
onApp.UpdateHierarchy(xml2import);
succeeded = true;
}
catch(Exception e)
{
if (args.Length == 1)
{
try
{
onApp.DeleteHierarchy(importedPageID, DateTime.MinValue);
}
catch (Exception e1)
{
MessageBox.Show("Could not delete page and error on insert, bailing! Error: " + e1.Message);
onApp = null;
Environment.Exit(-99);
}
MessageBox.Show("You cannot import a folder into OneNote!");
onApp = null;
Environment.Exit(-99);
}
else
{
//supressed, we just hit a folder but we will jsut keep on going...
//MessageBox.Show("Couldn't import " + s + " into OneNote. Error code: " + e.Message);
}
}
}
This might seem like a lot of code but what I am doing is going through each string in the arguments (that came from Windows Explorer when I chose to send to OneNote).
There is a lot more going on with this code for example I put all of the XML together and you can see to embed a file you only need to provide the pathSource. I then send this to OneNote with the UpdateHierarchy method, you might ask why I do this for each individual file instead of creating the XML for all of the files and import that together? I did this because I wanted my imports to be more transactional; if something failed to import I wanted to know more about it and have something atomic that I could rollback if need be. It also made my code pretty clean and easy (imho).
If we failed on the import that means that we tried to insert something which cannot be embedded which was probably a folder. If the user only tried to import the folder this is invalid and we throw an error to the user. I also go ahead and delete the page that I just created, otherwise the user would have a blank page in their Unfiled Notes.
However if we were successful I go through and do this for each file that was passed to our app.
The final part of this code is error handling and last steps. If we were successful importing the files we will navigate the user to the newly created page, otherwise we will delete the new blank page because the user tried to send only folders which are invalid. See here:
if (!succeeded)
{
try
{
onApp.DeleteHierarchy(importedPageID, DateTime.MinValue);
}
catch (Exception e)
{
MessageBox.Show("Failed to delete the page with nothing on it. Error: " + e.Message);
//add code to remove this stuff!
}
MessageBox.Show("You cannot send folders to OneNote!");
}
else
{
onApp.NavigateTo(importedPageID, null, false);
}
The last steps would be to test this out and put a shortcut in your SendTo folder under your user profile. At some point I hope to package this all up and make it a download that anyone can easily use. I guess I will have to wait until RTM when we don't have any more changes in the code.
As always I appreciate your feedback and thoughts on this, hope you learned something.
Update: I am including the source as well, you can find it here: SendtoOneNote.zip
Comments
Anonymous
October 03, 2006
PingBack from http://www.onenotepowertoys.com/2006/10/04/send-to-onenote-from-windows-explorer-sample-app/Anonymous
October 04, 2006
Nice example. Thanks.Anonymous
October 04, 2006
I thank you for your time and efforts to keep us abreast of these applications with Windows Explorer. I don't usually have good usage of them either (e.g., at times). But, anyway THNAK YOU for your TIME. Respectfully, Pam SwaffordAnonymous
December 10, 2006
Thank you for the great example. My 11 yr old son is interested in learning program code and this will make a great example. I am not going to let him download the zip file but have him enter the code following your instructions. It has been more than 20 yrs since I programed so you know how out of date I am. If this question is too revealing of my ignorance please bear with me. Will this code work for OneNote 2003 SP1? If not can it be easily adapted? and can you tell me how? Thank you, I have never run across code like this before .. I want to look for more ... my son will enjoy learning from practical applications that the whole family can use. Thanks KevinHAnonymous
December 11, 2006
KevinH - This code will not work with OneNote 2003, only OneNote 2007 so sadly you would need to download the trial or find some other code. That being said I have some pointers for you: o Code4Fun (http://msdn.microsoft.com/coding4fun/default.aspx) which is a site to help kids learn how to program. o Camp CAEN (http://www.engin.umich.edu/caen/campcaen/) this is a camp that I used to work at. During my time there I taught a great many kids how to program in Java. I am sure there are other camps nearby where you live if you aren't in the midwest. Hope this helps and please keep your son interested in programming! Take careAnonymous
February 15, 2007
Finally a powertoy that I wrote! This is a small application that I blogged about before where I includedAnonymous
March 17, 2007
Lo trovo piuttosto impressionante. Lavoro grande fatto..)Anonymous
April 07, 2007
9 su 10! Ottenerlo! Siete buoni!Anonymous
April 09, 2007
Interessieren. SEHR interessant! ;)Anonymous
April 12, 2007
L'information interessante que vous avez! I'am allant revenir bientot.Anonymous
April 15, 2007
Lo trovo piuttosto impressionante. Lavoro grande fatto..)Anonymous
April 16, 2007
pagine piuttosto informative, piacevoli =)Anonymous
July 25, 2007
The comment has been removedAnonymous
July 26, 2007
Hi, Could you explain how to add the short cut key to the oneNote 2007 application in the SendTo option.Anonymous
August 08, 2007
Alok - I don't programm in VB but you should search online and discuss with VB.Net developers who might be able to help you. Kaushik - I don't understand your question, do you want to know how to add the shortcut key to this application? This wouldn't work so well since you would have to have a file path that you send to this application, I don't think it would do what you are interested in.Anonymous
September 02, 2007
Hi Daniel, For our application we need to have a thread which runs whenever onenote application is running. But the problem is we are not able to find any event for it (like the addin_startup event in outlook) We also donot want to write any windows service for it. Is there any way out?Anonymous
November 13, 2008
<a href='http://undiefan.com/blog/wp-content/uploads/2007/news-129-2008-09-15.html'>albuterol effects side sulfate</a> <a href="http://undiefan.com/blog/wp-content/uploads/2007/news-129-2008-09-15.html">albuterol effects side sulfate</a> [link=http://undiefan.com/blog/wp-content/uploads/2007/news-129-2008-09-15.html]albuterol effects side sulfate[/link]Anonymous
November 13, 2008
<a href='http://mennlaw.com/pdf/docs/comment1096.htm'>bowel cancer</a> <a href="http://mennlaw.com/pdf/docs/comment1096.htm">bowel cancer</a> [link=http://mennlaw.com/pdf/docs/comment1096.htm]bowel cancer[/link]Anonymous
November 13, 2008
<a href='http://wma-bg.com/files/gallery/thumbs/pic/news-2002.html'>trump plaza</a> <a href="http://wma-bg.com/files/gallery/thumbs/pic/news-2002.html">trump plaza</a> [link=http://wma-bg.com/files/gallery/thumbs/pic/news-2002.html]trump plaza[/link]Anonymous
November 13, 2008
cartoon children [URL=http://wma-bg.com/files/gallery/thumbs/pic/news-10.html]cartoon children[/URL] [url=http://wma-bg.com/files/gallery/thumbs/pic/news-10.html]cartoon children[/url] [url]http://wma-bg.com/files/gallery/thumbs/pic/news-10.html[/url]Anonymous
December 13, 2008
abu dhabi mall movies [URL=http://enpasel.ifrance.com/1023916822.htm]abu dhabi mall movies[/URL] [url=http://enpasel.ifrance.com/1023916822.htm]abu dhabi mall movies[/url] [url]http://enpasel.ifrance.com/1023916822.htm[/url]Anonymous
December 13, 2008
audio video rental [URL=http://ezpascal.strefa.pl/audio-video-rental.htm]audio video rental[/URL] [url=http://ezpascal.strefa.pl/audio-video-rental.htm]audio video rental[/url] [url]http://ezpascal.strefa.pl/audio-video-rental.htm[/url]Anonymous
April 13, 2009
Thank you very much! Very good and very helpful example!Anonymous
October 19, 2009
In the main Xml which is imported into one Note, the following element should not be present: </one:Section>Anonymous
November 23, 2010
HI Daniel, I followed your code, its only putting the file, but it is not embeding the file , i need the file to be embeded in the onenote pageAnonymous
July 08, 2013
Well, 2013 now, and this is still a good post about how to deal with OneNote API. Too bad the download is broken now, would've loved to have a complete sample! (Since I can't get the code snippets to work properly...) Thanks anyway! :)