Description:
Requires:
Code Language:
IntroductionThis example demonstrates how to build a standard help viewer window that reads HV2 catalogs, using the Help Viewer 2.0 API (Microsoft.VisualStudio.Help.Runtime.dll COM interface). We won't be talking about the chrome, just the main help API calls at the heart of the viewer. SetupOpen the solution HV2viewer.sln (alternatively open HV2Everything.sln and make the "HV2Viewer" project the start up project) ![]() The main executable project "HV2Viewer" uses 2 other DLL assemblies "Helpware.Misc" (our non-help related support code) & "HV2Lib" (our HV2 related support code for rendering etc). Both HV2Viewer and HV2Lib require references to the following 2 x Help Viewer 2.0 DLLs. Actually only HV2Lib uses the second DLL solely for the rendering HTML. (in VS 11 we right-click each project's "References" folder and select "Add Reference" and added these DLLs) C:\Program Files\Microsoft Help Viewer\v2.0\Microsoft.VisualStudio.Help.Runtime.dll C:\Program Files\Microsoft Help Viewer\v2.0\Microsoft.VisualStudio.Help.dll
Windows 8 RuntimeTo use the Windows 8 Help run-time instead of VS 11 Help run-time:
Note that Windows 8 does not provide an equivalent to Microsoft.VisualStudio.Help.dll for rendering support. In this case you will need to comment out the one VS Help render call in our code examples (which will be obvious because it will cause a compile time error).HV2 NamespacesTo open and read catalogs you will need this namespace: using Microsoft.VisualStudio.Help.Runtime;
For the one place where we reference VS 11 renderering method you need: using Microsoft.VisualStudio.Help; If you are coding using the Windows 8 help runtime, there is no equivalent rendering DLL. However, we provide code in the HV2Lib assembly to renderer topics. More on this later. Opening CatalogsThe API can open managed & unmanaged catalogs as well as .mshx file catalogs. There are 2 main classes we need to access (Catalog & CatalogRead). Most of the other class/enums are used by the methods in these classes. First thing to do instantiate these 2 important classes. private Catalog _catalog = new Catalog(); private CatalogRead _catalogRead = new CatalogRead(); To open a catalog we pass the catalog directory path and locale to the Open() method. The locale parameter is a prioritized list of locales, but we normally want to pass in one specific locale. String catalogPath = @"C:\ProgramData\Microsoft\HelpLibrary2\Catalogs\VisualStudio11"; String locale = "en-US"; _catalog.Open(catalogPath, new String[] { locale }); To open a .mshx file we use the OpenMshx() method. String catalogPath = "c:\somePath\helpfile.mshx"; _catalog.OpenMshx(catalogPath); if (_catalog.IsOpen) _catalog.Close(); Use the Open Catalog command in the Toolbar to access these commands. Below the menu are a list of VS registered catalogs. A hover tip displays the registry info for the catalog registration. See Catalogs to find out about catalog registration. Return ValueAt the time of writing most method don't return errors. If there is a problem (file not found, or invalid catalog) you will catch an exception. Table Of ContentsA HV2 table of contents can contain millions of items, so we build it incrementally as nodes are expanded.To get the the [+] gadget to appear on unexpanded nodes we add a dummy node under the unexpanded node. The TOC is hosted in a UserControl which we parent into the Content navigation tab. When you click a TOC node there is a call back to ping a handler in the main form code. You can explore this code at your leisure. Here are the main HV2 API calls for building a TOC. This gives us the root nodes of the TOC. ITopicCollection topics = _catalogRead.GetTableOfContents(_catalog, "-1", null, TocReturnDetail.TocRootNodes); This one gives us the immediate children of a particular topic. ITopicCollection topics = _catalogRead.GetTableOfContents(_catalog, topic.Id, null, TocReturnDetail.TocChildren); And that's all we really need to build a TOC. The GetTableOfContents() method returns a collections of Topic objects, according to the parameters passed in.
TocReturnDetail enum
Topic classITopicCollection is a collection of Topic objects. Here we are getting all Topics from the collection.ITopicCollection topics = _catalogRead.GetTableOfContents(_catalog, "-1", null, TocReturnDetail.TocRootNodes); { topics.MoveTo(i); Topic topic = (Topic) topics.Current; .... } There is also topics.MoveNext(); that can be used if you are iterating with a foreach(...) { } loop.The Topic class has very handy members. Here an example of the contents: topic.Category topic.ContentFilter Visual F# topic.ContentType Reference topic.Description Returns a string by concatenating... topic.DisplayVersion topic.Id 489CF6E9-E0A0-457A-9E9B-BF630A40A25B topic.Locale en-US topic.Package Visual_Studio_21800792_VS_100_en-us_1.mshc;\R402.htm topic.ParentId A5FDA9CD-D71F-4271-A6A4-AB4CAA0BE550 topic.TableOfContentsHasChildren False topic.TableOfContentsPosition 10 topic.Title String.replicate Function (F#) topic.TopicLocale EN-US topic.TopicVersion 100 topic.Url Visual_Studio_21800792_VS_100_en-us_1.mshc;\R402.htm topic.Vendor Microsoft Note especially...
IndexAgain this is very simple. The VS catalog can contain over a million keywords so we are using a ListView control in Virtual mode. We get a collection of Keywords by passing the _catalog object into GetKeywords() method. IKeywordCollection keywords = _catalogRead.GetKeywords(_catalog, true); The true parameter enables some caching to speed up loading. Keyword ClassWe can access each Keyword object by iterating through the collection. for (int i = 0; i < keywords.Count; i++) { keywords.MoveTo(i); keyword keyword = (Keyword)keywords.Current; .... } There is also keywords.MoveNext(); that can be used if you are iterating with a foreach(...) { } loop.The keyword object has these handy members:
Word WheelWe want to type in the edit box (above the keyword list) and select the closest match in the list (the keyword starting with that text). The MoveToKeyword() method returns the index of the List item we want to select and scroll into view int i = _helpKeywords.MoveToKeyword(text); //where text is the text typed SearchSearch is also straight forward. Here GetSearchResults() returns a list of topic hits. String query = "String Builder "; HelpFilter filter = null; // adv filtering not required (we want all results) int pageSize = 50; // number of items per page to return int pageNumber = 1; // which page of results to return int _totalAvailableHits = 0; // total number of hits returned ITopicCollection topics = _catalogRead.GetSearchResults(_catalog, query, filter, SearchOptions.None, pageSize, pageNumber, out _totalAvailableHits); And again we can iterate through the returned topic list (see details for ITopicCollection and Topic above).These Search options can be OR'd together if required.
Note: The API code demo demonstrates the options. More Search OptionsNote that special search prefixes (as it's not clear from the demo) can be concatenated and prefix any search term. So when searching for "String AND Lock" this is also valid: "code:c#:keyword:String AND title:Lock". In HV1 the protocol ms-xhelp:/// was associated with the Agent tray application, which would fully render the returned topic for the default browser or help viewer.HV2 and HV1 need to co-exist, so in HV2 we associated the protocol with just our application using a Pluggable Protocol. Once installed any URL starting with ms-xhelp:/// protocol directed at the embedded web browser, will cause an event to fire asking for the stream of the required HTML file or asset (image).The Pluggable Protocol code (similar to that used by VS 11 HlpViewer.exe) is included in HV2Lib.DLL. Here's how to use our example MsxhelpProtocol object.private MsxhelpProtocol msxhelpProtocol = new MsxhelpProtocol(); and whenever a help catalog is opened we tell the object so it knows where to read help topics from. MsxhelpProtocol.Catalog = _catalog; We tell the object which render to use: MsxhelpProtocol.RenderUsingVS = TRUE; If you use the VS render you will get better result for VS content but the VS render is only available if VS 11 is installed (as we talked about in the introduction). Alternatively set this FALSE (the default) and use the supplied rendering code. The advantage of using the supplied rendering code is
About RenderingTry flipping between the VS Renderer (checked) and the Custom Rendering (unchecked), using the options menu. If you were building an actual embedded viewer, you would typically choose one or the other.
The "View Unrendered Source" command shows the raw source straight from the .mshc file. Use the WebBrowser "Viewer Source" command to view the rendered source. The differences you see are what the rendering code adds. At a minimum the rendering code needs to expand all topic links (CSS, Images, JS etc) to use the ms-xhelp:/// protocol, that way our handler will be asked to serve up the stream of the asset from the current catalog. Brief Code Walk ThroughThe pluggable protocol handler (receives requests for catalog assets and indexed HTML files) is here... <HelpViewerProject\HV2Lib>\PluggableProtocol\HelpPluggableProtocolHandler.cs Once the Pluggable Protocol is installed (see new MsxhelpProtocol() above) we can do the following and our handler will server up the catalog topics and assets.String url = " ms-xhelp:///?method=page&id=xxx&locale=en-US&vendor=Microsoft&topicVersion=&topicLocale=EN-US " webBrowser1.Navigate(url); The URL above says load the catalog page with topic Id=xxx. If you can provide all parameters do so. Otherwise the following (with no-ti-breakers) will normally work just as well. String url = "ms-xhelp:///?method=page&id=xxx" webBrowser1.Navigate(url); Next the Handlers HelpPluggableProtocolHandler.cs receives a request to serve up the help topic from the catalog.The handler palms the rendering off to either the VS render or our custom rendering code. We examine the szURL (topic file, image file, CSS file, js file etc) and returns a stream from the current catalog. if (MsxhelpProtocol.RenderUsingVS) //Requires VS 11 DLL(Microsoft.VisualStudio.Help.DLL) { if (_vsRenderer == null) _vsRenderer = new TopicRenderer(); _stream = (Stream)_vsRenderer.ProcessLink(szURL, (ICatalog)MsxhelpProtocol.Catalog, null/*_renderParameters*/); } else // our custom code { if (_customRenderer == null) _customRenderer = CustomRenderer.Create(); _stream = _customRenderer.UrlToStream(szURL); _stream.Position = 0; } That's it! The handler first receives the topic URL (as sent to webBrowser1 above). We read the topic from the catalog and tweak all the links so they use the ms-xhelp:/// URLs and point to the correct location in the catalog file. If rendered correctly, the next messages that come though will be requests for the assets (JS, CSS, image files etc). |
Code Examples >