News:

MASM32 SDK Description, downloads and other helpful links
MASM32.com New Forum Link
masmforum WebSite

Using HTMLHelp2 with DExplore

Started by donkey, January 30, 2009, 02:16:37 AM

Previous topic - Next topic

donkey

Hi ToutEnMasm,

I had a chance to read the page you posted, an interesting approach to getting the keyword list. Here is the skeleton of an implementation in GoAsm, I didn't bother with the subtopics. There are 584,230 main topics found, no idea how many subtopics but some had 48 subtopics. For this reason I think there must be a faster way to get his information since this would on the surface be slow.

In the following code I just use a debug function to display the topic title...

BuildKeywordList FRAME hwnd, pszCollection
uses ebx
LOCAL wszCollection[1024]:W
LOCAL szTitle[1024]:B
LOCAL pThisSession:D
LOCAL pbstrCollection:D
LOCAL pbstrNavMoniker:D
LOCAL pIHxTopicList:D
LOCAL pIHxIndex:D
LOCAL count:D
LOCAL pbszLink:D

// Create an instance of IHxSession
invoke CoCreateInstance,offset CLSID_IHxSession, NULL, CLSCTX_ALL,offset IID_IHxSession, offset pThisSession
test eax, eax
jnz >>.ERROR_NO_RELEASE

// Convert the Collection name to unicode then create a BSTR with it
invoke MultiByteToWideChar,CP_ACP,NULL,[pszCollection],-1,offset wszCollection,1024
invoke SysAllocString,offset wszCollection
mov [pbstrCollection],eax

// Set the collection
CoInvoke(pThisSession,IHxSession.Initialize,[pbstrCollection],NULL)
test eax, eax
jnz >>.ERROR_MUST_RELEASE

// Create a BSTR with the navigation moniker
invoke SysAllocString,L"!DefaultKeywordIndex"
mov [pbstrNavMoniker],eax

// Allocate an uninitialed string to return the URL
invoke SysAllocStringLen ,NULL,1024
mov [pbszLink],eax

CoInvoke(pThisSession,IHxSession.GetNavigationObject,[pbstrNavMoniker],[pbstrNULL],offset pIHxIndex)
test eax, eax
jnz >>.ERROR_MUST_RELEASE_ALL

CoInvoke(pIHxIndex,IHxIndex.Count,offset count)
test eax, eax
jnz >>.ERROR_MUST_RELEASE_ALL

xor ebx,ebx
inc ebx
:
CoInvoke(pIHxIndex,IHxIndex.GetStringFromSlot,ebx,[pbszLink])
mov eax,[pbszLink]
mov eax,[eax]
invoke WideCharToMultiByte,CP_ACP,NULL,eax,-1,offset szTitle,1024,"",NULL
PrintString(szTitle)
inc ebx
cmp ebx,[count]
jl <<

invoke SysFreeString,[pbszLink]
invoke SysFreeString,[pbstrCollection]
invoke SysFreeString,[pbstrNavMoniker]


CoInvoke(pThisSession,IHxSession.IUnknown.Release)

RET

.ERROR_MUST_RELEASE_ALL
invoke SysFreeString,[pbszLink]
invoke SysFreeString,[pbstrNavMoniker]

.ERROR_MUST_RELEASE
invoke SysFreeString,[pbstrCollection]
// Release the IHxSession interface
push eax
CoInvoke(pThisSession,IHxSession.IUnknown.Release)
pop eax

.ERROR_NO_RELEASE

RET
ENDF
"Ahhh, what an awful dream. Ones and zeroes everywhere...[shudder] and I thought I saw a two." -- Bender
"It was just a dream, Bender. There's no such thing as two". -- Fry
-- Futurama

Donkey's Stable

ToutEnMasm

Thanks,
I have found this searching in the CLSID register.
Quote
IID_IHxTreeView GUID <314111B9h,0A502h,11D2h,<0BBh,0CAh,00h,0C0h,4Fh,8Eh,0C2h,94h>>
invoke GetInterfaceFromProgId,SADR("HxVz.HxIndexCtrl.1"),addr IID_IHxTreeView
it is part of {314111BA-A502-11D2-BBCA-00C04F8EC294} microsoft help visual 1.0
This control can call The Ihx interface.
I try,without success to connect to it,perhaps did he need visual studio ?

donkey

Hi ToutEnMasm,

Yes, I looked at that interface but I don't think its necessary. There is an extremely fast way of reading the topics, essentially you do the same thing but use a virtual listbox or treeview. Since the topics are extracted by an index number getting the topics to display is rather simple. It requires shifting stuff around a little bit but if I get some time tonight I will write and post the code. Filters are fairly easy to design, the moniker is in the format:

'"DocSet"="PSDK" OR "Product"="kbwindows"'

For the most part I understand and can use most of the basic interface, at least enough to display the topics list, find the keyword's URL and display it. Here's an update of the example I posted with the shifting around needed for the virtual listbox. I haven't implemented the listbox yet but I do display the first 100 topics found in a normal listbox.

[attachment deleted by admin]
"Ahhh, what an awful dream. Ones and zeroes everywhere...[shudder] and I thought I saw a two." -- Bender
"It was just a dream, Bender. There's no such thing as two". -- Fry
-- Futurama

Donkey's Stable

akane

Donkey, IHxIndex.GetStringFromSlot requires a pointer to BSTR, not a pointer to string. This method will allocate and return a BSTR.
About the tabs in topics: I have experimented with my htmlhelp-hook tool, and found that every tab should be displayed as a line-break + indent:
1. set indent to 0.
2. while you find a tab in topic text, increment the indent.
3. set listbox item to space(indent * 4) + the text AFTER the last tab.

If you click a topic in dexplore, sometimes a additional topic list pops. You can implement this by calling (see in my project CWorkerTopics::LookupKeywordUrl)
IHxTopicList *topics;
session->Query(bstrKeyword, bstrItemMoniker, HxQuery_No_Option, bstrFilterMoniker, &topics);
int NumberOfTopics;
topics->get_Count(&NumberOfTopics);

// get the topics enumerator
IEnumHxTopic *topiclist;
topics->EnumTopics(0, 0, &topiclist);


If NumberOfTopics is one, you can display it now:
HRESULT hr;
topiclist->Next(1, &topic, &hr);

BSTR bstrUrl;
topic->get_URL(&bstrUrl);


If NumberOfTopics is 2 or more, use the Next method to fill additional popup listbox.

donkey

#34
Hi akane,

I did passs it a pointer to a BSTR, I created it with SysAllocStringLen earlier in the function. The additional topics list is the list of subtopics associated with an entry, they can be found by using GetTopicsFromSlot and IHxTopic::GetInfo or IHxTopic::Title. But as I said in an earlier post I am ignoring the subtopics for the demo. However, since IHxTopicList is in itself an enumeration, there is no need to further enumerate the topics, here's the same code but with subtopics displayed (no indentation)...

BuildKeywordList FRAME hwnd, iFirst, nItems
uses ebx, edi
LOCAL pIHxTopicList:D
LOCAL count:D
LOCAL EndCount:D
LOCAL TopicCount:D
LOCAL pbszLink:D
LOCAL pbszTopic:D
LOCAL pIHxTopic:D
LOCAL pIHxTopicsList:D

invoke SendMessage,[hwnd],LB_RESETCONTENT,0,0

// Allocate an uninitialed string to return the URL
invoke SysAllocStringLen ,NULL,1024
mov [pbszLink],eax

// Allocate an uninitialed string to return the URL
invoke SysAllocStringLen ,NULL,1024
mov [pbszLink],eax

mov eax,[iFirst]
add eax,[nItems]
mov [EndCount],eax

CoInvoke(pIHxIndex,IHxIndex.Count,offset count)

test eax, eax
jnz >>.ERROR

cmp D[count],0
je >>.ERROR

// Calculate the string space needed
// Average of 64 bytes per title
invoke SendMessage,[hwnd],LB_INITSTORAGE,[count],64

mov ebx,[iFirst]
.GETTOPICS
CoInvoke(pIHxIndex,IHxIndex.GetStringFromSlot,ebx,[pbszLink])
mov eax,[pbszLink]
mov eax,[eax]
invoke SendMessageW,[hwnd],LB_ADDSTRING,0,eax
CoInvoke(pIHxIndex,IHxIndex.GetTopicsFromSlot,ebx,offset pIHxTopicsList)
CoInvoke(pIHxTopicsList,IHxTopicList.Count,offset TopicCount)
cmp D[TopicCount],2
jl >>.SHOWTOPIC
mov edi,0
inc edi
.GETSUBTOPICS
CoInvoke(pIHxTopicsList,IHxTopicList.ItemAt,edi,offset pIHxTopic)
CoInvoke(pIHxTopic,IHxTopic.Title,HxTopicGetHTMTitle,HxTopicGetTitleFileName,offset pbszTopic)
invoke SendMessageW,[hwnd],LB_ADDSTRING,0,[pbszTopic]
inc edi
cmp edi,[TopicCount]
jl <<.GETSUBTOPICS
.SHOWTOPIC
inc ebx
cmp ebx,[EndCount]
jl <<.GETTOPICS

invoke SysFreeString,[pbszLink]

xor eax,eax
RET

.ERROR
invoke SysFreeString,[pbszLink]
xor eax,eax
dec eax
RET
ENDF
"Ahhh, what an awful dream. Ones and zeroes everywhere...[shudder] and I thought I saw a two." -- Bender
"It was just a dream, Bender. There's no such thing as two". -- Fry
-- Futurama

Donkey's Stable

ToutEnMasm

Successful connection to the help2 controls
Here is a full list of the help2 CLSID
Quote
CLSID_HxSession GUID <31411198h,0a502h,11d2h,<0bbh,0cah,00h,0c0h,4fh,8eh,0c2h,94h>>   ;HxSession Class
CLSID_HxComp GUID <314111b4h,0a502h,11d2h,<0bbh,0cah,00h,0c0h,4fh,8eh,0c2h,94h>> ;HxComp Class
CLSID_Help2_Contents_Control GUID <314111b8h,0a502h,11d2h,<0bbh,0cah,00h,0c0h,4fh,8eh,0c2h,94h>>   ;Microsoft Help 2.0 Contents Control
CLSID_Help2_Index_Control GUID <314111c6h,0a502h,11d2h,<0bbh,0cah,00h,0c0h,4fh,8eh,0c2h,94h>>   ;Microsoft Help 2.0 Index Control
CLSID_HxProtocol1 GUID <314111c7h,0a502h,11d2h,<0bbh,0cah,00h,0c0h,4fh,8eh,0c2h,94h>>   ;HxProtocol Class
CLSID_HxProtocol2 GUID <314111c9h,0a502h,11d2h,<0bbh,0cah,00h,0c0h,4fh,8eh,0c2h,94h>>   ;HxProtocol Class
CLSID_HxRegistryWalker GUID <314111f0h,0a502h,11d2h,<0bbh,0cah,00h,0c0h,4fh,8eh,0c2h,94h>>   ;HxRegistryWalker Class
CLSID_HxParseDisplayName GUID <314111f7h,0a502h,11d2h,<0bbh,0cah,00h,0c0h,4fh,8eh,0c2h,94h>>   ;HxParseDisplayName Class
CLSID_HxRegisterSession GUID <31411219h,0a502h,11d2h,<0bbh,0cah,00h,0c0h,4fh,8eh,0c2h,94h>>   ;HxRegisterSession Class
CLSID_HxRegisterProtocol1 GUID <31411228h,0a502h,11d2h,<0bbh,0cah,00h,0c0h,4fh,8eh,0c2h,94h>>   ;HxRegisterProtocol Class
CLSID_HxRegisterProtocol2 GUID <31430c59h,0bed1h,11D1h,<08Dh,0e8h,00h,0C0h,04Fh,0C2h,0E0h,0C7h>>   ;HxRegisterProtocol Class

II_D for Help2 controls  (Microsoft Help Visuals 1.0 typelib)
Quote
IID_IHxTreeView GUID <314111B9h,0A502h,11D2h,<0BBh,0CAh,00h,0C0h,4Fh,8Eh,0C2h,94h>>
IID_IHxIndexView GUID <314111C4h,0A502h,11D2h,<0BBh,0CAh,00h,0C0h,4Fh,8Eh,0C2h,94h>>

Connection with cocreateinstance,there is also a progid but connection couldn't be made with it.
This following methods works
Quote
      invoke CoCreateInstance,addr CLSID_Help2_Contents_Control,NULL,
         CLSCTX_INPROC_HANDLER OR CLSCTX_INPROC_SERVER OR CLSCTX_LOCAL_SERVER,
         addr IID_IHxTreeView,addr ppvIdispatch
      .if eax == S_OK
         invoke MessageBox,NULL,SADR("Youpi"),SADR("Contents_Control"),MB_YESNO               
         Idispatch Release
         mov ppvIdispatch,0
      .endif
      invoke CoCreateInstance,addr CLSID_Help2_Index_Control,NULL,
         CLSCTX_INPROC_HANDLER OR CLSCTX_INPROC_SERVER OR CLSCTX_LOCAL_SERVER,
         addr IID_IHxIndexView,addr ppvIdispatch
      .if eax == S_OK
         invoke MessageBox,NULL,SADR("Youpi"),SADR("Index_Control"),MB_YESNO               
         Idispatch Release
         mov ppvIdispatch,0
      .endif      

Idispatch is used here only for test

ToutEnMasm


To be complete , here is some more information and translated typelib files for masm.

The IHxSession GetNavigationObject function is a poxerful method to connect many interface.
For example:
Quote
   IHxSession Initialize,BSTR("ms-help://ms.lhsmssdk.1033/sdkintro"),NULL
   IHxSession GetNavigationObject,BSTR("!DefaultToc"),\
            pbstrFilter,addr ppvIdispatch      
connect to the IHxHierarchy interface , How knowing that,answer is in the mssdk.hxc file

Quote
   <!-- Optional, Multiple -->
   <ItemMoniker Name="!DefaultToc" ProgId="HxDs.HxHierarchy" InitData="AnyString"/>
   <ItemMoniker Name="!DefaultFullTextSearch" ProgId="HxDs.HxFullTextSearch" InitData="AnyString"/>
   <ItemMoniker Name="!DefaultAssociativeIndex" ProgId="HxDs.HxIndex" InitData="A"/>   
   <ItemMoniker Name="!DefaultKeywordIndex" ProgId="HxDs.HxIndex" InitData="K"/>
   <ItemMoniker Name="!DefaultContextWindowIndex" ProgId="HxDs.HxIndex" InitData="F"/>
   <ItemMoniker Name="!DefaultDynamicLinkIndex"   ProgId="HxDs.HxIndex" InitData="B" />
   <ItemMoniker Name="!DefaultNamedUrlIndex" ProgId="HxDs.HxIndex" InitData="MSSDK_NamedUrl"/>
   <ItemMoniker Name="!SampleInfo" ProgId="HxDS.HxSampleCollection" InitData="AnyString"/>
There is just to change the chain beginning by a "!" in the GetNavigationObject function and you got the interface.


[attachment deleted by admin]

ToutEnMasm

After all this research,here is a sample.
He search Help index of the SDK (6.1),take the first he found,get the url of this index and send it to the internet explorer itself.
There is no use of dll or windows to make all that.He used the IWebBrowser2 interface.
Notes:
Take care with BSTR = pointer and BSTR* pURL = pointer on pointer
Some macro can interpret "!" as an operator see SADR macro to know how avoid this
All is written for masm,and the executable will work immediately if you have the sdk.If not modify the namespace.
Quote
   ;NameSpace = "ms-help://ms.lhsmssdk.1033/sdkintro"
   IHxSession Initialize,BSTR("ms-help://ms.lhsmssdk.1033/sdkintro"),NULL




[attachment deleted by admin]

ToutEnMasm

Hello,
There is one thing to know for initiate and uninitiate Help2 and dexplore.exe.
They are independant.
To close all ,two things must be made.
Quote
            Help2  Close         
            Help2 Release
If "Help2  Close" isn't call,dexplore stay invisible in memory,and must be closed with the task manager.
The help2 interface create a new Instance of dexplore when we call the first function using it.
To avoid mutiples Instances of dexplore when there is multiple callers,the interface pointer can be pass creating a new message with RegisterWindowMessage.
I am testing this.




ToutEnMasm

The good maner to get the default collection with Help2
Quote
Help2 Collection,addr BSTRanswer
This give the last collection used.
A little sample  is join to play with.
You have to find the good order to make it work.
In case of multiple callers,a dll is needed,to avoid multiple dexplore in memory.
Some errors (manual translate) must be corrected in the source.  :green2
Just replace the old text by this one
Quote
   STHelp2   STRUCT
      QueryInterface                  comethod3 ?
      AddRef                          comethod1 ?
      Release                         comethod1 ?
      GetTypeInfoCount                comethod2 ?
      GetTypeInfo                     comethod4 ?
      GetIDsOfNames                   comethod6 ?
      invoke1                         comethod9 ?   
      ;---- end Idispatch ----------------------      
      Contents                        comethod1 ?
      Index                           comethod1 ?
      Search                          comethod1 ?
      IndexResults                    comethod1 ?
      SearchResults                   comethod1 ?
      DisplayTopicFromId              comethod3 ?
      DisplayTopicFromURL             comethod2 ?
      DisplayTopicFromURLEx           comethod3 ?
      DisplayTopicFromKeyword         comethod2 ?
      DisplayTopicFromF1Keyword       comethod2 ?
      DisplayTopicFrom_OLD_Help       comethod3 ?
      SyncContents                    comethod2 ?
      CanSyncContents                 comethod2 ?
      GetNextTopic                    comethod3 ?
      GetPrevTopic                    comethod3 ?
      FilterUI                        comethod1 ?
      CanShowFilterUI                 comethod1 ?
      Close                           comethod1 ?
      SyncIndex                       comethod3 ?
      SetCollection                   comethod3 ?
      Collection                      comethod2 ?
      Filterout                       comethod2 ?
      Filterin                        comethod2 ?
      FilterQuery                     comethod2 ?
      HelpOwnerout                    comethod2 ?
      HelpOwnerin                     comethod2 ?
      HxSession                       comethod2 ?         ;IDispatch** ppObj
      Help                            comethod2 ?         ;IDispatch** ppObj
      GetObject1                      comethod4 ?
      ;-------- 9.0----------------------------
      SearchEx                        comethod4 ?
      HowDoI                          comethod1 ?
      Favorites                       comethod1 ?
      AskAQuestion                    comethod2 ?
      DisplayTopicFromURLEx2          comethod4 ?
      InitializeSettingsToken         comethod2 ?      
   STHelp2   ENDS




[attachment deleted by admin]