Send to OneNote from Windows Explorer – Sample App

Send to OneNote from Windows Explorer – Sample App

  • Comments 26

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:

  1. 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.
  2. 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.
  3. Add this line for your using statements:

    using OneNote = Microsoft.Office.Interop.OneNote;

  4. 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.
  5. 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 : )

  6. 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.

  7. 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="http://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().

  8. 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=\"http://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.

  1. 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.

  2. Now I have the main XML that I am importing into OneNote:

    string xml2importBase = "<?xml version=\"1.0\"?>\n<one:Section xmlns:one=\"http://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>";

  3. 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.

  4. 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

Leave a Comment
  • Please add 1 and 4 and type the answer here:
  • Post
  • PingBack from http://www.onenotepowertoys.com/2006/10/04/send-to-onenote-from-windows-explorer-sample-app/

  • Nice example. Thanks.

  • 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 Swafford

  • 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

    KevinH

  • 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 care

  • Finally a powertoy that I wrote! This is a small application that I blogged about before where I included

  • Lo trovo piuttosto impressionante. Lavoro grande fatto..)

  • 9 su 10! Ottenerlo! Siete buoni!

  • Interessieren. SEHR interessant! ;)

  • L'information interessante que vous avez! I'am allant revenir bientot.

  • Lo trovo piuttosto impressionante. Lavoro grande fatto..)

  • pagine piuttosto informative, piacevoli =)

  • Hi,

    Is there any way to open One Note 2007 application using VB script/Java script?

    I  tried this VB script ...this failes because VB script is not supporting OUT parameter

      dim oneNote,pageStyle,importedPageID,unfiledPath,unfiledID

          set oNote = CreateObject("OneNote.Application")

          oNote.GetSpecialLocation 1,unfiledPath

          oNote.OpenHierarchy unfiledPath, "", unfiledID, 0

          oNote.CreateNewPage unfiledID,importedPageID, 0

          Dim xml2importBase 'As String

          xml2importBase = "<?xml version=""1.0""?><one:Section xmlns:one=""http://schemas.microsoft.com/office/onenote/2007/onenote"" ID="""

          xml2importBase = xml2importBase + unfiledID + """>"

          xml2importBase = xml2importBase + "<one:Page ID=""" + importedPageID + """>"

          xml2importBase = xml2importBase + "<one:Title><one:OE><one:T><![CDATA[Title Comes here-2]]></one:T></one:OE></one:Title>"

          Dim tailXml

          tailXml = "</one:Page></one:Section>"

          Dim xml2import

          xml2import = xml2importBase + tailXml

          oNote.UpdateHierarchy xml2import

          oNote.NavigateTo importedPageID, "", False

    I know we cant send out parameter in the VB script.

    So Is there any way to overcome this problem and make this script run?

    Or can I import XML to the OneNote2007 application without SectionID and PageID?

    alok.pagariya@persistent.co.in

  • Hi,

    Could you explain how to add the short cut key to the oneNote 2007 application in the SendTo option.

  • 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.

Page 1 of 2 (26 items) 12