對於初學com的,一個完整的樣本還是挺麻煩的,最近筆者也在學習,通過自己的摸索瞭解了下基本原理。在此將一個簡單string的樣本一步一步的詳解。
原始碼下載
1.建立工程
首先使用vs2010(筆者使用的vs2010就拿此來講)建立一個工程。選擇空項目,筆者命名為MyCom。
2.建立介面類
#ifndef IString_h__#define IString_h__#include <Windows.h>#include <Unknwn.h>#include <ObjBase.h>extern "C" const GUID IID_IString;class IString : public IUnknown{public:virtual HRESULT STDMETHODCALLTYPE SetString(char*) = 0;virtual HRESULT STDMETHODCALLTYPE GetString(char*, long) = 0;virtual HRESULT STDMETHODCALLTYPE AboutMessage() = 0;};#endif // IString_h__
3.建立組件類
#ifndef String_h__#define String_h__#include "IString.h"class String :public IString{public:String();~String();public://IUkown FunctionHRESULT STDMETHODCALLTYPE QueryInterface(REFIID iid,LPVOID *ppv);ULONG STDMETHODCALLTYPE AddRef();ULONG STDMETHODCALLTYPE Release();//IString FunctionHRESULT STDMETHODCALLTYPE SetString(char* chBuf);HRESULT STDMETHODCALLTYPE GetString(char* chBuf, long cLength);HRESULT STDMETHODCALLTYPE AboutMessage();protected:ULONG m_Ref;char buffer[80];};#endif // String_h__
3.定義類廠
#ifndef CFactory_h__#define CFactory_h__#include <Unknwn.h>class CFactory : public IClassFactory{public:CFactory();~CFactory();//IUnknown membersHRESULT STDMETHODCALLTYPE QueryInterface(const IID& iid, void **ppv);ULONGSTDMETHODCALLTYPE AddRef();ULONGSTDMETHODCALLTYPE Release();//IClassFactory membersHRESULT STDMETHODCALLTYPE CreateInstance(IUnknown *, const IID& iid, void **ppv);HRESULT STDMETHODCALLTYPE LockServer(BOOL bIsLocck);protected:ULONG m_Ref;};#endif // CFactory_h__
4.類廠實現
#include "CFactory.h"#include "String.h"extern ULONG g_LockNumber;extern ULONG g_StringNumber;CFactory::CFactory(){m_Ref = 0;}CFactory::~CFactory(){}HRESULT CFactory::QueryInterface(const IID& iid, void **ppv){if (iid == IID_IUnknown){*ppv = (IUnknown*)this;((IUnknown*)(*ppv))->AddRef();}else if(iid == IID_IClassFactory){*ppv = (IClassFactory*)this;((IClassFactory*)(*ppv))->AddRef();}else{*ppv = NULL;return E_NOINTERFACE;}return S_OK;}ULONG CFactory::AddRef(){m_Ref++;return m_Ref;}ULONG CFactory::Release(){m_Ref--;if (m_Ref == 0){delete this;return 0;}return m_Ref;}HRESULT CFactory::CreateInstance(IUnknown *pUnknownOuter, const IID& iid, void **ppv){HRESULT hr;String *pObj;*ppv = NULL;hr = E_OUTOFMEMORY;if(NULL != pUnknownOuter)return CLASS_E_NOAGGREGATION;pObj = new String();if(NULL == pObj)return hr;hr = pObj->QueryInterface(iid,ppv);if (hr != S_OK){g_StringNumber--;delete pObj;}return S_OK;}HRESULT CFactory::LockServer(BOOL bIsLock){if (bIsLock)g_LockNumber++;elseg_LockNumber--;return S_OK;}
5、引出函數def檔案
LIBRARY "MyCom"EXPORTS ; Explicit exports can go hereDllGetClassObject PRIVATEDllRegisterServer PRIVATEDllUnregisterServer PRIVATEDllCanUnloadNow PRIVATE
6.組件註冊
#ifndef __Registry_H__#define __Registry_H__//// Registry.h// - Helper functions registering and unregistering a component.//// - These helper functions were borrowed and modifed from// Dale Rogerson's book Inside COM.// This function will register a component in the Registry.// DllRegisterServer function should call this function.HRESULT RegisterServer(const CLSID& clsid, const char *szFileName, const char* szProgID, const char* szDescription, const char* szVerIndProgID) ;// This function will unregister a component. Components// DllUnregisterServer function should call this function.HRESULT UnregisterServer(const CLSID& clsid, const char* szProgID, const char* szVerIndProgID) ;#endif
7.組件註冊實現
//// Registry.cpp//#include <objbase.h>#include <assert.h>#include "Registry.h"//////////////////////////////////////////////////////////// Internal helper functions prototypes//// - These helper functions were borrowed and modifed from// Dale Rogerson's book Inside COM.// Set the given key and its value.BOOL SetKeyAndValue(const char* pszPath, const char* szSubkey, const char* szValue) ;// Convert a CLSID into a char string.void CLSIDtoString(const CLSID& clsid, char* szCLSID, int length) ;// Delete szKeyChild and all of its descendents.LONG DeleteKey(HKEY hKeyParent, const char* szKeyString) ;//////////////////////////////////////////////////////////// Constants//// Size of a CLSID as a stringconst int CLSID_STRING_SIZE = 39 ;///////////////////////////////////////////////////////////// Public function implementation////// Register the component in the registry.//HRESULT RegisterServer(const CLSID& clsid, // Class ID const char *szFileName, // DLL module handle const char* szProgID, // IDs const char* szDescription, // Description String const char* szVerIndProgID) // optional{ // Convert the CLSID into a char. char szCLSID[CLSID_STRING_SIZE] ; CLSIDtoString(clsid, szCLSID, sizeof(szCLSID)) ; // Build the key CLSID\\{...} char szKey[64] ; strcpy_s(szKey, "CLSID\\") ; strcat_s(szKey, szCLSID) ; // Add the CLSID to the registry. SetKeyAndValue(szKey, NULL, szDescription) ; // Add the server filename subkey under the CLSID key. SetKeyAndValue(szKey, "InprocServer32", szFileName) ; // Add the ProgID subkey under the CLSID key. if (szProgID != NULL) { SetKeyAndValue(szKey, "ProgID", szProgID) ; SetKeyAndValue(szProgID, "CLSID", szCLSID) ; } if (szVerIndProgID) { // Add the version-independent ProgID subkey under CLSID key. SetKeyAndValue(szKey, "VersionIndependentProgID", szVerIndProgID) ; // Add the version-independent ProgID subkey under HKEY_CLASSES_ROOT. SetKeyAndValue(szVerIndProgID, NULL, szDescription) ; SetKeyAndValue(szVerIndProgID, "CLSID", szCLSID) ; SetKeyAndValue(szVerIndProgID, "CurVer", szProgID) ; // Add the versioned ProgID subkey under HKEY_CLASSES_ROOT. SetKeyAndValue(szProgID, NULL, szDescription) ; SetKeyAndValue(szProgID, "CLSID", szCLSID) ; } return S_OK ;}//// Remove the component from the registry.//HRESULT UnregisterServer(const CLSID& clsid, // Class ID const char* szProgID, // IDs const char* szVerIndProgID) // Programmatic{ // Convert the CLSID into a char. char szCLSID[CLSID_STRING_SIZE] ; CLSIDtoString(clsid, szCLSID, sizeof(szCLSID)) ; // Build the key CLSID\\{...} char szKey[64] ; strcpy_s(szKey, "CLSID\\") ; strcat_s(szKey, szCLSID) ; // Delete the CLSID Key - CLSID\{...} LONG lResult = DeleteKey(HKEY_CLASSES_ROOT, szKey) ; // Delete the version-independent ProgID Key. if (szVerIndProgID != NULL) lResult = DeleteKey(HKEY_CLASSES_ROOT, szVerIndProgID) ; // Delete the ProgID key. if (szProgID != NULL) lResult = DeleteKey(HKEY_CLASSES_ROOT, szProgID) ; return S_OK ;}/////////////////////////////////////////////////////////////// Internal helper functions//// Convert a CLSID to a char string.void CLSIDtoString(const CLSID& clsid, char* szCLSID, int length){ assert(length >= CLSID_STRING_SIZE) ; // Get CLSID LPOLESTR wszCLSID = NULL ; HRESULT hr = StringFromCLSID(clsid, &wszCLSID) ; assert(SUCCEEDED(hr)) ; // Covert from wide characters to non-wide. wcstombs(szCLSID, wszCLSID, length) ; // Free memory. CoTaskMemFree(wszCLSID) ;}//// Delete a key and all of its descendents.//LONG DeleteKey(HKEY hKeyParent, // Parent of key to delete const char* lpszKeyChild) // Key to delete{ // Open the child. HKEY hKeyChild ; LONG lRes = RegOpenKeyEx(hKeyParent, lpszKeyChild, 0, KEY_ALL_ACCESS, &hKeyChild) ; if (lRes != ERROR_SUCCESS) { return lRes ; } // Enumerate all of the decendents of this child. FILETIME time ; char szBuffer[256] ; DWORD dwSize = 256 ; while (RegEnumKeyEx(hKeyChild, 0, szBuffer, &dwSize, NULL, NULL, NULL, &time) == S_OK) { // Delete the decendents of this child. lRes = DeleteKey(hKeyChild, szBuffer) ; if (lRes != ERROR_SUCCESS) { // Cleanup before exiting. RegCloseKey(hKeyChild) ; return lRes; } dwSize = 256 ; } // Close the child. RegCloseKey(hKeyChild) ; // Delete this child. return RegDeleteKey(hKeyParent, lpszKeyChild) ;}//// Create a key and set its value.//BOOL SetKeyAndValue(const char* szKey, const char* szSubkey, const char* szValue){ HKEY hKey; char szKeyBuf[1024] ; // Copy keyname into buffer. strcpy(szKeyBuf, szKey) ; // Add subkey name to buffer. if (szSubkey != NULL) { strcat(szKeyBuf, "\\") ; strcat(szKeyBuf, szSubkey ) ; } // Create and open key and subkey. long lResult = RegCreateKeyEx(HKEY_CLASSES_ROOT , szKeyBuf, 0, NULL, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &hKey, NULL) ; if (lResult != ERROR_SUCCESS) { return FALSE ; } // Set the Value. if (szValue != NULL) { RegSetValueEx(hKey, NULL, 0, REG_SZ, (BYTE *)szValue, strlen(szValue)+1) ; } RegCloseKey(hKey) ; return TRUE ;}
8.引出函數及組件函數實現
#include "CFactory.h"#include "Registry.h"#include "String.h"ULONG g_LockNumber = 0;ULONG g_StringNumber = 0;// {913AAE18-1D57-4868-AF2F-B47D32163E8F}extern "C" const GUID CLSID_String = { 0x913aae18, 0x1d57, 0x4868, { 0xaf, 0x2f, 0xb4, 0x7d, 0x32, 0x16, 0x3e, 0x8f } };// {416DC65F-48E2-436a-BA34-FC00AC3DA598}extern "C" const GUID IID_IString = { 0x416dc65f, 0x48e2, 0x436a, { 0xba, 0x34, 0xfc, 0x0, 0xac, 0x3d, 0xa5, 0x98 } };HMODULE g_hModule=0;BOOL APIENTRY DllMain( HMODULE hModule,DWORD ul_reason_for_call,LPVOID lpReserved){g_hModule = hModule;return TRUE;}extern "C" HRESULT STDMETHODCALLTYPE DllCanUnloadNow(){if((g_LockNumber == 0)&&(g_StringNumber))return S_OK;elsereturn S_FALSE;}extern "C" HRESULT STDMETHODCALLTYPE DllRegisterServer(){char szModule[1024];DWORD dwResult = ::GetModuleFileName((HMODULE)g_hModule, szModule, 1024);if (dwResult == 0)return -1;return RegisterServer(CLSID_String,szModule, "STRING.Object","MyCom String Component",NULL);}extern "C" HRESULT STDMETHODCALLTYPE DllUnregisterServer(){return UnregisterServer(CLSID_String,"STRING.Object",NULL);}extern "C" HRESULT STDMETHODCALLTYPE DllGetClassObject(__in REFCLSID rclsid, __in REFIID riid, __deref_out LPVOID FAR* ppv){if (rclsid == CLSID_String){CFactory *pFactory = new CFactory();if(pFactory == NULL)return E_OUTOFMEMORY;HRESULT hr = pFactory->QueryInterface(riid,ppv);return hr;}else{return CLASS_E_CLASSNOTAVAILABLE;}}HRESULT String::QueryInterface(REFIID iid,LPVOID *ppv){if (iid == IID_IUnknown){*ppv = (IUnknown*)this;((IUnknown*)(*ppv))->AddRef();}else if(iid == IID_IString){*ppv = (IClassFactory*)this;((IClassFactory*)(*ppv))->AddRef();}else{*ppv = NULL;return E_NOINTERFACE;}return S_OK;}String::String(){m_Ref = 0;}String::~String(){}ULONG String::AddRef(){m_Ref++;return m_Ref;}ULONG String::Release(){m_Ref--;if (m_Ref == 0){delete this;return 0;}return m_Ref;}HRESULT String::GetString(char*chBuf, long cLength){long i; // 拷貝IExample的buffer到傳入的buffer中 i = lstrlen(buffer); --cLength; if (i > cLength) i = cLength; CopyMemory(chBuf, buffer, i); chBuf[i] = 0; return(0); }HRESULT String::SetString(char* chBuf){DWORD i; // 把傳入的str拷貝到IExample的buffer中 i = lstrlen(chBuf); if (i > 79) i = 79; CopyMemory(buffer, chBuf, i); buffer[i] = 0; return(0);}HRESULT String::AboutMessage(){MessageBox(NULL,"hello, i am a message box!","Message",MB_OK);return S_OK;}
9.測試檔案編寫(記得手工註冊下dll)
#include "IString.h"#include <Windows.h>#include <iostream>using namespace std;// {913AAE18-1D57-4868-AF2F-B47D32163E8F}extern "C" const GUID CLSID_String = { 0x913aae18, 0x1d57, 0x4868, { 0xaf, 0x2f, 0xb4, 0x7d, 0x32, 0x16, 0x3e, 0x8f } };// {416DC65F-48E2-436a-BA34-FC00AC3DA598}extern "C" const GUID IID_IString = { 0x416dc65f, 0x48e2, 0x436a, { 0xba, 0x34, 0xfc, 0x0, 0xac, 0x3d, 0xa5, 0x98 } };int main(){IString *pIStr;IUnknown *pIUk = NULL;HRESULT hr;char chGetChar[80];CLSID rclsid;if(CoInitialize(NULL) != S_OK){cout<<"initialize Failed"<<endl;return -1;}hr = CLSIDFromProgID(OLESTR("STRING.Object"),&rclsid);if (hr != S_OK){cout<<"Can't find the dictionary CLSID!"<<endl;return -2;}hr = CoCreateInstance(rclsid,NULL,CLSCTX_INPROC_SERVER,IID_IUnknown,(LPVOID*)&pIUk);if (hr != S_OK){cout<<"Create object failed!"<<endl;return -2;}hr = pIUk->QueryInterface(IID_IString,(LPVOID*)&pIStr);if (hr != S_OK) {pIUk->Release();printf("QueryInterface IString failed!\n");return -3;}pIStr->SetString("wqlgregergerg");pIStr->GetString(chGetChar,sizeof(chGetChar));cout<<chGetChar<<endl;pIStr->AboutMessage();CoUninitialize();return 0;}
終於貼完了。一個完整的樣本,親測可以哈!一起學習。