/// <summary> /// Make a call into the OneNote application which is protected against recoverable errors. /// </summary> /// <param name="cmd">lambda function calling into the OneNote application object</param> /// <returns>Return value of the lambda function</returns> /// <typeparam name="Tresult">Return type of the protected lambda method</typeparam> /// <example>string id = onenote.ExecuteMethodProtected(o => {return o.Windows.CurrentWindow;});</example> private Tresult ExecuteMethodProtected <Tresult>(Func <Application, Tresult> cmd) { int retries = MAX_RETRIES; while (retries-- > 0) { try { return(cmd.Invoke(_on)); } catch (COMException ce) { if ((uint)ce.ErrorCode == 0x8001010A && retries >= 0) { // RPC_E_SERVERCALL_RETRYLATER TraceLogger.Log(TraceCategory.Info(), "OneNote busy. Retrying method invocation ..."); Thread.Sleep(1000); // wait until COM Server becomes responsive } else { TraceLogger.Log(TraceCategory.Error(), "Unrecoverable COM exception while executing OneNote method: {0}", ce.Message); TraceLogger.Log(TraceCategory.Error(), ce.StackTrace); TraceLogger.Log(TraceCategory.Error(), "Re-throwing exception"); throw; } } catch (Exception e) { TraceLogger.Log(TraceCategory.Error(), "Exception while executing OneNote method: {0}", e.Message); TraceLogger.Log(TraceCategory.Error(), e.StackTrace); throw; } } return(default(Tresult)); }
/// <summary> /// Get images for ribbon bar buttons /// </summary> /// <param name="imageName">name of image to get</param> /// <returns>image stream</returns> public IStream GetImage(string imageName) { MemoryStream mem = new MemoryStream(); switch (imageName) { case "pageTags.png": Properties.Resources.tag_32x32.Save(mem, ImageFormat.Png); break; case "managePageTags.png": Properties.Resources.settings_32x32.Save(mem, ImageFormat.Png); break; case "findPageTags.png": Properties.Resources.tagSearch_32x32.Save(mem, ImageFormat.Png); break; default: TraceLogger.Log(TraceCategory.Warning(), "Unknown image requested: {0}", imageName); Properties.Resources.tag_32x32.Save(mem, ImageFormat.Png); break; } return(new COMReadonlyStreamAdapter(mem)); }
/// <summary> /// Show a singleton WPF dialog. /// </summary> /// <typeparam name="T">dialog type</typeparam> /// <typeparam name="M">view model type</typeparam> /// <param name="viewModelFactory">factory lambda function to create a view model</param> /// <returns>dialog result</returns> public static bool?ShowDialog <T, M>(Func <M> viewModelFactory) where T : System.Windows.Window, IOneNotePageWindow <M>, new() where M : WindowViewModelBase { bool?retval = null; var thread = new Thread(() => { try { System.Windows.Window w = new T(); w.Closed += (s, e) => w.Dispatcher.InvokeShutdown(); w.Topmost = true; M viewmodel = viewModelFactory(); ((IOneNotePageWindow <M>)w).ViewModel = viewmodel; var helper = new WindowInteropHelper(w) { Owner = (IntPtr)viewmodel.OneNoteApp.CurrentWindow.WindowHandle }; BringWindowIntoView(w); retval = w.ShowDialog(); Trace.Flush(); } catch (Exception ex) { TraceLogger.Log(TraceCategory.Error(), "Exception while creating dialog: {0}", ex); TraceLogger.ShowGenericErrorBox(Properties.Resources.TagEditor_Error_WindowCreation, ex); } }); thread.SetApartmentState(ApartmentState.STA); thread.IsBackground = true; thread.Start(); thread.Join(); return(retval); }
/// <summary> /// Create a new instance of a OneNote proxy. /// </summary> /// <param name="onenote">OneNote application object</param> internal OneNoteProxy(Application onenote) { _on = onenote; TaggingService = new BackgroundTagger(this); TaggingService.Run(); TraceLogger.Log(TraceCategory.Info(), "OneNote application proxy constructed successfully"); TraceLogger.Flush(); }
/// <summary> /// Find pages by full text search /// </summary> /// <param name="query"> query string</param> /// <param name="scopeID"> /// OneNote id of the scope to search for pages. This is the element ID of a /// notebook, section group, or section. If given as null or empty string scope is /// the entire set of notebooks open in OneNote. /// </param> /// <returns>XML page descriptors</returns> public XDocument FindPages(string scopeID, string query) { TraceLogger.Log(TraceCategory.Info(), "query={0}; Scope = {1}", query, scopeID); return(ExecuteMethodProtected <XDocument>(o => { string outXml; o.FindPages(scopeID, query, out outXml, false, fDisplay: false, xsSchema: OneNoteSchema); return XDocument.Parse(outXml); })); }
/// <summary> /// Get the XML descriptor of nodes in the OneNote hierarchy /// </summary> /// <remarks>Only basic information (as of OneNote 2010) is returned.</remarks> /// <param name="nodeID">id of the starting node</param> /// <param name="scope"> scope of the nodes to return</param> /// <returns>XML document describing the nodes in the OneNote hierarchy</returns> /// <exception cref="COMException">Call to OneNote failed</exception> public XDocument GetHierarchy(string nodeID, HierarchyScope scope) { TraceLogger.Log(TraceCategory.Info(), "Start Node = {0}; Scope = {1}", nodeID, scope); return(ExecuteMethodProtected <XDocument>(o => { string outXml; o.GetHierarchy(nodeID, scope, out outXml, OneNoteSchema); return XDocument.Parse(outXml); })); }
/// <summary> /// Find OneNote pages which have meta-data with a given key. /// </summary> /// <param name="scopeID"> /// search scope. The id of a node in the hierarchy (notebook, section group, or /// section) below which to search for content. If null or empty string, the search /// scope is the entire set of notebooks open in OneNote. for the search. /// </param> /// <param name="metadataKey">Key (name) of the meta-data</param> /// <returns>page descriptors of pages with the requested meta-data</returns> public XDocument FindPagesByMetadata(string scopeID, string metadataKey) { TraceLogger.Log(TraceCategory.Info(), "Scope = {0}; metaKey = {1}", scopeID, metadataKey); return(ExecuteMethodProtected <XDocument>(o => { string outXml; o.FindMeta(scopeID, metadataKey, out outXml, false, OneNoteSchema); return XDocument.Parse(outXml); })); }
/// <summary> /// Show a WPF window. /// </summary> /// <typeparam name="W">window type</typeparam> /// <typeparam name="M">view model type</typeparam> /// <param name="viewModelFactory">factory method to generate the view model in the UI thread of the WPF window</param> public void Show <W, M>(Func <M> viewModelFactory) where W : System.Windows.Window, IOneNotePageWindow <M>, new() where M : WindowViewModelBase { var thread = new Thread(() => { try { lock (_SingletonWindows) { System.Windows.Window w; if (_SingletonWindows.TryGetValue(typeof(W), out w)) { w.Dispatcher.Invoke(() => { w.WindowState = WindowState.Normal; BringWindowIntoView(w); }); return; } w = new W(); w.Closing += (o, e) => UnregisterWindow(typeof(W)); w.Closed += (s, e) => w.Dispatcher.InvokeShutdown(); M viewmodel = viewModelFactory(); ((IOneNotePageWindow <M>)w).ViewModel = viewmodel; var helper = new WindowInteropHelper(w) { Owner = (IntPtr)viewmodel.OneNoteApp.CurrentWindow.WindowHandle }; w.Show(); BringWindowIntoView(w); _SingletonWindows.Add(typeof(W), w); } // Turn this thread into an UI thread System.Windows.Threading.Dispatcher.Run(); } catch (ThreadAbortException ta) { TraceLogger.Log(TraceCategory.Warning(), "Window Thread aborted: {0}", ta); } catch (Exception ex) { TraceLogger.Log(TraceCategory.Error(), "Exception while creating dialog: {0}", ex); TraceLogger.ShowGenericErrorBox(Properties.Resources.TagEditor_Error_WindowCreation, ex); } }); thread.SetApartmentState(ApartmentState.STA); thread.IsBackground = true; thread.Start(); }
/// <summary> /// Occurs whenever an add-in loads. /// </summary> /// <param name="custom"> /// An empty array that you can use to pass host-specific data for use when the /// add-in loads. /// </param> public void OnStartupComplete(ref Array custom) { TraceLogger.Log(TraceCategory.Info(), "Startup Arguments '{0}'", custom); try { XMLSchema s = _onProxy.OneNoteSchema; // cache the schema } catch (Exception ex) { TraceLogger.Log(TraceCategory.Error(), "{0} initialization failed: {1}", Properties.Resources.TaggingKit_About_Appname, ex); TraceLogger.Flush(); throw; } TraceLogger.Log(TraceCategory.Info(), "{0} initialization complete!", Properties.Resources.TaggingKit_About_Appname); TraceLogger.Flush(); }
/// <summary> /// Handle the connection to the OneNote application. /// </summary> /// <param name="app"> The instance of OneNote which added the add-in</param> /// <param name="ConnectMode"> /// Enumeration value that indicates the way the add-in was loaded. /// </param> /// <param name="AddInInst"> Reference to the add-in's own instance</param> /// <param name="custom"> /// An empty array that you can use to pass host-specific data for use in the add-in /// </param> public void OnConnection(object app, ext_ConnectMode ConnectMode, object AddInInst, ref Array custom) { try { TraceLogger.Log(TraceCategory.Info(), "Connection mode '{0}'", ConnectMode); _onProxy = new OneNoteProxy(app as Microsoft.Office.Interop.OneNote.Application); _dialogmanager = new AddInDialogManager(); TraceLogger.Flush(); } catch (Exception ex) { TraceLogger.Log(TraceCategory.Error(), "Connecting {0} failed: {1}", Properties.Resources.TaggingKit_About_Appname, ex); TraceLogger.Flush(); throw; } }
/// <summary> /// Occurs whenever OneNote shuts down while an add-in is running. /// </summary> /// <param name="custom"> /// An empty array that you can use to pass host-specific data for use in the add-in. /// </param> public void OnBeginShutdown(ref Array custom) { TraceLogger.Log(TraceCategory.Info(), "Beginning {0} shutdown; Arguments '{1}'", Properties.Resources.TaggingKit_About_Appname, custom); if (_dialogmanager != null) { _dialogmanager.Dispose(); _dialogmanager = null; } if (_onProxy != null) { _onProxy.Dispose(); _onProxy = null; } }
/// <summary> /// log a message. /// </summary> /// <param name="category">logging category</param> /// <param name="message"> logging message</param> /// <param name="args"> parameters for the logging message</param> internal static void Log(TraceCategory category, string message, params object[] args) { #if TRACE Trace.Write(category.CallerName, category.Category); Trace.Write(" ("); Trace.Write(category.Line); Trace.Write(") | "); try { Trace.WriteLine(string.Format(message, args)); } catch (Exception ex) { Log(TraceCategory.Error(), "Logging failed {0}", ex); } #endif //TRACE }
/// <summary> /// Show an error box for an exception. /// </summary> /// <param name="message">Message to describe the failing operation</param> /// <param name="ex"> exception</param> internal static void ShowGenericErrorBox(string message, Exception ex) { TraceLogger.Log(TraceCategory.Error(), "{0}: {1}", message, ex); Trace.Flush(); MessageBoxResult result = MessageBox.Show(string.Format(Properties.Resources.TaggingKit_ErrorBox_GenericSevereError, message, ex.Message, TraceLogger.LogFile), string.Format(Properties.Resources.TaggingKit_ErrorBox_Title, Properties.Resources.TaggingKit_About_Appname), MessageBoxButton.OKCancel, MessageBoxImage.Error); if (result == MessageBoxResult.OK) { // browse to the troubleshooting tips string wikipage = "Troubleshooting-Tips"; COMException ce = ex as COMException; if (ce != null) { switch ((uint)ce.ErrorCode) { case 0x80042019: wikipage = "0x80042019"; break; case 0x8004200C: wikipage = "0x8004200C"; break; } } try { Process.Start(new ProcessStartInfo(string.Format(Properties.Resources.TaggingKit_Wiki_Page, wikipage))); } catch (Exception ex1) { TraceLogger.Log(TraceCategory.Error(), "Failed to open web browser: {0}", ex1); } } }
public void Dispose() { if (!_disposed) { _disposed = true; // We do not lock the window collection because closing a window also // attempts to lock that collection. foreach (System.Windows.Window w in _SingletonWindows.Values.ToArray()) { try { w.Dispatcher.Invoke(() => { TraceLogger.Log(TraceCategory.Info(), "Closing Window: {0}", w.Title); w.Close(); }); } catch (Exception e) { TraceLogger.Log(TraceCategory.Error(), "Closing window failed: {0}", e); } } } }
/// <summary> /// handle disconnection of the OneNote application. /// </summary> /// <param name="RemoveMode"> /// Enumeration value that informs an add-in why it was unloaded /// </param> /// <param name="custom"> /// An empty array that you can use to pass host-specific data for use after the /// add-in unloads. /// </param> public void OnDisconnection(ext_DisconnectMode RemoveMode, ref Array custom) { TraceLogger.Log(TraceCategory.Info(), "Disconnecting; mode='{0}'; Arguments: '{1}'", RemoveMode, custom); if (_dialogmanager != null) { _dialogmanager.Dispose(); _dialogmanager = null; } Trace.Flush(); GC.Collect(); GC.WaitForPendingFinalizers(); if (RemoveMode == ext_DisconnectMode.ext_dm_HostShutdown || RemoveMode == ext_DisconnectMode.ext_dm_UserClosed) { // a dirty hack to make sure the ddlhost shuts down after an exception // occurred. This is necessary to allow the add-in to be loaded // successfully next time OneNote starts (a zombie dllhost would prevent that) TraceLogger.Log(TraceCategory.Info(), "Forcing COM Surrogate shutdown"); Trace.Flush(); Environment.Exit(0); } }
/// <summary> /// register the logging utility with the tracing system. /// </summary> internal static void Register() { FileStream log = new FileStream(LogFile, FileMode.OpenOrCreate); // Creates the new trace listener. TextWriterTraceListener listener = new TextWriterTraceListener(log); Trace.Listeners.Add(listener); #if DEBUG string config = "Debug"; #else string config = "Release"; #endif Log(TraceCategory.Info(), "{0} logging activated.\r\n\tAddin-Version: {1}\r\n\t.net Framework Version: {2}\r\n\tConfiguration: {3}", Properties.Resources.TaggingKit_About_Appname, Assembly.GetExecutingAssembly().GetName().Version, Environment.Version, config ); Flush(); }
/// <summary> /// Action to open the "Related Pages" UI /// </summary> /// <param name="ribbon">OneNote ribbon bar</param> public void relatedPages(IRibbonControl ribbon) { TraceLogger.Log(TraceCategory.Info(), "Show related pages tracer"); _dialogmanager.Show <RelatedPages, RelatedPagesModel>(() => new RelatedPagesModel(_onProxy)); }
/// <summary> /// Occurs whenever an add-in is loaded or unloaded. /// </summary> /// <param name="custom"> /// An empty array that you can use to pass host-specific data for use in the add-in. /// </param> public void OnAddInsUpdate(ref Array custom) { TraceLogger.Log(TraceCategory.Info(), "{0} update initiated; Arguments '{1}'", Properties.Resources.TaggingKit_About_Appname, custom); }
/// <summary> /// Action to open a tag management dialog. /// </summary> /// <param name="ribbon"></param> public void manageTags(IRibbonControl ribbon) { TraceLogger.Log(TraceCategory.Info(), "Show settings editor"); AddInDialogManager.ShowDialog <TagManager, TagManagerModel>(() => new TagManagerModel(_onProxy)); }
/// <summary> /// Action to open the search tags UI /// </summary> /// <param name="ribbon">OneNote ribbon bar</param> public void findTags(IRibbonControl ribbon) { TraceLogger.Log(TraceCategory.Info(), "Show tag finder"); _dialogmanager.Show <FindTaggedPages, FindTaggedPagesModel>(() => new FindTaggedPagesModel(_onProxy)); }
/// <summary> /// Action to open a tag editor dialog. /// </summary> /// <remarks>Opens the page tag editor</remarks> /// <param name="ribbon">OneNote ribbon bar</param> public void editTags(IRibbonControl ribbon) { TraceLogger.Log(TraceCategory.Info(), "Show tag editor"); _dialogmanager.Show <TagEditor, TagEditorModel>(() => new TagEditorModel(_onProxy)); }
/// <summary> /// Get the ribbon definition of this add-in /// </summary> /// <param name="RibbonID">identifier of the ribbon</param> /// <returns>ribbon definition XML as string</returns> public string GetCustomUI(string RibbonID) { TraceLogger.Log(TraceCategory.Info(), "UI configuration requested: {0}", RibbonID); return(Properties.Resources.ribbon); }