1、 创建一个win32 dll应用类型项目工程。
2、 添加ComTest.h及ComTest.cpp文件,并在ComTest.h头文件包含Unknwn.h头文件
3、 用guid.exe生成一个接口IID及com对象类CLSID
代码如下:
//ComTest.h
#pragma once
#include <Unknwn.h>
EXTERN_C const CLSID CLSID_TestObject;
EXTERN_C const CLSID IID_ObjectInterface;
MIDL_INTERFACE("DE5A091A-EF80-4bd3-8AEB-CB20B879A2F1")
IObjectInterface :public IUnknown
{
virtual void __stdcall TestMethod() PURE;
};
//ComTest.cpp
#include "StdAfx.h"
#include "ComTest.h"
EXTERN_C const CLSID CLSID_TestObject =
{0xa33e547a, 0x8a95, 0x4118, {0x86, 0xa, 0x46, 0xb2, 0x3c, 0x6d, 0x40, 0xbd}};
EXTERN_C const CLSID IID_ObjectInterface =
{0xde5a091a, 0xef80, 0x4bd3, {0x8a, 0xeb, 0xcb, 0x20, 0xb8, 0x79, 0xa2, 0xf1}};
4、 添加CTestObject类,这个类必须实现IObjectInterface接口,并且必须实现IUnkown接口的三个函数。
代码如下
//TestObject.h
#pragma once
#include "ComTest.h"
class CTestObject:public IObjectInterface
{
public:
CTestObject(void);
public:
~CTestObject(void);
public:
HRESULT __stdcall QueryInterface( REFIID riid,void **ppvObject);
ULONG __stdcall AddRef( void);
ULONG __stdcall Release( void);
public:
void __stdcall TestMethod();
private:
int m_nRef;
};
//TestObject.cpp
#include "StdAfx.h"
#include "TestObject.h"
#include <iostream>
using namespace std;
CTestObject::CTestObject(void)
:m_nRef(0)
{
}
CTestObject::~CTestObject(void)
{
}
HRESULT CTestObject::QueryInterface( REFIID riid,void **ppvObject)
{
if (riid==__uuidof(IUnknown))
{
*ppvObject = (IUnknown*)this;
((IUnknown*)*ppvObject)->AddRef();
return S_OK;
}
else if (riid==__uuidof(IObjectInterface))
{
*ppvObject = (IObjectInterface*)this;
((IObjectInterface*)*ppvObject)->AddRef();
return S_OK;
}
else
{
*ppvObject = NULL;
return E_NOINTERFACE;
}
}
ULONG CTestObject::AddRef( void)
{
m_nRef++;
return m_nRef;
}
ULONG CTestObject::Release( void)
{
m_nRef--;
if (m_nRef==0)
{
delete this;
}
return m_nRef;
}
void CTestObject::TestMethod()
{
cout<<"this is my com test!"<<endl;
}
5、 必须要为com对象创建一个工厂类,该工厂类还必须实现IClassFactory接口。
当我们调用CoCreateInstance函数创建com对象的时候,com标准库会调用DllGetClassObject获取工厂对象,再由工厂对象调用其成员函数CreateInstance来创建我们需要的com对象。
代码如下:
//.h
#pragma once
#include <Unknwn.h>
extern ULONG g_LockNumber;
class CTestObjectFactory:public IClassFactory
{
protected:
int m_nRef;
public:
CTestObjectFactory(void);
public:
~CTestObjectFactory(void);
public:
HRESULT __stdcall QueryInterface( REFIID riid,void **ppvObject);
ULONG __stdcall AddRef( void);
ULONG __stdcall Release( void);
public:
HRESULT STDMETHODCALLTYPE CreateInstance(IUnknown *pUnkOuter,REFIID riid,void **ppvObject);
HRESULT STDMETHODCALLTYPE LockServer(BOOL fLock) ;
};
//.cpp
#include "StdAfx.h"
#include "ComTest.h"
#include "TestObject.h"
#include "TestObjectFactory.h"
CTestObjectFactory::CTestObjectFactory(void)
:m_nRef(0)
{
}
CTestObjectFactory::~CTestObjectFactory(void)
{
}
HRESULT CTestObjectFactory::QueryInterface( REFIID riid,void **ppvObject)
{
if (riid==__uuidof(IUnknown))
{
*ppvObject = (IUnknown*)this;
((IUnknown*)*ppvObject)->AddRef();
return S_OK;
}
else if (riid==__uuidof(IClassFactory))
{
*ppvObject = (IClassFactory*)this;
((IClassFactory*)*ppvObject)->AddRef();
return S_OK;
}
else
{
*ppvObject = NULL;
return E_NOINTERFACE;
}
}
ULONG CTestObjectFactory::AddRef( void)
{
m_nRef++;
return m_nRef;
}
ULONG CTestObjectFactory::Release( void)
{
m_nRef--;
if (m_nRef==0)
{
delete this;
}
return m_nRef;
}
HRESULT CTestObjectFactory::CreateInstance(IUnknown *pUnkOuter,REFIID riid,void **ppvObject)
{
if (NULL != pUnkOuter)
return CLASS_E_NOAGGREGATION;
DT(L"Enter CreateInstance...");
CTestObject *pObj=NULL;
pObj = new CTestObject();
if (pObj==NULL)
{
*ppvObject = NULL;
return E_OUTOFMEMORY;
}
HRESULT hr = pObj->QueryInterface(riid,ppvObject);
if (FAILED(hr))
{
DT(L"Failed to Create TestObj!");
delete pObj;
}
return hr;
}
HRESULT CTestObjectFactory::LockServer(BOOL fLock)
{
if (fLock)
{
g_LockNumber++;
}
else
g_LockNumber--;
return NOERROR;
}
6、 有了com对象类和工厂类后,我们要想办法将我们的com对象信息写到注册表中去,
一般要写入以下信息:
HKEY_LOCAL_MACHINE/SOFTWARE/Classes/CLSID/{A33E547A-8A95-4118-860A-46B23C6D40BD}
HKEY_LOCAL_MACHINE/SOFTWARE/Classes/CLSID/{A33E547A-8A95-4118-860A-46B23C6D40BD}/InprocServer32
HKEY_LOCAL_MACHINE/SOFTWARE/Classes/CLSID/{A33E547A-8A95-4118-860A-46B23C6D40BD}/ProgID
HKEY_LOCAL_MACHINE/SOFTWARE/Classes/TestCom.Object
HKEY_LOCAL_MACHINE/SOFTWARE/Classes/TestCom.Object/CLSID
7、 要添加导出文件,添加下面四个函数的实现:
LIBRARY "Component"
EXPORTS
DllGetClassObject PRIVATE
DllRegisterServer PRIVATE
DllUnregisterServer PRIVATE
DllCanUnloadNow PRIVATE
有关这四个函数的实现,参考如下代码:
// Registry.cpp
#include "StdAfx.h"
#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 TCHAR* pszPath,
const TCHAR* szSubkey,
const TCHAR* szValue) ;
// Convert a CLSID into a char string.
void CLSIDtoString(const CLSID& clsid,
TCHAR* szCLSID,
int length) ;
// Delete szKeyChild and all of its descendents.
LONG DeleteKey(HKEY hKeyParent, const TCHAR* szKeyString) ;
////////////////////////////////////////////////////////
//
// Constants
//
// Size of a CLSID as a string
const int CLSID_STRING_SIZE = 39 ;
/////////////////////////////////////////////////////////
//
// Public function implementation
//
//
// Register the component in the registry.
//
HRESULT RegisterServer(const CLSID& clsid, // Class ID
const TCHAR *szFileName, // DLL module handle
const TCHAR* szProgID, // IDs
const TCHAR* szDescription, // Description String
const TCHAR* szVerIndProgID) // optional
{
DT(szFileName);
DT(szProgID);
// Convert the CLSID into a char.
TCHAR szCLSID[CLSID_STRING_SIZE] ;
CLSIDtoString(clsid, szCLSID, sizeof(szCLSID)) ;
//StringFromCLSID(clsid,szCLSID);
// Build the key CLSID//{...}
TCHAR szKey[64] ;
lstrcpy(szKey, L"CLSID//") ;
lstrcat(szKey, szCLSID) ;
DT(szKey);
// Add the CLSID to the registry.
SetKeyAndValue(szKey, NULL, szDescription) ;
// Add the server filename subkey under the CLSID key.
SetKeyAndValue(szKey, L"InprocServer32", szFileName) ;
// Add the ProgID subkey under the CLSID key.
if (szProgID != NULL) {
SetKeyAndValue(szKey, L"ProgID", szProgID) ;
SetKeyAndValue(szProgID, L"CLSID", szCLSID) ;
}
if (szVerIndProgID) {
// Add the version-independent ProgID subkey under CLSID key.
SetKeyAndValue(szKey, L"VersionIndependentProgID",
szVerIndProgID) ;
// Add the version-independent ProgID subkey under HKEY_CLASSES_ROOT.
SetKeyAndValue(szVerIndProgID, NULL, szDescription) ;
SetKeyAndValue(szVerIndProgID, L"CLSID", szCLSID) ;
SetKeyAndValue(szVerIndProgID, L"CurVer", szProgID) ;
// Add the versioned ProgID subkey under HKEY_CLASSES_ROOT.
SetKeyAndValue(szProgID, NULL, szDescription) ;
SetKeyAndValue(szProgID, L"CLSID", szCLSID) ;
}
return S_OK ;
}
//
// Remove the component from the registry.
//
HRESULT UnregisterServer(const CLSID& clsid, // Class ID
const TCHAR* szProgID, // IDs
const TCHAR* szVerIndProgID) // Programmatic
{
// Convert the CLSID into a char.
TCHAR szCLSID[CLSID_STRING_SIZE] ;
CLSIDtoString(clsid, szCLSID, sizeof(szCLSID)) ;
//StringFromCLSID(clsid,szCLSID);
// Build the key CLSID//{...}
TCHAR szKey[64] ;
lstrcpy(szKey, L"CLSID//") ;
lstrcat(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,
TCHAR* szCLSID,
int length)
{
assert(length >= CLSID_STRING_SIZE) ;
// Get CLSID
LPOLESTR wszCLSID = NULL ;
HRESULT hr = StringFromCLSID(clsid, &wszCLSID) ;
assert(SUCCEEDED(hr)) ;
lstrcpy(szCLSID,wszCLSID);
DT(szCLSID);
// Free memory.
CoTaskMemFree(wszCLSID) ;
}
//
// Delete a key and all of its descendents.
//
LONG DeleteKey(HKEY hKeyParent, // Parent of key to delete
const TCHAR* 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 ;
TCHAR 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 TCHAR* szKey,
const TCHAR* szSubkey,
const TCHAR* szValue)
{
HKEY hKey;
TCHAR szKeyBuf[1024] ;
// Copy keyname into buffer.
lstrcpy(szKeyBuf, szKey) ;
// Add subkey name to buffer.
if (szSubkey != NULL)
{
lstrcat(szKeyBuf, L"//") ;
lstrcat(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)
{
DT(L"SetKeyAndValue Failed!");
return FALSE ;
}
// Set the Value.
if (szValue != NULL)
{
RegSetValueEx(hKey, NULL, 0, REG_SZ,
(BYTE *)szValue,
2*lstrlen(szValue)+1) ;
}
RegCloseKey(hKey) ;
DT(L"SetKeyAndValue Succeed!");
return TRUE ;
}
//componet.com
#include "stdafx.h"
#include "Registry.h"
//#include "ComTest.h"
#ifdef _MANAGED
#pragma managed(push, off)
#endif
HMODULE g_hModule = NULL;
ULONG g_LockNumber = 0;
EXTERN_C const CLSID CLSID_TestObject;
EXTERN_C const CLSID IID_ObjectInterface;
//class CTestObjectFactory;
#include "TestObjectFactory.h"
STDAPI DllGetClassObject(const CLSID& clsid, const IID& iid, void **ppv)
{
DT(L"DllGetClassObject...");
if (clsid == CLSID_TestObject ) {
CTestObjectFactory *pFactory = new CTestObjectFactory;
if (pFactory == NULL) {
return E_OUTOFMEMORY ;
}
HRESULT result = pFactory->QueryInterface(iid, ppv);
return result;
} else {
return CLASS_E_CLASSNOTAVAILABLE;
}
}
STDAPI DllCanUnloadNow(void)
{
if (g_LockNumber == 0)
return S_OK;
else
return S_FALSE;
}
STDAPI DllRegisterServer()
{
TCHAR szFileName[MAX_PATH];
::GetModuleFileName((HMODULE)g_hModule,szFileName,sizeof(szFileName));
DT(szFileName);
return RegisterServer(CLSID_TestObject,szFileName,L"TestCom.Object",L"Test Componet",NULL);
}
STDAPI DllUnregisterServer()
{
//return AMovieDllRegisterServer2(FALSE);
return UnregisterServer(CLSID_TestObject,L"TestCom.Object",NULL);
}
BOOL APIENTRY DllMain( HMODULE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
if (!g_hModule)
{
g_hModule = hModule;
}
return TRUE;
}
#ifdef _MANAGED
#pragma managed(pop)
#endif