晚上在博客堂讀完VSTO寫的一篇文章,介紹了CodePlex上面的一個項目,叫做 “All-In-One Code Framework”,代號CodeFx,簡單的說,就是收集了幾乎所有 常見的微軟開發技術的示例項目,將其打包到這個框架裡,而且還使用各種不同的語言進行 實現。比如創建一個ActiveX控件和COM組件,CodeFx裡面使用ATL、MFC、VB、C#來實現同樣 的功能。
適合新手入門,也可以作為一份模板供經驗豐富的開發者使用,可節省大量的時間。官方 網站上給出了框架的基本結構,如下圖所示:
COM組件和ActiveX控件示例
數據訪問示例
庫示例
進程間通信示例
花了2 個小時粗略閱讀了代碼,記錄下學習心得:
1)先來說ActiveX這條線,它裡面使用了 ATL(這裡有2種實現,進程內和進程外),MFC,C#,VB四種技術來實現。功能就是四點:一個返 回字符串的HelloWorld方法,一個float類型的屬性FloatProperty,一個返回進程號和線程 號的GetProcessThreadID方法,一個FloatPropertyChanging事件。
2)授權支持是 ActiveX 控件的一項可選功能,它使您得以控制能使用或分發該控件的人。(請參見《MFC ActiveX 控件:授權 ActiveX 控件》)。
頭文件的修改
“ActiveX 控件向導”將下列代碼放置在控件頭文件中。聲明了 factory 對象的兩個成員函數,其中一個成員函數驗證控件 .LIC 文件是否存在,而另一個成員函數 則對包含該控件的應用程序中使用的許可證密鑰進行檢索:
BEGIN_OLEFACTORY(CMFCActiveXCtrl) // Class factory and guid
virtual BOOL VerifyUserLicense();
virtual BOOL GetLicenseKey(DWORD, BSTR FAR*);
END_OLEFACTORY(CMFCActiveXCtrl)
實現文件的修改
“ActiveX 控件向導”將下面兩條語句放置在控件實現文件中,以聲明許 可文件名和許可字符串:
static const TCHAR BASED_CODE _szLicFileName[] =
_T("License.lic");
static const WCHAR BASED_CODE _szLicString[] =
L"Copyright (c) 2000 ";
注意:如果以任何方式修改 szLicString,則必須也修改控件 .LIC 文件的第一行,否則授權將無法正確運行。
“ActiveX 控件向導”將下列代碼放置在控件實現文件中,以定義控件類 的 VerifyUserLicense 函數和 GetLicenseKey 函數:
// CMFCActiveXCtrl::CMFCActiveXCtrlFactory::VerifyUserLicense -
// Checks for existence of a user license
BOOL CMFCActiveXCtrl::CMFCActiveXCtrlFactory::VerifyUserLicense()
{
return AfxVerifyLicFile(AfxGetInstanceHandle(), _szLicFileName,
_szLicString);
}
// CMFCActiveXCtrl::CMFCActiveXCtrlFactory::GetLicenseKey -
// Returns a runtime licensing key
BOOL CMFCActiveXCtrl::CMFCActiveXCtrlFactory::GetLicenseKey(DWORD dwReserved,
BSTR FAR* pbstrKey)
{
if (pbstrKey == NULL)
return FALSE;
*pbstrKey = SysAllocString(_szLicString);
return (*pbstrKey != NULL);
}
最後,“ActiveX 控件向導”修改控件項目 .IDL 文件。將關鍵 字 licensed 添加到控件的 coclass 聲明中,如下例所示:
[ uuid (E389AD6C-4FB6-47AF-B03A-A5A5C6B2B820), licensed,
helpstring("MFCActiveX Control"), control ]
coclass MFCActiveX
3)作者封裝了一個方法AutoWrap來調用COM組件 公開出來的屬性或方法。
HRESULT AutoWrap(int autoType, VARIANT *pvResult, IDispatch *pDisp,
LPOLESTR ptName, int cArgs)
{
// Begin variable-argument list
va_list marker;
va_start(marker, cArgs);
if (!pDisp)
{
_putts(_T("NULL IDispatch passed to AutoWrap()"));
_exit(0);
}
// Variables used
DISPPARAMS dp = { NULL, NULL, 0, 0 };
DISPID dispidNamed = DISPID_PROPERTYPUT;
DISPID dispID;
HRESULT hr;
char szName[200];
// Convert down to ANSI
WideCharToMultiByte(CP_ACP, 0, ptName, -1, szName, 256, NULL, NULL);
// Get DISPID for name passed
hr = pDisp->GetIDsOfNames(IID_NULL, &ptName, 1, LOCALE_USER_DEFAULT,
&dispID);
if (FAILED(hr))
{
_tprintf(_T(
"IDispatch::GetIDsOfNames(\"%s\") failed w/err 0x%08lx\n"
), szName, hr);
return hr;
}
// Allocate memory for arguments
VARIANT *pArgs = new VARIANT[cArgs+1];
// Extract arguments
for(int i=0; i<cArgs; i++)
{
pArgs[i] = va_arg(marker, VARIANT);
}
// Build DISPPARAMS
dp.cArgs = cArgs;
dp.rgvarg = pArgs;
// Handle special-case for property-puts
if (autoType & DISPATCH_PROPERTYPUT)
{
dp.cNamedArgs = 1;
dp.rgdispidNamedArgs = &dispidNamed;
}
// Make the call
hr = pDisp->Invoke(dispID, IID_NULL, LOCALE_SYSTEM_DEFAULT,
autoType, &dp, pvResult, NULL, NULL);
if (FAILED(hr))
{
_tprintf(_T(
"IDispatch::Invoke(\"%s\"=%08lx) failed w/err 0x%08lx\n"
), szName, dispID, hr);
return hr;
}
// End variable-argument section
va_end(marker);
delete[] pArgs;
return hr;
}
4)DLL的延遲加載使得我們不需要使用LoadLibrary和GetProcAddress。這 樣的好處是直到程序調用DLL中的函數時才加載此DLL。
#include <Delayimp.h>
卸載延遲加載的DLL的代碼:
PCSTR pszDll = "CppDllExport.dll";
_tprintf(_T("__FUnloadDelayLoadedDLL2 => %d\n"),
__FUnloadDelayLoadedDLL2(pszDll));