两个星期近乎狂热地查资料、调试,接着又是近两个星期的编写毕业论文,到现在已将近一个月时间了,
对我做的经历给个总结:
三天时间浏览了一些网上的com入门级教程、看了com技术内幕,然后开始写第一个com,从创建动态链接库
,到实现自己的IFactory,到编写组建的DllGetClassObject、DllCanUnloadNow、DllRegisterServer、
DllUnregisterServer,到组建加载成功用了将近一个星期,然后开始加原作者的核心代码到组建中,编译调
试......。然而,遗憾的是我发现我走到了尽头,按我对作者原文的理解,我的程序怎么也达不到作者提到的
结果,一按f1我的vc就报错。郁闷。。。。
最后找到了原作者的源码,发现我的代码比较幼稚可笑。
首先将KeywordSearch重定向到MyKeywordSearch的那段代码虽然写的正确,但是放错了地方,我将他放在
了DLLMain中(这就是我一直纳闷"Tools"->"Customize"->"Add-ins and Macro Files"->"加载我的dll时为什么
我的代码被反复执行的原因),原码中这段实现在CDSAddIn::OnConnection中实现,这样保证了这段代码只在第
一次加载插件时执行。
其次对MyKeywordSearch的实现想得过于简单,我的实现方式如下:
HRESULT __stdcall MyKeywordSearch(LPWSTR pszKeyword, long dwFlags, long dwReserved ){
Help* pHelp;
HRESULT _hr = theHelp.QueryInterface(__uuidof(VsHelp::Help), (void**)&pHelp);
if (FAILED(_hr)) AfxMessageBox("failed,return");
_hr = pHelp->DisplayTopicFromF1Keyword(pszKeyword);
if (FAILED(_hr)) AfxMessageBox("failed2,return");
return _hr;
}
其中theHelp的定义为:HelpPtr theHelp;
而源码的实现为:
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;
}
看过源码有茅塞顿开之感觉,真是学到了不少东西!
先将源码中的两个主要文件内容贴出来
//
#include " stdafx.h "
#include " VSNetHelp.h "
#include " DSAddIn.h "
#include " Commands.h "
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
#import " C:Program FilesCommon FilesMicrosoft SharedMSEnvvshelp.tlb "
// #import "C:Program FilesCommon FilesMicrosoft SharedVS98vshelp.dll"
#include " vc6help.tlh "
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 dwFlags, long dwReserved);
} ;
struct _IVsHelpSystem
... {
struct _IVsHelpSystemVtbl * lpVtbl;
} ;
VsHelp::HelpPtr theHelp;
VsHelpServices::IVsHelpSystemPtr vc6Help;
_IVsHelpSystem * iHelp = NULL;
HRESULT (STDMETHODCALLTYPE * OldKeywordSearch)(IUnknown * This, LPWSTR pszKeyword, long dwFlags, long dwReserved) = NULL;
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 " , 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 " , 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 " , 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;
}
// This is called when the user first loads the add-in, and on start-up
// of each subsequent Developer Studio session
STDMETHODIMP CDSAddIn::OnConnection(IApplication * pApp, VARIANT_BOOL bFirstTime,
long dwCookie, VARIANT_BOOL * OnConnection)
... {
AFX_MANAGE_STATE(AfxGetStaticModuleState());
// Store info passed to us
IApplication * pApplication = NULL;
if (FAILED(pApp -> QueryInterface(IID_IApplication, ( void ** ) & pApplication))
|| pApplication == NULL)
... {
* OnConnection = VARIANT_FALSE;
return S_OK;
}
... {
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 " , iHelp);
TRACE1( " lpVtbl = %x " , iHelp -> lpVtbl);
TRACE1( " KeywordSearch = %x " , iHelp -> lpVtbl -> KeywordSearch);
OldKeywordSearch = iHelp -> lpVtbl -> KeywordSearch;
DWORD dwOldProtect;
if (VirtualProtect(iHelp -> lpVtbl, sizeof (_IVsHelpSystemVtbl), PAGE_READWRITE, & dwOldProtect))
iHelp -> lpVtbl -> KeywordSearch = MyKeywordSearch;
}
}
}
m_dwCookie = dwCookie;
// Create command dispatch, send info back to DevStudio
CCommandsObj::CreateInstance( & m_pCommands);
m_pCommands -> AddRef();
// The QueryInterface above AddRef'd the Application object. It will
// be Release'd in CCommand's destructor.
m_pCommands -> SetApplicationObject(pApplication);
// (see stdafx.h for the definition of VERIFY_OK)
VERIFY_OK(pApplication -> SetAddInInfo(( long ) AfxGetInstanceHandle(),
(LPDISPATCH) m_pCommands, - 1 , - 1 , m_dwCookie));
// Inform DevStudio of the commands we implement
// TODO: Replace the AddCommand call below with a series of calls,
// one for each command your add-in will add.
// The command name should not be localized to other languages. The
// tooltip, command description, and other strings related to this
// command are stored in the string table (IDS_CMD_STRING) and should
// be localized.
LPCTSTR szCommand = _T( " VSNetHelpCommand " );
VARIANT_BOOL bRet;
CString strCmdString;
strCmdString.LoadString(IDS_CMD_STRING);
strCmdString = szCommand + strCmdString;
CComBSTR bszCmdString(strCmdString);
CComBSTR bszMethod(_T( " VSNetHelpCommandMethod " ));
VERIFY_OK(pApplication -> AddCommand(bszCmdString, bszMethod, - 1 , m_dwCookie, & bRet));
if (bRet == VARIANT_FALSE)
... {
// AddCommand failed because a command with this name already
// exists. You may try adding your command under a different name.
// Or, you can fail to load as we will do here.
* OnConnection = VARIANT_FALSE;
return S_OK;
}
* OnConnection = VARIANT_TRUE;
return S_OK;
}
// This is called on shut-down, and also when the user unloads the add-in
STDMETHODIMP CDSAddIn::OnDisconnection(VARIANT_BOOL bLastTime)
... {
AFX_MANAGE_STATE(AfxGetStaticModuleState());
m_pCommands -> Release();
m_pCommands = NULL;
// TODO: Perform any cleanup work here
if (iHelp && iHelp -> lpVtbl -> KeywordSearch == MyKeywordSearch)
... {
iHelp -> lpVtbl -> KeywordSearch = OldKeywordSearch;
iHelp = NULL;
}
if (theHelp)
theHelp.Release();
if (vc6Help)
vc6Help.Release();
return S_OK;
}
有兴趣的话可以mail我索取原作者的源码:maomaovv@gmail.com