1

I am relatively new to C# and Visual Studio, and I am completely new to Microsoft.Office.Interop.OneNote. I have begun work on a console app using as a starting place the code in this answer, which is a reply to a question about writing to a OneNote app using C# and interop. My app does successfully retrieve content from a OneNote notebook, but I am not sure how to close the app in such a way that OneNote shuts down cleanly. Whenever I run the app in Visual Studio and then close the console, the next time I try to open OneNote, I get a message saying "We're sorry. OneNote is cleaning up from the last time it was open. Please wait." After about 30 seconds, this is followed by a message saying "It looks like OneNote is having trouble starting right now. If you keep seeing this message, restart your computer and start OneNote again. We're sorry."

It is impossible to open OneNote again until I either reboot or empty the contents of C:\users%userprofile%\appdata\local\temp.

What is the proper code to use to close out of OneNote cleanly, and/or how should I be closing my app inside Visual Studio? (As I said, I'm a noob.)

Here is my code:

using System;
using Word = Microsoft.Office.Interop.Word;
using OneNote = Microsoft.Office.Interop.OneNote;
using System.Runtime.InteropServices;
using System.Xml.Linq;
using System.Linq;

namespace OneNote_to_Word
{
    class Program
    {
        static OneNote.Application onenoteApp = new OneNote.Application();
        static XNamespace ns = null;

        static void Main(string[] args)
        {
            GetNamespace();
            string notebookId = GetObjectId(null, OneNote.HierarchyScope.hsNotebooks, "Notebook Name");
            string sectionId = GetObjectId(notebookId, OneNote.HierarchyScope.hsSections, "done");
            string firstPageId = GetObjectId(sectionId, OneNote.HierarchyScope.hsPages, "140506");
            GetPageContent(firstPageId);
            Console.Read();
        }
        static void GetNamespace()
        {
            string xml;

            onenoteApp.GetHierarchy(null, OneNote.HierarchyScope.hsNotebooks, out xml);
            var doc = XDocument.Parse(xml);
            ns = doc.Root.Name.Namespace;
        }

        static string GetObjectId(string parentId, OneNote.HierarchyScope scope, string objectName)
        {
            string xml;
            onenoteApp.GetHierarchy(parentId, scope, out xml);

            var doc = XDocument.Parse(xml);
            var nodeName = "";

            switch (scope)
            {
                case (OneNote.HierarchyScope.hsNotebooks): nodeName = "Notebook"; break;
                case (OneNote.HierarchyScope.hsPages): nodeName = "Page"; break;
                case (OneNote.HierarchyScope.hsSections): nodeName = "Section"; break;
                default:
                    return null;
            }

            var node = doc.Descendants(ns + nodeName).Where(n => n.Attribute("name").Value == objectName).FirstOrDefault();

            return node.Attribute("ID").Value;
        }
        static string GetPageContent(string pageId)
        {
            string xml;
            onenoteApp.GetPageContent(pageId, out xml, OneNote.PageInfo.piAll);
            var doc = XDocument.Parse(xml);
            var outLine = doc.Descendants(ns + "Outline").First();
            var content = outLine.Descendants(ns + "T").First();
            string contentVal = content.Value;
            //content.Value = "modified";
            //onenoteApp.UpdatePageContent(doc.ToString());
            Console.WriteLine(contentVal);
            return null;
        }
    }
}
Erica Ackerman
  • 189
  • 1
  • 2
  • 11

1 Answers1

1

At least for .NET < 5, I needed to add the following lines at the end of main():

    Marshal.FinalReleaseComObject(onenoteApp);
    onenoteApp = null;
    GC.Collect(); // Start .NET CLR Garbage Collection
    GC.WaitForPendingFinalizers(); // Wait for Garbage Collection to finish

So main() now looks like this:

static void Main(string[] args)
    {
        GetNamespace();
        string notebookId = GetObjectId(null, OneNote.HierarchyScope.hsNotebooks, "My Notebook");
        string sectionId = GetObjectId(notebookId, OneNote.HierarchyScope.hsSections, "done");
        string firstPageId = GetObjectId(sectionId, OneNote.HierarchyScope.hsPages, "140506");
        GetPageContent(firstPageId);
        Marshal.FinalReleaseComObject(onenoteApp);
        onenoteApp = null;
        GC.Collect(); // Start .NET CLR Garbage Collection
        GC.WaitForPendingFinalizers(); // Wait for Garbage Collection to finish
    }

However, in .NET 5, when I tried to use Marshal.FinalReleaseComObject(), Visual Studio complained that "This call site is reachable on all platforms. 'Marshal.FinalReleaseComObject(object)' is only supported on: 'windows'". This problem could be fixed by placing this line above the containing class, main() (cf. this answer):

[System.Runtime.Versioning.SupportedOSPlatform("windows")]
Erica Ackerman
  • 189
  • 1
  • 2
  • 11
  • 1
    Thanks so much, this was driving me crazy! I did make a change though, based on the comments on this answer: https://stackoverflow.com/a/15730764/892770. They say you should try to avoid calling GC.Collect() manually, since it'll promote garbage each time. I tested it with just the FinalReleaseComObject and it still works fine, so I got rid of those last two lines. Cheers! – UnionP May 30 '22 at 16:59