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

The PSDK and MSDN library are both examples of HTMLHelp2, though Microsoft has not really pushed the standard it is used in Visual Studio to display help files and I am writing an extension to the RadHelp plugin for RadASM that will allow it to use DExplore directly. The following is an example of a simple use of he COM interface for VSHelp80, the interface has very little documentation at MSDN, as a matter of fact for all of the information in the header file I was required to use OleView from the Microsoft toolkit and build the header that way. Both the IVSHelp and IVSHelp80 interfaces appear to work fine. The only problem I have is preventing the DExplore window from closing, I tried getting its pid/tid and using the wait functions but that was unsuccessful since the process and thread don't appear to close until the interface is released. I didn't feel like writing a message hook to watch for WM_CLOSE so I wrote a kludge for the example that checks to see if the window is visible, if not it releases the interface and shuts down.

VSHelp80.h (this will be included in the next header project release)
#IFNDEF VSHELP80_H
#define GUID_IID_IVSHelp <0x4A791148,0x19E4,0x11D3,0xB8,0x6B,0x00,0xC0,0x4F,0x79,0xF8,0x02>
#define GUID_IID_IVSHelp80 <0x78413D2D,0x0492,0x4A9B,0xAB,0x25,0x73,0x06,0x33,0x67,0x99,0x77>

#IFNDEF Unknown
Unknown STRUCT
   QueryInterface DD
   AddRef DD
   Release DD
ENDS
#ENDIF

#IFNDEF Dispatch
Dispatch STRUCT
GetTypeInfoCount DD
GetTypeInfo DD
GetIDsOfNames DD
Invoke DD
ENDS
#ENDIF

HELP STRUCT
Contents DD
Index DD
Search DD
IndexResults DD
SearchResults DD
DisplayTopicFromId DD
DisplayTopicFromURL DD
DisplayTopicFromURLEx DD
DisplayTopicFromKeyword DD
DisplayTopicFromF1Keyword DD
DisplayTopicFrom_OLD_Help DD
SyncContents DD
CanSyncContents DD
GetNextTopic DD
GetPrevTopic DD
FilterUI DD
CanShowFilterUI DD
Close DD
SyncIndex DD
SetCollection DD
GetCollection DD
GetFilter DD
SetFilter DD
FilterQuery DD
GetHelpOwner DD
SetHelpOwner DD
HxSession DD
Help DD
GetObject DD
ENDS

HELP2 STRUCT
SearchEx DD
HowDoI DD
Favorites DD
AskAQuestion DD
DisplayTopicFromURLEx2 DD
InitializeSettingsToken DD
ENDS

IVSHelp STRUCT
IUnknown Unknown <>
IDispatch Dispatch <>
Contents DD
Index DD
Search DD
IndexResults DD
SearchResults DD
DisplayTopicFromId DD
DisplayTopicFromURL DD
DisplayTopicFromURLEx DD
DisplayTopicFromKeyword DD
DisplayTopicFromF1Keyword DD
DisplayTopicFrom_OLD_Help DD
SyncContents DD
CanSyncContents DD
GetNextTopic DD
GetPrevTopic DD
FilterUI DD
CanShowFilterUI DD
Close DD
SyncIndex DD
SetCollection DD
GetCollection DD
GetFilter DD
SetFilter DD
FilterQuery DD
GetHelpOwner DD
SetHelpOwner DD
HxSession DD
Help DD
GetObject DD
ENDS

IVSHelp80 STRUCT
IUnknown Unknown <>
IDispatch Dispatch <>
Help HELP <>
Help2 HELP2 <>
ENDS

#define vsAskQuestionFlagsAskNew 0x00000001
#define vsAskQuestionFlagsCheckStatus 0x00000002
#define vsAskQuestionFlagsSendFeedback 0x00000004

#define vsSearchFlagsNone 0x00000000
#define vsSearchFlagsExecuteSearch 0x00000001
#define vsSearchFlagsAddToExistingQueryString 0x00000002
#define vsSearchFlagsFilterTransformSpecified 0x00000004

#define vsHelpDisplayUrlFlagsNone 0
#define vsHelpDisplayUrlFlagsHighlightTerm 1
#define vsHelpDisplayUrlFlagsOpenNewWindow 2
#define vsHelpDisplayUrlFlagsGuidLocal 16
#define vsHelpDisplayUrlFlagsGuidOnline 32
#define vsHelpDisplayUrlFlagsGuidLocale 64
#define vsHelpDisplayUrlFlagsGuidFailover 128
#define vsHelpDisplayUrlFlagsNamedUrl 256
#define vsHelpDisplayUrlFlagsNoHistory 512
#define vsHelpDisplayUrlFlagsNoHistoryThisPage 1024

#define HUFTID_Default 0
#define HUFTID_Local 1
#define HUFTID_Online 2
#define HUFTID_Locale 4

#define vsHelpSearchFilterTransformsHelp L"Help"
#define vsHelpSearchFilterTransformsControls L"Controls"
#define vsHelpSearchFilterTransformsSamples L"Samples"
#define vsHelpSearchFilterTransformsSnippets L"Snippets"
#define vsHelpSearchFilterTransformsStarterKits L"StarterKits"
#define vsHelpSearchFilterTransformsAddins L"Addins"
#define vsHelpSearchFilterTransformsUnfiltered L"Unfiltered"

#define DExploreWndClass "wndclass_desked_gsk",0

#ENDIF


The example for use with the latest MSDN library
#DEFINE IDISPATCH_DEFINED
#DEFINE IUNKNOWN_DEFINED

#DEFINE LINKFILES

#DEFINE NOMCX
#DEFINE NOIME
#DEFINE NOTV
#DEFINE NONETWORK
#DEFINE NOSERVICE
#DEFINE NOCON
#DEFINE NOCARD
#DEFINE NORADASM
#DEFINE NOZLIB
#DEFINE NODONKEY

#include "Windows.h"
#include "VSHelp80.h"
#include "macros.a" // For the CoInvoke macro

DATA SECTION
hInstance DD ?
IID_IVSHelp80 GUID GUID_IID_IVSHelp80
Collection DB "ms-help://MS.MSDNQTR.v90.en",0
Keyword DB "CoInitialize",0
wszNULL DB L"",0
pDExplore DD ?
hDExplore DD ?
hHook DD ?

CODE SECTION

Start:
Invoke CoInitialize,NULL
invoke GetInterface,"DExplore.AppObj"
test eax,eax
jz >>
mov [pDExplore],eax

invoke SetCollection,[pDExplore],offset Collection

invoke ShowTopic,[pDExplore],offset Keyword

invoke WaitForClose

invoke ReleaseInterface

:
invoke CoUninitialize
invoke ExitProcess,0

WaitForClose FRAME
LOCAL hwnd:D
LOCAL pid:D
LOCAL tid:D
LOCAL hThread:D

invoke FindWindow, DExploreWndClass, NULL
mov [hwnd],eax

:
invoke Sleep,125
invoke IsWindowVisible,[hwnd]
test eax,eax
jnz <

RET
ENDF

GetInterface FRAME ProgId
LOCAL wszProgID[1024]:W
LOCAL pInterface:D
LOCAL clsid :GUID

invoke MultiByteToWideChar,CP_ACP,NULL,[ProgId],-1,offset wszProgID,1024
// Get our Class ID
invoke CLSIDFromProgID,offset wszProgID, offset clsid
// Create an instance
invoke CoCreateInstance,offset clsid, NULL, CLSCTX_ALL,offset IID_IVSHelp80, offset pInterface
test eax,eax
jnz >>.NOINSTANCE
mov eax,[pInterface]
RET

.NOINSTANCE
mov eax,0
RET
ENDF

ReleaseInterface FRAME pInterface

CoInvoke(pInterface,IVSHelp80.IUnknown.Release)
RET

ENDF

SetCollection FRAME pInterface,pCollection
LOCAL wszCollection[1024]:W
LOCAL pbstrCollection:D
LOCAL pbstrNULL:D

invoke MultiByteToWideChar,CP_ACP,NULL,[pCollection],-1,offset wszCollection,1024

invoke SysAllocString,offset wszCollection
mov [pbstrCollection],eax

invoke SysAllocString,offset wszNULL
mov [pbstrNULL],eax

//SetCollection
CoInvoke(pInterface,IVSHelp80.Help.SetCollection,[pbstrCollection],[pbstrNULL])
invoke SysFreeString,[pbstrCollection]
invoke SysFreeString,[pbstrNULL]

RET
ENDF

SyncTopic FRAME pInterface, szTopic
LOCAL wszTopic[1024]:W
LOCAL pbstrTopic:D

invoke MultiByteToWideChar,CP_ACP,NULL,[szTopic],-1,offset wszTopic,1024
invoke SysAllocString,offset wszTopic
mov [pbstrTopic],eax

CoInvoke(pInterface,IVSHelp80.Help.SyncIndex,[pbstrTopic],1)
CoInvoke(pInterface,IVSHelp80.Help.Index)

invoke SysFreeString,[pbstrTopic]
RET
ENDF

ShowTopic FRAME pInterface, szTopic
LOCAL wszTopic[1024]:W
LOCAL pbstrTopic:D

invoke MultiByteToWideChar,CP_ACP,NULL,[szTopic],-1,offset wszTopic,1024
invoke SysAllocString,offset wszTopic
mov [pbstrTopic],eax

CoInvoke(pInterface,IVSHelp80.Help.DisplayTopicFromKeyword,[pbstrTopic])
CoInvoke(pInterface,IVSHelp80.Help.SyncIndex,[pbstrTopic],TRUE)

invoke SysFreeString,[pbstrTopic]
mov eax,[pInterface]
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

Hello,
I am testing this (only with masm) and have differents results.
If dexplore is running,results are OK.
If dexplore isn't running,he is launched but there is no topic displayed.Dexplore isn't in his defaut configuration and windows are missing (summary,index ...)
Seems some steps are missing to launch it correctly ?.
The question is where find this steps,any idea ?.

donkey

Hi ToutEnMasm,

Could be a problem in the translation or perhaps something specific about your installation, it works well here. Here is the executable for the project.



[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

ToutEnMasm


I am using  this clsid and progid:
sIID_Help2   TEXTEQU   <{078413D2Dh,00492h,04A9Bh,{0ABh,025h,073h,006h,033h,067h,099h,077h}}>
"DExplore.AppObj.9.0"
Whis that i need to use Contents after SetCollection to make it work.
I have found the sIID with oleview.
But perhaps i make the labor more hard using this.
I will post my source code after a little experiments.



gfalen

Did you know that Dexplore has a command line interface?
Run "dexplore /?" (you might need a full path)

For context help in my editor I just use ShellExecute with "dexplore.exe" as the lpFile and
"/LaunchFKeywordTopic " + current word as the lpParameters

ToutEnMasm


Hello,
I know that if in the C:\Program Files\Fichiers communs\Microsoft Shared\Help 9 path,I put a batch like that:
Quote
@echo off
dexplore /helpcol ms-help://ms.lhsmssdk.1033 /LaunchFKeywordTopic SysAllocString
This is working perfectly.
Here is a good exercice on how using dexplore with a com interface and a progid,and without.






ToutEnMasm


Here is my best result in masm.
It's work but need Enter to search the keyword.


[attachment deleted by admin]

donkey

Quote from: gfalen on January 30, 2009, 10:54:52 AM
Did you know that Dexplore has a command line interface?
Run "dexplore /?" (you might need a full path)

For context help in my editor I just use ShellExecute with "dexplore.exe" as the lpFile and
"/LaunchFKeywordTopic " + current word as the lpParameters

Hi gfalen,

Now that would be cheating ;) It is much more fun to figure out the COM interface. But seriously, the issue for me is that I am trying to figure out how to send partial keywords and have the first instance displayed, not sure if it's possible, for example passing "ExitProcess" as the keyword will not have it automatically displayed since the keyword is listed as "ExitProcess function". This is the main reason I am exploring the COM interface, also /LaunchFKeywordTopic on my system defaults to DisplayTopicFromF1Keyword which launches an internet search for the topic, this is the default behavior while DisplayTopicFromKeyword will use local help.
"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


Found,
At end , i have just change the searched keyword "CoInitialize" instead of "SysAllocString" and there is no need of the enter key to finish.
All is good with my source code.

donkey

Hi ToutEnMasm,

This is simply the behaviour I described, if you change "SysAllocString" to "SysAllocString [AUTOMATION]" it should work as well, the topic is only displayed if there is an exact complete text match to the entry in the index and it is the only match otherwise it simply displays the first partial match in the index list and does not open the document. It demonstrates well one of the reasons I am trying the COM interface as opposed to the command line.
"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

gfalen

I only have the PSDK installed (latest one 6.1) no VCExpress or VSudio and it works every time including ExitProcess & SysAllocString.

Maybe installing one of the compilers changes something.

ToutEnMasm


To gfalen:
Problem is only with the contents of the index  (search by help2 interface).
If we search "SysAllocString",the index of the help file is written as this:
"SysAllocString [AUTOMATION]"
If we send "SysAllocString" a confirmation by the enter key is needed
if we send  "SysAllocString [AUTOMATION]" ,no confirmation is needed

Making a search in the index of the help file seems  not possible.
Used of the batch haven't this problem.

I have also the PSDK 6.1 installed and VC++ 2008 express edition.
Verify that the index is not matching exactly what you search,and if there is no need of confirmation
it works not in the same manners than for me.





ToutEnMasm


I have made a change in the code
Quote
invoke CoCreateInstance,addr clsid, NULL,CLSCTX_LOCAL_SERVER,pIID,addr ppv
With CLSCTX_ALL , dexplore.exe can stay in memory (without be visible) after the interface is released.

donkey

Quote from: ToutEnMasm on January 31, 2009, 06:34:05 AM

I have made a change in the code
Quote
invoke CoCreateInstance,addr clsid, NULL,CLSCTX_LOCAL_SERVER,pIID,addr ppv
With CLSCTX_ALL , dexplore.exe can stay in memory (without be visible) after the interface is released.


I have noticed this as well but CLSCTX_LOCAL_SERVER does not seem to solve the problem reliably, since CLSTX_ALL is simply a combination of CLSCTX_INPROC_SERVER|CLSCTX_INPROC_HANDLER and CLSCTX_LOCAL_SERVER it resolves to the same thing once CoCreateInstance discards the INPROC contexts (this type of interface cannot be instanced INPROC). The problem appears to be related (bizarrely enough) to the configuration of help searches, if you do not allow for online searches it does not terminate on release, if you allow them it will terminate. The behaviour does not seem to affect DExplore except when called via the COM interface which suggests that there is something rather large that I am missing here, I continue to scratch my head over this one....

For the problem with it not opening the help for partial matches I cheated and added a keybd_event call, however it forces us to assume that the DExplore window has the keyboard focus, I can't imagine a possibility where it wouldn't have it after a call to DisplayTopicFromKeyword but I can't definitively say a condition that it will not does not exist.

ShowTopic FRAME pInterface, szTopic
LOCAL wszTopic[1024]:W
LOCAL pbstrTopic:D

invoke MultiByteToWideChar,CP_ACP,NULL,[szTopic],-1,offset wszTopic,1024
invoke SysAllocString,offset wszTopic
mov [pbstrTopic],eax

CoInvoke(pInterface,IVSHelp80.Help.DisplayTopicFromKeyword,[pbstrTopic])

// This synchonizes the index and forces the index window to show
invoke SyncTopic,[pInterface],[szTopic]

// Show the first topic found, this is a hack but it works for now
invoke keybd_event,VK_RETURN,NULL,NULL,NULL

CoInvoke(pInterface,IVSHelp80.Help.SyncContents,[pbstrTopic])

invoke SysFreeString,[pbstrTopic]
mov eax,[pInterface]
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

akane

I have recently created a webbrowser application with integrated PSDK help. It is able to enumerate installed namesaces, filters and topics, and ofcource extend a scintilla based IDE with F1 help system. It does not use dexplore, just the help collections.
If you have a topic with additional characters, you can query it for "ApiName" topic property, and it will return CreateProcess, CreateProcessA and CreateProcessW (click View/topic properties).
If your familiar with c++ like code, this topic may be helpful: http://www.ionicwind.com/forums/index.php/topic,2462.0.html.
I have used Microsoft Help Data Services 1.0 Type Library to create this tool.

EDIT: search in sources for CWorkerTopics::EnumNamespaces, CWorkerTopics::EnumFilters, CWorkerTopics::EnumTopics, GetTopicAttribute.