void func() {
IX* pIX = NULL;
hr = CoCreateInstance(CLSID_MYCOMPONENT, NULL, CLSCTX_INPROC_SERVER,
IID_IX, (void**)pIX); // 錯誤:缺 & 運算子
assert(hr);
pIX->IxFunction(); // 行為未定義,多數情況錯誤
}
原因:建立組件/查詢介面函數非型別安全,void** 參數接受任意型別指標。
void func() {
CComPtr spIX = NULL;
hr = CoCreateInstance(CLSID_MYCOMPONENT, NULL, CLSCTX_INPROC_SERVER,
IID_IY, (void**)&pIX); // IID_IY 配 IX* → 錯誤
assert(hr);
pIX->IxFunction();
}
斷言能捕獲,但為何不從源頭杜絕?
void func() {
CComPtr spIX = NULL; // IID 在建構時綁定
spIX.CoCreateInstance(CLSID_MYCOMPONENT); // 無 IID、無 void**
assert(spIX);
spIX->IxFunction();
}
程式碼簡潔且型別安全。_com_ptr_t 更進一步:
_COM_SMARTPTR_TYPEDEF(ICalculator, IID_ICALCULATOR);
ICalculatorPtr spCalculator(CLSID_CALCULATOR); // 建構函數建立組件
spCalculator->Add(1, 2, &nSum);
_com_ptr_t:特化時指定 IID(_COM_SMARTPTR_TYPEDEF(ICalculator, IID_ICALCULATOR)),靈活但易錯配。
CComPtr:要求介面宣告時用 __declspec(uuid) 綁定 IID:
interface __declspec(uuid("994D80AC-A5B1-430a-A3E9-2533100B87CE")) ICalculator : IUnknown {
virtual HRESULT STDMETHODCALLTYPE Add(const int nNum1, const int nNum2, int* pnSum) const = 0;
virtual HRESULT STDMETHODCALLTYPE Sub(const int nMinuend, const int nSubtrahend, int* pnQuotient) const = 0;
};
否則 CComPtr 編譯失敗:no GUID has been associated with this object。
介面宣告統一採用 __declspec(uuid),兼容 CComPtr 編譯期檢查與 _com_ptr_t 靈活特化。雖犧牲些許可移植性,換來編譯期型別安全——先考慮程式正確執行,再談相容性。
IDL 定義介面並綁定 IID 更合理,但為示例清晰此處採用 C++ 語法。
—— 作者:liuchang5 的專欄