typedef struct Interface Interface ISimpleMsgBox : public IUnknown { // IUnknown STDMETHOD_(ULONG, AddRef)() PURE; STDMETHOD_(ULONG, Release)() PURE; STDMETHOD(QueryInterface)(REFIID riid, void** ppv) PURE; // ISimpleMsgBox STDMETHOD(DoSimpleMsgBox)(HWND hwndParent, BSTR bsMessageText) PURE; };
替代方案:使用idl文件定义接口,其会生成相关头文件,但可读性很差
class CSimpleMsgBoxImpl : public ISimpleMsgBox { public: CSimpleMsgBoxImpl(); virtual ~CSimpleMsgBoxImpl(); // IUnknown STDMETHOD_(ULONG, AddRef)(); STDMETHOD_(ULONG, Release)(); STDMETHOD(QueryInterface)(REFIID riid, void** ppv); // ISimpleMsgBox STDMETHOD(DoSimpleMsgBox)(HWND hwndParent, BSTR bsMessageText); protected: ULONG m_uRefCount; };
实现部分,只关注QueryInterface 和DoSimpleMsgBox 部分
STDMETHODIMP CSimpleMsgBoxImpl::QueryInterface ( REFIID riid, void** ppv ) { HRESULT hrRet = S_OK; TRACE(">>> SimpleMsgBoxSvr: In CSimpleMsgBoxImpl::QueryInterface()\n" ); // Check that ppv really points to a void*. if ( IsBadWritePtr ( ppv, sizeof(void*) )) return E_POINTER; // Standard QI initialization - set *ppv to NULL. *ppv = NULL; // If the client is requesting an interface we support, set *ppv. if ( InlineIsEqualGUID ( riid, IID_IUnknown )) { *ppv = (IUnknown*) this; TRACE(">>> SimpleMsgBoxSvr: Client QIed for IUnknown\n" ); } else if ( InlineIsEqualGUID ( riid, __uuidof(ISimpleMsgBox) )) { *ppv = (ISimpleMsgBox*) this; TRACE(">>> SimpleMsgBoxSvr: Client QIed for ISimpleMsgBox\n" ); } else { hrRet = E_NOINTERFACE; TRACE(">>> SimpleMsgBoxSvr: Client QIed for unsupported interface\n" ); } // If we're returning an interface pointer, AddRef() it. if ( S_OK == hrRet ) { ((IUnknown*) *ppv)->AddRef(); } return hrRet; } ////////////////////////////////////////////////////////////////////// // ISimpleMsgBox methods STDMETHODIMP CSimpleMsgBoxImpl::DoSimpleMsgBox ( HWND hwndParent, BSTR bsMessageText ) { _bstr_t bsMsg = bsMessageText; LPCTSTR szMsg = (TCHAR*) bsMsg; // Use _bstr_t to convert the string to ANSI if necessary. TRACE(">>> SimpleMsgBoxSvr: In CSimpleMsgBoxImpl::DoSimpleMsgBox()\n" ); MessageBox ( hwndParent, szMsg, _T("Simple Message Box"), MB_OK ); return S_OK; }
使用VC++扩展方法
struct __declspec(uuid("{7D51904D-1645-4a8c-BDE0-0F4A44FC38C4}")) ISimpleMsgBox; class __declspec(uuid("{7D51904E-1645-4a8c-BDE0-0F4A44FC38C4}")) CSimpleMsgBoxImpl;
class Demo { }; class __declspec(uuid("B372C9F6-1959-4650-960D-73F20CD479BA")) Demo; int main( void ) { CLSID clsid = __uuidof(Demo); }
每个类必须配有一个类厂,关注CreateInstance 和LockServer 方法.
CoCreateInstance方法内部会生命周期依赖这个接口
////////////////////////////////////////////////////////////////////// // IClassFactory methods STDMETHODIMP CSimpleMsgBoxClassFactory::CreateInstance ( IUnknown* pUnkOuter, REFIID riid, void** ppv ) { HRESULT hrRet; CSimpleMsgBoxImpl* pMsgbox; TRACE(">>> SimpleMsgBoxSvr: In CSimpleMsgBoxClassFactory::CreateInstance()\n" ); // We don't support aggregation, so pUnkOuter must be NULL. if ( NULL != pUnkOuter ) return CLASS_E_NOAGGREGATION; // Check that ppv really points to a void*. if ( IsBadWritePtr ( ppv, sizeof(void*) )) return E_POINTER; *ppv = NULL; // Create a new COM object! pMsgbox = new CSimpleMsgBoxImpl; if ( NULL == pMsgbox ) return E_OUTOFMEMORY; // QI the object for the interface the client is requesting. hrRet = pMsgbox->QueryInterface ( riid, ppv ); // If the QI failed, delete the COM object since the client isn't able // to use it (the client doesn't have any interface pointers on the object). if ( FAILED(hrRet) ) delete pMsgbox; return hrRet; } STDMETHODIMP CSimpleMsgBoxClassFactory::LockServer ( BOOL fLock ) { // Increase/decrease the DLL ref count, according to the fLock param. fLock ? g_uDllLockCount++ : g_uDllLockCount--; TRACE(">>> SimpleMsgBoxSvr: In CSimpleMsgBoxClassFactory::LockServer(), new DLL ref count = %d\n", g_uDllLockCount ); return S_OK; }
万事俱备,现在要将其信息写入注册表,
1.实现DllRegisterServer和DllUnregisterServer方法,并将方法声明为STDAPI,表明属于导出函数
// DllRegisterServer() creates the registy entries that tells COM where our // server is located and its threading model. STDAPI DllRegisterServer() { HKEY hCLSIDKey = NULL, hInProcSvrKey = NULL; LONG lRet; TCHAR szModulePath [MAX_PATH]; TCHAR szClassDescription[] = _T("SimpleMsgBox class"); TCHAR szThreadingModel[] = _T("Apartment"); __try { // Create a key under CLSID for our COM server. lRet = RegCreateKeyEx ( HKEY_CLASSES_ROOT, _T("CLSID\\{7D51904E-1645-4a8c-BDE0-0F4A44FC38C4}"), 0, NULL, REG_OPTION_NON_VOLATILE, KEY_SET_VALUE | KEY_CREATE_SUB_KEY, NULL, &hCLSIDKey, NULL ); if ( ERROR_SUCCESS != lRet ) return HRESULT_FROM_WIN32(lRet); // The default value of the key is a human-readable description of the coclass. lRet = RegSetValueEx ( hCLSIDKey, NULL, 0, REG_SZ, (const BYTE*) szClassDescription, sizeof(szClassDescription) ); if ( ERROR_SUCCESS != lRet ) return HRESULT_FROM_WIN32(lRet); // Create the InProcServer32 key, which holds info about our coclass. lRet = RegCreateKeyEx ( hCLSIDKey, _T("InProcServer32"), 0, NULL, REG_OPTION_NON_VOLATILE, KEY_SET_VALUE, NULL, &hInProcSvrKey, NULL ); if ( ERROR_SUCCESS != lRet ) return HRESULT_FROM_WIN32(lRet); // The default value of the InProcServer32 key holds the full path to our DLL. GetModuleFileName ( g_hinstThisDll, szModulePath, MAX_PATH ); lRet = RegSetValueEx ( hInProcSvrKey, NULL, 0, REG_SZ, (const BYTE*) szModulePath, sizeof(TCHAR) * (lstrlen(szModulePath)+1) ); if ( ERROR_SUCCESS != lRet ) return HRESULT_FROM_WIN32(lRet); // The ThreadingModel value tells COM how it should handle threads in our DLL. // The concept of apartments is beyond the scope of this article, but for // simple, single-threaded DLLs, use Apartment. lRet = RegSetValueEx ( hInProcSvrKey, _T("ThreadingModel"), 0, REG_SZ, (const BYTE*) szThreadingModel, sizeof(szThreadingModel) ); if ( ERROR_SUCCESS != lRet ) return HRESULT_FROM_WIN32(lRet); } __finally { if ( NULL != hCLSIDKey ) RegCloseKey ( hCLSIDKey ); if ( NULL != hInProcSvrKey ) RegCloseKey ( hInProcSvrKey ); } return S_OK; } // DllUnregisterServer() deleted the registy entries that DllRegisterServer() created. STDAPI DllUnregisterServer() { // Delete our registry entries. Note that you must delete from the deepest // key and work upwards, because on NT/2K, RegDeleteKey() doesn't delete // keys that have subkeys on NT/2K. RegDeleteKey ( HKEY_CLASSES_ROOT, _T("CLSID\\{7D51904E-1645-4a8c-BDE0-0F4A44FC38C4}\\InProcServer32") ); RegDeleteKey ( HKEY_CLASSES_ROOT, _T("CLSID\\{7D51904E-1645-4a8c-BDE0-0F4A44FC38C4}") ); return S_OK; }
2.定义def文件
EXPORTS DllRegisterServer PRIVATE DllUnregisterServer PRIVATE DllGetClassObject PRIVATE DllCanUnloadNow PRIVATE
3.编译通过后使用regsvr32命令,注册此dll组件
在调用此com组件之前,必须通过cmd命令注册,很重要,DllRegisterServer将会成为入口点,
只有通过全局函数DllGetClassObject,才可以创建类厂,这个方法com类库会去调用,其会根据CLSID返回一个类工厂(一个dll可能会有多个com类)
生命周期(非常重要):
CoCreateInstance->CoGetClassObject->DllGetClassObject->IClassFactory->CreateInstance
STDAPI DllGetClassObject ( REFCLSID rclsid, REFIID riid, void** ppv ) { HRESULT hrRet; CSimpleMsgBoxClassFactory* pFactory; TRACE(">>> SimpleMsgBoxSvr: In DllGetClassObject()\n"); // Check that the client is asking for the CSimpleMsgBoxImpl factory. if ( !InlineIsEqualGUID ( rclsid, __uuidof(CSimpleMsgBoxImpl) )) return CLASS_E_CLASSNOTAVAILABLE; // Check that ppv really points to a void*. if ( IsBadWritePtr ( ppv, sizeof(void*) )) return E_POINTER; *ppv = NULL; // Construct a new class factory object. pFactory = new CSimpleMsgBoxClassFactory; if ( NULL == pFactory ) return E_OUTOFMEMORY; // AddRef() the factory since we're using it. pFactory->AddRef(); // QI() the factory for the interface the client wants. hrRet = pFactory->QueryInterface ( riid, ppv ); // We're done with the factory, so Release() it. pFactory->Release(); return hrRet; }
void DoMsgBoxTest(HWND hMainWnd) { ISimpleMsgBox* pIMsgBox; HRESULT hr; hr = CoCreateInstance ( __uuidof(CSimpleMsgBoxImpl), NULL, CLSCTX_INPROC_SERVER, __uuidof(ISimpleMsgBox), (void**) &pIMsgBox ); if ( FAILED(hr) ) return; pIMsgBox->DoSimpleMsgBox ( hMainWnd, _bstr_t("Hello COM!") ); pIMsgBox->Release(); }
以上步骤不可以省略,可以看到创建一个com组件是比较麻烦的一个流程,很容易出错
或者先常见一个类工厂
HRESULT hr; IClassFactory *pc=NULL; hr=::CoGetClassObject(__uuidof(CSimpleMsgBoxImpl), CLSCTX_INPROC_SERVER,NULL, IID_IClassFactory, (void**) &pc );