HOOK OF COM (1)
很少看见这样的话题,前不久在www.codeguru.com 看到了这篇文章,觉得不错,
最近写COM程序,看到这个,翻译了以下,有错在所难免,欢迎指教
、、、、、、、、、、、
我还将翻译作者的其他2篇文章 HOOK OF COM (2) 过些日 翻译 完毕
、、、、、、、、、、、
/*
翻译文档来自www.codeguru.com
    原作者: Chunhua Liu(好像是在美国工作的中国人,程序写的不错)


程序修改
,翻译: 赵宗宝

本人

时间:2005-12-10 19:30 备注: 我一直希望HOOK OF COM 这样的文档有中文文档(参考的资料实在太少了)
       第一次翻译难免有错误的地方,请您发E_Mail我一定及时改正 提供源代码,以及编译过的文件,只要我有时间我就恢复你
声明: 版权归原作者所有.
测试环境:WindowsXP-SP2(English), Visual C++ 6.0(Englsh), MSDN 2003 FEB(CHINA)
还有一个BUG: 有时HOOK会有问题
*/自2001.Oct后, 我们在vc6里就不能按F1键使用最新的MSDN.微软已经改变了从CHM到
文挡浏览器的格式。
在vc6里可以使用最新的MSDN吗?
问题的答案当然是“YES”!详述: 首先,我们必须知道在vc6中按下F1是如何引发帮助的。如果你有像Soft-ICE这样的调试器
这一起显的很简单。 如果你没有安装MSDN.当你按下F1后,vc6将弹出一个消息对话框内容是你没有安装MSDN.
 在MessageBox设置断点。按F1并且调试对话框。看看栈。我们将看到这个函数引发自
 "C:\Program Files\Common Files\Microsoft Shared\VS98\vshelp.dll" Then we use Dependency Walker ("depends" in Visual Studio Tools) to see 
 what functions are exported. We will see DllRegisterServer  
 and DllUnregisterServer. It's obviously a COM.用vc6创建一个a simple console project. 增加下面这行语句在.CPP文件中
#import "c:\Program Files\Common Files\Microsoft Shared\VS98\vshelp.dll"编译工程。在Debug目录里,你会发现"vshelp.tlh"和"vshelp.thi".打开"vshelp.tlh",我们看见
struct __declspec(uuid("854d7ac0-bc3d-11d0-b421-00a0c90f9dc4"))
IVsHelpSystem : IUnknown
{
    //
    // Wrapper methods for error-handling
    //    HRESULT KeywordSearch ( // 这里这里
        LPWSTR pszKeyword,
        long dwFlags,
        long dwReserved );
    HRESULT ALinkSearch (
        LPWSTR pszALink,
        long dwFlags,
        long dwReserved );
......
}
 当我们按F1后KeywordSearch函数被调用.因此我们替换这个函数并把她调用最新的MSDN帮助函数,那么帮助将会
 起作用. 怎么去实施呢?因为是一个COM 接口,因此很容易Hook It 基础的COM接口就是一个C++的虚函数表。她有一个指向所有虚函数表地址的头指针.并且这个表共享了所有的实例所以我们仅仅需要改变虚函数在表里的地址。这个接口看起来是这样。
struct _IVsHelpSystemVtbl
{
HRESULT (STDMETHODCALLTYPE *QueryInterface)(IUnknown *This, REFIID riid, void **ppvObject);
ULONG (STDMETHODCALLTYPE *AddRef)(IUnknown *This);
ULONG (STDMETHODCALLTYPE *Release)(IUnknown *This);
HRESULT (STDMETHODCALLTYPE *KeywordSearch)(IUnknown *This,LPWSTR pszKeyword,long dwFlag, long dwReserved);
};struct _IVsHelpSystem
{
struct _IVsHelpSystemVtb1 *lpVtbl;
};我们需要的是仅仅创建这个COM实例.并且我们将得到这个表的地址。
现在我们需要知道最新的MSDN是如何引发的.It seems it's not documented. It's also a COM在CPP文件中加入下面的这句.你将得到COM的声明
#import "C:\Program Files\Common Files\Microsoft Shared\MSEnv\vshelp.tlb"在"vshelp.tlh"中,你将发现这样的函数:
 HRESULT DisplayTopicFromF1Keyword( _bstr_t pszKeyword ); 很明显,这是我们需要的。所以我们在KeywordSearch函数里,调用DisplayTopicFromF1Keyword就会
 引用最新的MSDN。

解决方案 »

  1.   


     在这里有Hook KeywordSearch函数的代码:
     
     HRESULT hr = theHelp.CreateInstance(__uuidof(VsHelp::DExploreAppObj));
    if (SUCCEEDED(hr))
    {
        HRESULT hr = vc6Help.CreateInstance(
          __uuidof(VsHelpServices::VsHelpServices));
        if (SUCCEEDED(hr))
        {
            iHelp = (_IVsHelpSystem *)vc6Help.GetInterfacePtr();        TRACE1("iHelp = %x\n", iHelp);
            TRACE1("lpVtbl = %x\n", iHelp->lpVtbl);
            TRACE1("KeywordSearch = %x\n", iHelp->lpVtbl->KeywordSearch);        OldKeywordSearch = iHelp->lpVtbl->KeywordSearch;        DWORD dwOldProtect;
            if (VirtualProtect(iHelp->lpVtbl, sizeof(
              _IVsHelpSystemVtbl), PAGE_READWRITE, &dwOldProtect))
                iHelp->lpVtbl->KeywordSearch = MyKeywordSearch;
        }
    }这就是 MyKeywordSearch的代码. 在这个函数里,这个地方我没有合适的词语,原文附上,
    /*
    Here is the code of MyKeywordSearch. In this function, 
    we will use the help collection specified by Dependency Walker 
    as the default help collection. If default help collection is specified, 
    we will check if it's 'MSDN Online'. If yes, we will call ShellExecute 
    to start a browser to show the help. Otherwise,
    we will find the help collection's filename from the registry.
    */
    HRESULT __stdcall MyKeywordSearch(IUnknown * This, LPWSTR pszKeyword, long dwFlags, long dwReserved)
    {
        ASSERT(theHelp != NULL);
        LONG lResult;
        HKEY hKey;
        lResult = RegOpenKeyEx(HKEY_CURRENT_USER,
            "Software\\Microsoft\\Dependency Walker\\External Help",
            0, KEY_READ, &hKey);
        if (lResult == ERROR_SUCCESS)
        {
            CHAR szCollection[MAX_PATH];
            DWORD cbCollection = MAX_PATH;
            lResult = RegQueryValueEx(hKey, "Collection", NULL, 
                      NULL, (LPBYTE)szCollection, &cbCollection);
            if (lResult == ERROR_SUCCESS)
            {
                TRACE1("use collection: %s\n", szCollection);            if (stricmp(szCollection, "Online") == 0)
                {
                    WCHAR szURL[1024];
                    DWORD dwURL = sizeof(szURL);
                    lResult = RegQueryValueExW(hKey, L"URL", 
                                   NULL, NULL, (LPBYTE)szURL, &dwURL);
                    if (lResult == ERROR_SUCCESS)
                    {
                        WCHAR *p = wcsstr(szURL, L"%1");
                        if (p)
                        {
                            p[1] = L's';
                            WCHAR szLink[1024];
                            if (_snwprintf(szLink, sizeof(szLink)/sizeof(WCHAR), 
                                                   szURL, pszKeyword) > 0)
                            {
                                TRACE1("use collection: %S\n", szLink);
                                
                                ShellExecuteW(NULL, L"open", szLink, 
                                              NULL, NULL, SW_SHOWNORMAL);                            RegCloseKey(hKey);
                                return S_OK;
                            }
                        }
                    }
                } /* end Online */
                else
                {
                    HKEY hHelp;
                    lResult = RegOpenKeyEx(HKEY_LOCAL_MACHINE,
                        "SOFTWARE\\Microsoft\\MSDN\\7.0\\Help\\0x0409",
                        0, KEY_READ|KEY_ENUMERATE_SUB_KEYS, &hHelp);
                    if (lResult == ERROR_SUCCESS)
                    {
                        DWORD dwIndex = 0;
                        CHAR szGuid[MAX_PATH];
                        while (RegEnumKey(hHelp, dwIndex++, szGuid, 
                               MAX_PATH) == ERROR_SUCCESS)
                        {
                            HKEY hGuid;
                            if (RegOpenKeyEx(hHelp, szGuid, 0, 
                                             KEY_READ, &hGuid) != ERROR_SUCCESS)
                                continue;
                            
                            CHAR szCollection2[MAX_PATH];
                            DWORD cbCollection2 = MAX_PATH;
                            lResult = RegQueryValueEx(hGuid, NULL, NULL, 
                                      NULL, (LPBYTE)szCollection2, &cbCollection2);
                            if (lResult == ERROR_SUCCESS && 
                                stricmp(szCollection, szCollection2) == 0)
                            {
                                cbCollection2 = MAX_PATH;
                                lResult = RegQueryValueEx(hGuid, "Filename", 
                                          NULL, NULL, (LPBYTE)szCollection2, 
                                          &cbCollection2);
                                if (lResult == ERROR_SUCCESS &&
                                    strnicmp(szCollection2, "ms-help://", 
                                    sizeof("ms-help://")-1) == 0)
                                {
                                    TRACE1("use collection: %s\n", szCollection2);
                                    theHelp->SetCollection(szCollection2, "");
                                    
                                    RegCloseKey(hGuid);
                                    break;
                                }
                            }
                            
                            RegCloseKey(hGuid);
                        } /* end RegEnumKey(hHelp) */
                        
                        RegCloseKey(hHelp);
                    }
                } /* end not Online */          
            }        RegCloseKey(hKey);
        }    theHelp->SyncIndex(pszKeyword, 1);
        theHelp->DisplayTopicFromF1Keyword(pszKeyword);    return S_OK;
    }
      

  2.   

    /*
    Easy, right?There are two things you must keep in mind. First, we must declare the vc6Help in global space and release 
    it when the program quits. This is because the vshelp.dll will be freed if there is no more instance. 
    If so, then what we modified will be gone with it. Then second, we must call VirtualProtect to make
    the virtual table become writable, otherwise you cannot modify the virtual table because it's read only.Sometimes, you may have a couple of help collections installed. After you press F1, it may not show the help
    collection you want.How to set the default help collection?
    I believe you have installed the latest Platform SDK. If you don't have one, download it from Microsoft. 
    It's free and useful. You must download and install it. 
    Run 'Depends.exe' under the SDK\bin directory. 
    Select menu 'Options'->'Configure External Function Help Collection'. 
    Choose the help collection you want. You can even choose 'MSDN Online' and this add-in will show
    online help. This means you can get help without having MSDN installed. 
    怎么使用??
    Click VC6's menu "Tools"->"Customize"->"Add-ins and Macro Files"->"Browse". 
    Then choose "VSNetHelp.dll", and click "Close".Move the caret to a keyword, press F1. You got it!Chunhua Liu*/