一:創建一個接口
- 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;
- };
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文件定義接口,其會生成相關頭文件,但可讀性很差
二.聲明一個C++類實現該接口
- 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;
- };
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;
- }
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;
}
三.爲接口和類指定各自指定一個GUID
使用VC++擴展方法
- struct __declspec(uuid("{7D51904D-1645-4a8c-BDE0-0F4A44FC38C4}")) ISimpleMsgBox;
- class __declspec(uuid("{7D51904E-1645-4a8c-BDE0-0F4A44FC38C4}")) CSimpleMsgBoxImpl;
struct __declspec(uuid("{7D51904D-1645-4a8c-BDE0-0F4A44FC38C4}")) ISimpleMsgBox;
class __declspec(uuid("{7D51904E-1645-4a8c-BDE0-0F4A44FC38C4}")) CSimpleMsgBoxImpl;
這樣便可以使用__uuidof關鍵字了
__uuidof使用方法介紹
屬於VC++擴展語法
- class Demo
- {
- };
- class __declspec(uuid("B372C9F6-1959-4650-960D-73F20CD479BA")) Demo;
- int main( void )
- {
- CLSID clsid = __uuidof(Demo);
- }
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;
- }
//////////////////////////////////////////////////////////////////////
// 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;
}
五.註冊COM組件(手動註冊)
萬事俱備,現在要將其信息寫入註冊表,
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;
- }
// 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
EXPORTS
DllRegisterServer PRIVATE
DllUnregisterServer PRIVATE
DllGetClassObject PRIVATE
DllCanUnloadNow PRIVATE
3.編譯通過後使用regsvr32命令,註冊此dll組件
在調用此com組件之前,必須通過cmd命令註冊,很重要,DllRegisterServer將會成爲入口點,
六.編寫DllGetClassObject
只有通過全局函數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;
- }
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;
}
七.調用com組件
- 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();
- }
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 );
HRESULT hr;
IClassFactory *pc=NULL;
hr=::CoGetClassObject(__uuidof(CSimpleMsgBoxImpl), CLSCTX_INPROC_SERVER,NULL,
IID_IClassFactory, (void**) &pc );