2023年12月5日发(作者:)

C++插件化技术学习——基于COM的插件化

我理解,基于类多态的插件化对于一般的不跨语言不跨平台的应用程序来说已经够用了,今天要说的基于COM的插件化,我觉得更多的是

针对大型的跨平台跨语言,需求变动较频繁,涉及领域较广泛的的应用而设计的。

COM是一种跨应用和语言共享二进制代码的方法,它明确指出二进制模块(DLLS和EXEs)必须被编译成与指定的结构匹配。我理

解,COM其实就是定义了一种管理模块二进制代码的标准,不管你是用什么语言编写插件,最终都会转换为二进制,而依据COM标准就可

以把插件都统一组织成COM对象,这样,基于COM的插件不受限于不同的语言了,而且更方便对插件进行统一的管理。从本质上来

说,COM也是一种不局限操作系统的技术,但是目前貌似只在Windows操作系统出现过COM,有人见过在LINU或UNIX下使用COM的欢

迎留言。

对于COM编程,我个人也了解不多,这里主要是学习下COM编程的思想,用于C++插件化的实现。据我了解,目前基于COM思想实现插

件化主要有三种思路:

1. 基于windows系统自带的COM库来实现

2. 基于插件管理器+插件的形式的实现

3. 基于OSGI+微内核+系统插件+应用插件的模式来实现

今天主要对第一种方法实现以下,第2,3种,我看网上有很多基于这两种思路自己写的插件框架,本人水平有限,还需继续学习相关内

容。下面利用windows系统自带的COM库来实现一把基于COM的插件化,仍然以做衣服的案例来实现,其主要步骤如下.

主程序步骤:

1. 初始化COM库

2. 根据指定衣服样式(这里当作ProgID)获取对应的组件类ID

3. 根据组件类ID创建COM对象,并返回IUnknown指针

4. 根据协议接口ID查询协议接口类指针

5. 调用协议内的功能函数

6. 释放内存及COM库分配内存

插件实现步骤(即COM组件)

1. 按照协议接口类编写子类并实现接口函数,同时要实现IUnKnown接口的函数

2. 编写工厂子类,继承于IClassFactory,要实现IUnknown接口和IClassFactory接口,其中接口CreateInstance就是用来创建特定之

类对象的

3. 编写与COM库协议的4个协议函数 :(1)DllRegisterServer:注册插件;(2)DllUnregisterServer:删除注册插件;

(3)DllCanUnloadNow:判断是否可以卸载本组建, 由CoFreeUnusedLibraries函数调用;(4)DllGetClassObject 获取工厂类对象

4. 注册插件:

5. 运行主程序

这里,重点讲一下CoCreateInstance函数创建对象的过程。该函数内部首先会调用与COM库的协议接口DllGetClassObject,来获取特定

的子类工厂对象,然后根据子类工厂中重写的CreateInstance接口来获取特定的实现子类对象.。

另外,这里涉及几个标识符,首先,这里的给定字符表示“oth”作为progID,用于标识该插件实现哪种样式的衣服,该

progID下存储了一个GUID(“{9CA9DBE8-C0B1-42c9-B6C7-856BE5756855}”),这是一个全球唯一标识符,用于标识该组件

类,以获取特定的子类对象.该组件类ID下的InprocServer32键下存储了插件dll对应的路径,这样,我们就可以根据给定的progID获取特

定的dll,并创建子类对象。

在cmd中注册插件: dlll路径,内部其实就是调用协议函数DllRegisterServer进行注册的

注册后,插件的信息就存储到注册表中的HKEY_CLASSES_ROOT/oth和

HKEY_CLASSES_ROOT/CLSID/{9CA9DBE8-C0B1-42c9-B6C7-856BE5756855}下面

主程序实现代码如下:#include

#include "IComMakeClothes.h"

using namespace std;

int main()

{

//1.0 初始化COM,使用默认的内存分配器

CoInitialize(NULL);

//所有的工厂类或插件子类或接口类都是继承与COM库内部的IUnknown接口的

IUnknown* pUnKnown = NULL;

GUID CLSID_MakeFallClothes;

//根据给定的ProgID获取对应的组件CLSID

char progID[] = "oth";

HRESULT hRes = CLSIDFromProgID(L"oth", &CLSID_MakeFallClothes);

if(S_OK != hRes)

{

cout<<"Can't find the CLSID!"<

return -1;

}

else

{

//将CLSID从GUID的格式转成字符串,打印

LPOLESTR szCLSID;

StringFromCLSID(CLSID_MakeFallClothes, &szCLSID);

cout<<"find CLSID is "<

}

//用CLSID调用COM库函数CoCreateInstance创建COM对象并获取IUnKnown接口

hRes = CoCreateInstance(CLSID_MakeFallClothes, NULL,CLSCTX_INPROC_SERVER,IID_IUnknown,(void**)&pUnKnown);

if(S_OK != hRes || NULL == pUnKnown)

{

cout<<"create Object failed!"<

return -1;

}

IMakeClothes* pMakeClothes = NULL;

//通过接口QueryInterface查询协议接口IID_IMakeClothes对应的接口类

hRes = pUnKnown->QueryInterface(IID_IMakeClothes, (void**)&pMakeClothes);

//调用接口中的功能函数

//打版

pMakeClothes->MakePattern();

//裁剪

pMakeClothes->Tailor();

//缝制

pMakeClothes->SewUp();

//COM库反初始化

CoUninitialize();

return 0;

}

协议接口代码如下:#pragma once

#ifndef ICOMMAKECLOTHES_H_

#define ICOMMAKECLOTHES_H_

#include

// {81A80687-6CC4-4996-8DD2-F058907FDCA8}

static const GUID IID_IMakeClothes =

{ 0x81a80687, 0x6cc4, 0x4996, { 0x8d, 0xd2, 0xf0, 0x58, 0x90, 0x7f, 0xdc, 0xa8 } };

class IMakeClothes : public IUnknown

{

public:

virtual ~IMakeClothes(){};

virtual void MakePattern() = 0;

virtual void Tailor() = 0;

virtual void SewUp() = 0;

};

#endif

实现子类MakeFallClothes.h和代码如下:

//MakeFallClothes.h

#ifndef MAKEFALLCLOTHES_H_

#define MAKEFALLCLOTHES_H_

#include "IComMakeClothes.h"

#define API_OUT_C extern "C" _declspec(dllexport)//以C的方式导出

#define API_OUT_Cplusplus _declspec(dllexport) //以C++的方式导出

// {9CA9DBE8-C0B1-42c9-B6C7-856BE5756855}

static const GUID CLSID_FallClothes =

{ 0x9ca9dbe8, 0xc0b1, 0x42c9, { 0xb6, 0xc7, 0x85, 0x6b, 0xe5, 0x75, 0x68, 0x55 } };

class FallCloth : public IMakeClothes

{

public:

FallCloth();

virtual ~FallCloth();

//要实现IUnknown接口

API_OUT_Cplusplus virtual HRESULT _stdcall QueryInterface(const IID& riid, void** ppvObject);

API_OUT_Cplusplus virtual ULONG _stdcall AddRef();

API_OUT_Cplusplus virtual ULONG _stdcall Release();

//要实现的IComMakeClothes接口

API_OUT_Cplusplus virtual void MakePattern();

API_OUT_Cplusplus virtual void Tailor();

API_OUT_Cplusplus virtual void SewUp();

protected:

ULONG m_Ref;

static ULONG m_objNum;

};};

#endif

//

#include "MakeFallClothes.h"

#include

ULONG FallCloth::m_objNum = 0;//组件中CompTestClass对象的个数,用于判断是否可以卸载本组建,如值为0则可以卸载

FallCloth::FallCloth()

{

m_Ref = 0;

m_objNum ++; //构造了一个对象

}

FallCloth::~FallCloth()

{

m_objNum --; //释放了一个对象

}

void FallCloth::MakePattern()

{

printf("Fall Clothes has Maken Pattern.n");

}

void FallCloth::Tailor()

{

printf("Fall Clothes Tailor has finished.n");

}

void FallCloth::SewUp()

{

printf("Fall Clothes SewUp has Finished.n");

}

HRESULT _stdcall FallCloth::QueryInterface(const IID &riid, void **ppvObject)

{

if (IID_IUnknown == riid)

{

*ppvObject = (IUnknown*)this;

((IUnknown*)(*ppvObject))->AddRef();

}

else if (IID_IMakeClothes == riid)

{

*ppvObject = (IMakeClothes*)this;

((IMakeClothes*)(*ppvObject))->AddRef();

}

else

{

*ppvObject = NULL;

return E_NOINTERFACE;

}

return S_OK;

}

ULONG _stdcall FallCloth::AddRef()

{

m_Ref ++;

return m_Ref;

}

ULONG _stdcall FallCloth::Release()

{ m_Ref --;

if (0 == m_Ref)

{

delete this;

return 0;

}

return m_Ref;

}

工厂子类factory.h和代码如下:

//factory.h

#pragma once

#ifndef FACTORY_H

#define FACTORY_H

#include

#define API_OUT_Cplusplus _declspec(dllexport) //以C++的方式导出

class CompFactory : public IClassFactory

{

public:

CompFactory();

~CompFactory();

//要实现IUnknown接口

API_OUT_Cplusplus virtual HRESULT _stdcall QueryInterface(const IID& riid, void** ppvObject);

API_OUT_Cplusplus virtual ULONG _stdcall AddRef();

API_OUT_Cplusplus virtual ULONG _stdcall Release();

//要实现IClassFactory接口

API_OUT_Cplusplus virtual HRESULT _stdcall CreateInstance(IUnknown *pUnkOuter, const IID& riid, void **ppvObject);

API_OUT_Cplusplus virtual HRESULT _stdcall LockServer(BOOL fLock);

protected:

ULONG m_Ref;

};

#endif

//

#include "factory.h"

#include "MakeFallClothes.h"

CompFactory::CompFactory()

{

m_Ref = 0;

}

CompFactory::~CompFactory()

{

}

HRESULT _stdcall CompFactory::QueryInterface(const IID &riid, void **ppvObject)

{

if (IID_IUnknown == riid)

{

*ppvObject = (IUnknown*)this;

((IUnknown*)(*ppvObject))->AddRef();

}

else if (IID_IClassFactory == riid) else if (IID_IClassFactory == riid) {

*ppvObject = (IClassFactory*)this;

((IClassFactory*)(*ppvObject))->AddRef();

}

else {

*ppvObject = NULL;

return E_NOINTERFACE;

}

return S_OK;

}

ULONG _stdcall CompFactory::AddRef()

{

m_Ref ++;

return m_Ref;

}

ULONG _stdcall CompFactory::Release()

{

m_Ref --;

if (0 == m_Ref) {

delete this;

return 0;

}

return m_Ref;

}

{

if (NULL != pUnkOuter) {

return CLASS_E_NOAGGREGATION;

}

HRESULT hr = E_OUTOFMEMORY;

FallCloth* pObj = new FallCloth();

if (NULL == pObj) {

return hr;

}

hr = pObj->QueryInterface(riid, ppvObject);

if (S_OK != hr) {

delete pObj;

}

return hr;

}

HRESULT _stdcall CompFactory::LockServer(BOOL fLock)

{

return NOERROR;

}

HRESULT _stdcall CompFactory::CreateInstance(IUnknown *pUnkOuter, const IID &riid, void **ppvObject)//最重要的函数,这个函数创建CompTestClass对象,并协议函数代码实现如下://包含注册插件相关的协议函数#pragma once#include #include #include "MakeFallClothes.h"#include "factory.h"#include "factory.h"

HMODULE g_hModule; //dll进程实例句柄

ULONG g_num; //组件中CompTestClass对象的个数,用于判断是否可以卸载本组建,如值为0则可以卸载

#define API_OUT_C_OUT extern "C"//以C的方式导出

int MyReg(LPCWSTR lpPath)

{

HKEY thk, tclsidk;

//打开键HKEY_CLASSES_ROOTCLSID,在其下创建新键为CLSID_MakeFallClothes的CLSID

//在该键下创建InprocServer32,并将本组件(dll)所在的路径lpPath写为该键的默认值

if(ERROR_SUCCESS == RegOpenKey(HKEY_CLASSES_ROOT, "CLSID", &thk))

{

if(ERROR_SUCCESS == RegCreateKey(thk, "{9CA9DBE8-C0B1-42c9-B6C7-856BE5756855}",&tclsidk))

{

HKEY tinps32k;

if(ERROR_SUCCESS == RegCreateKey(tclsidk, "InprocServer32", &tinps32k))

{

if(ERROR_SUCCESS == RegSetValue(tinps32k, NULL, REG_SZ, (LPCSTR)lpPath, wcslen(lpPath) * 2))

{

std::cout<<"set lpPath success!"<

}

RegCloseKey(tinps32k);

}

RegCloseKey(tclsidk);

}

RegCloseKey(thk);

}

//在键HKEY_CLASSES_ROOT下创建新键为oth,

//在该键下创建子键,并将该组件dll对应的CLSID(CLSID_MakeFallClothes)写为该键的默认值

if (ERROR_SUCCESS == RegCreateKey(HKEY_CLASSES_ROOT, "oth", &thk))

{

if (ERROR_SUCCESS == RegCreateKey(thk, "CLSID", &tclsidk))

{

if (ERROR_SUCCESS == RegSetValue(tclsidk,

NULL,

REG_SZ,

"{9CA9DBE8-C0B1-42c9-B6C7-856BE5756855}",

wcslen(L"{9CA9DBE8-C0B1-42c9-B6C7-856BE5756855}") * 2))

{

std::cout<<"set CLSID success!"<

}

}

}

return 0;

}

//协议注册COM组件函数

API_OUT_C_OUT HRESULT _stdcall DllRegisterServer()

{

WCHAR szModule[1024];

//获取本组件(dll)所在路径

DWORD dwResult = GetModuleFileName(g_hModule, (LPSTR)szModule, 1024);

if (0 == dwResult)

{

return -1;

}

MyReg(szModule);//将路径等信息写入注册表

return 0;

}

int MyDelKey(HKEY hk, LPCWSTR lp)

{ if (ERROR_SUCCESS == RegDeleteKey(hk, (LPCSTR)lp))

{

}

return 0;

}

//删除注册时写入注册表的信息

int myDel()

{

HKEY thk;

if (ERROR_SUCCESS == RegOpenKey(HKEY_CLASSES_ROOT, "CLSID", &thk))

{

MyDelKey(thk, L"{9CA9DBE8-C0B1-42c9-B6C7-856BE5756855}InprocServer32");

MyDelKey(thk, L"{9CA9DBE8-C0B1-42c9-B6C7-856BE5756855}");

RegCloseKey(thk);

}

if (ERROR_SUCCESS == RegOpenKey(HKEY_CLASSES_ROOT, "oth", &thk))

{

MyDelKey(thk, L"CLSID");

}

MyDelKey(HKEY_CLASSES_ROOT, L"oth");

return 0;

}

API_OUT_C_OUT HRESULT _stdcall DllUnregisterServer()

{

myDel();//删除注册时写入注册表的信息

return 0;

}

//用于判断是否可以卸载本组建, 由CoFreeUnusedLibraries函数调用

API_OUT_C_OUT HRESULT _stdcall DllCanUnloadNow()

{

//如果对象个数为0,则可以卸载

if (0 == g_num)

{

return S_OK;

}

else

{

return S_FALSE;

}

}

API_OUT_C_OUT HRESULT _stdcall DllGetClassObject(__in REFCLSID rclsid, __in REFIID riid, LPVOID FAR* ppv)//用于创建类厂并返回所需接口,由CoGetClas

{

if (CLSID_FallClothes == rclsid)

{

CompFactory* pFactory = new CompFactory();//创建类厂对象

if (NULL == pFactory)

{

return E_OUTOFMEMORY;

}

HRESULT result = pFactory->QueryInterface(riid, ppv);//获取所需接口

return result;

}

else

{

return CLASS_E_CLASSNOTAVAILABLE;

}

}

BOOL APIENTRY DllMain( HMODULE hModule,

DWORD ul_reason_for_call,

LPVOID lpReserved

)

{

g_hModule = hModule;//获取进程实例句柄,用于获取本组件(dll)路径

switch (ul_reason_for_call)

{

case DLL_PROCESS_ATTACH:

case DLL_THREAD_ATTACH:

case DLL_THREAD_DETACH:

case DLL_PROCESS_DETACH:

break;

}

return TRUE;

}

声明DLL的引出函数:

LIBRARY "FallCloth"

EXPORTS

DllCanUnloadNow

DllGetClassObject

DllUnregisterServer

DllRegisterServer