com是为了让所有的语言都可以设计组件,不同的组件可以互相调用,所以需要一个统一接口去查询组件的接口有哪些,怎么区分
不同的组件的接口,通过一个CUID(GUID 全球唯一标识符)去查询,这样就不会有冲突,这是由实现组件的作者提供
接口不能放数据成员,方便以后扩展更新 统一返回值 统一错误方式,方便调式,用一下windows的错误处理方式
-
COM组件优点
- 采用统一规范,支持跨语言使用,所有的语言都可以开发dll,不同的dll可以互相调用,一般用c++写,因为c++效率高,如果网页之类的调用c++的组件,那效率不就提高了
- 团队开发效率更高
- COM组件灵活度高,可以动态的插入或卸出应用
//模拟COM设计框架接口要统一接口规范
1接口不能放数据成员,方便以后扩展更新
2统一字符串,统一类型, 统一返回值 统一错误方式,方便调式,我统一用一下windows的错误处理
虚函数设计统一:
/*
采用的是虚表的方式去实现多态接口,相同的业务逻辑处理不同的功能
不同的c++编译器对虚表的函数数量,顺序,数量编译后是不一样的,所以要统一虚函数的接口设计,
1.虚表函数数量:不要有虚析构
2.虚表函数顺序 : 不要使用虚函数重载
3.虚表数量:不能用多重继承(两个基类都有虚函数), 不能使用虚继承
*/
统一字符串,类型
/*
跨语言使用dll
1不同的语言对字符串结尾处理方式不同,c/c++是\0结尾,其他的语言是len + 字符串 所以微软扩展了一种新的字符串语法来支持所有的语言识别 len + 字符串 + \0, 这样的字符串,所有的语言都可以使用BSTR类型字符串
BSTR使用语法
BSTR bstrData = SysAllocString(OLESTR("hello"));
SysFreeString(bstrData);
不同编译器对int的处理也不一样,所以用long
*/
COM代码模拟如下
1设计一个框架接口Unkown.h
#pragma once
#include <windows.h>
//CUID c/c++共用
// {4A3F0CF6-C86D-4cf4-B315-7D61C4E2747F}
static const GUID IID_IMyUnkown =
{ 0x4a3f0cf6, 0xc86d, 0x4cf4, { 0xb3, 0x15, 0x7d, 0x61, 0xc4, 0xe2, 0x74, 0x7f } };
// {9D681815-B8B0-435B-9351-EB0450BB2C0C}
static const GUID IID_IMyClassFactory =
{ 0x9d681815, 0xb8b0, 0x435b, { 0x93, 0x51, 0xeb, 0x4, 0x50, 0xbb, 0x2c, 0xc } };
// {1A65A7FD-DEBE-4D1D-B1BA-4A5D8E81B736}
static const GUID CLSID_CSuperMath =
{ 0x1a65a7fd, 0xdebe, 0x4d1d, { 0xb1, 0xba, 0x4a, 0x5d, 0x8e, 0x81, 0xb7, 0x36 } };
// {CFBD4808-EF37-40f6-8BF5-8B4FD744B19A}
static const GUID IID_ISuperMath =
{ 0xcfbd4808, 0xef37, 0x40f6, { 0x8b, 0xf5, 0x8b, 0x4f, 0xd7, 0x44, 0xb1, 0x9a } };
#ifdef __cplusplus
//模拟COM设计框架的接口
/*
采用的是虚表的方式去实现多态接口,相同的业务逻辑处理不同的功能
不同的c++编译器对虚表的函数数量,顺序,数量编译后是不一样的,所以要统一虚函数的接口设计,
1.虚表函数数量:不要有虚析构
2.虚表函数顺序 : 不要使用虚函数重载
3.虚表数量:不能用多重继承(两个基类都有虚函数), 不能使用虚继承
*/
/*
跨语言使用dll
1不同的语言对字符串结尾处理方式不同,c/c++是\0结尾,其他的语言是len + 字符串 所以微软扩展了一种新的字符串语法来支持所有的语言识别 len + 字符串 + \0, 这样的字符串,所有的语言都可以使用BSTR类型字符串
BSTR使用语法
BSTR bstrData = SysAllocString(OLESTR("hello"));
SysFreeString(bstrData);
不同编译器对int的处理也不一样,所以用long
2不同语言的接口声明不一样, 所有就有一种接口描述语言(IDL)来写接口声明,然后用IDL编译器(微软有一个叫MIDL,在vs工具集里面用x64去编译)去编译这个接口文件,生成一个所有语言都能识别的二进制接口声明
语法实例
语法格式一般为[属性]HRESULT name(参数列表)
*/
/*
com是为了让所有的语言都可以设计组件,不同的组件可以互相调用,所以需要一个统一接口去查询组件的接口有哪些,怎么区分
不同的组件的接口,通过一个CUID(GUID 全球唯一标识符)去查询,这样就不会有冲突,这是由实现组件的作者提供
接口不能放数据成员,方便以后扩展更新 统一返回值 统一错误方式,方便调式,用一下windows的错误处理方式
*/
//提供一个导出对象的接口
HRESULT __stdcall DllGetClassObject(const GUID & clsid, const GUID & iid, void** ppObject);
typedef HRESULT(__stdcall *MY_GET_CLASS_OBJECT)(const GUID & clsid, const GUID & iid, void** ppObject);
class IMyUnkown
{
public:
//GUID 全球唯一标识符
virtual HRESULT __stdcall QueryInterface(const GUID& iid, void** ppv) = 0;
virtual HRESULT __stdcall AddRef() = 0;
virtual HRESULT __stdcall Release() = 0;
};
//接口模板
//定义个模板类去实现通用的接口,所有的接口实现都需要继承IMyUnkown实现,所以写个模板去实现这些接口,然后继承模板
//减少代码重复写
template<typename class_name,typename class_base>
class ImpIUnknow :public class_base
{
public:
ImpIUnknow(const GUID& iid) :m_iid(iid)
{
}
virtual HRESULT __stdcall QueryInterface(const GUID& iid, void** ppv)
{
if (memcmp(&iid, &IID_IMyUnkown, sizeof(GUID)) == 0)
{
*ppv = (IMyUnkown*)this;
AddRef();
}
else if (memcmp(&iid, &m_iid, sizeof(GUID)) == 0)
{
*ppv = (class_name*)this;
AddRef();
}
else
{
return E_NOINTERFACE;
}
return S_OK;
}
//多线程使用的话需要同步
virtual HRESULT __stdcall AddRef()
{
InterlockedIncrement((long*)&m_RefCount);
return S_OK;
}
virtual HRESULT __stdcall Release()
{
if (InterlockedDecrement((long*)&m_RefCount) == 0)
{
delete this;
}
return S_OK;
}
public:
static int m_RefCount;
const GUID& m_iid;
};
//初始化静态变量
template<typename class_name, typename class_base>
int ImpIUnknow<class_name, class_base>::m_RefCount = 0;
//接口声明,实现接口只要继承模板然后把这个类写到基类参数也就是继承了这个类,直接写自己的实现就好了
class ISuperMath :public IMyUnkown
{
public:
virtual HRESULT __stdcall Add(long n1, long n2, long* result) = 0;
virtual HRESULT __stdcall Sub(long n1, long n2, long* result) = 0;
};
//类工厂设计去创建对象
class IMyClassFactory :public IMyUnkown
{
public:
virtual HRESULT __stdcall CreateObject(const GUID & iid, void**ppObject) = 0;
};
//类工厂模板实现,负责创建对象和创建类工厂
template<typename class_name, typename class_base>
class CMyFactory :public ImpIUnknow<class_name, class_base>
{
public:
CMyFactory()
:ImpIUnknow(IID_IMyClassFactory)
{
}
//实现创建对象的接口
virtual HRESULT __stdcall CreateObject(const GUID & iid, void**ppObject)
{
static class_name *pObject = NULL;
if (pObject == NULL)
{
pObject = new class_name;
}
HRESULT hr = pObject->QueryInterface(iid, ppObject);
if (FAILED(hr) && class_name::m_RefCount == 0)
{
delete pObject;
pObject = NULL;
}
return hr;
}
//提供一个静态函数创建类工厂,这样就可以在外部使用
static HRESULT Create(const GUID & clsid, const GUID & iid, void** ppObject)
{
static IMyClassFactory *pObject = NULL;
if (pObject == NULL)
{
pObject = new CMyFactory();
}
HRESULT hr = pObject->QueryInterface(iid, ppObject);
if (FAILED(hr) && m_RefCount == 0)
{
delete pObject;
pObject = NULL;
}
return hr;
}
public:
static int m_RefCount;
};
template<typename class_name, typename class_base>
int CMyFactory<class_name, class_base>::m_RefCount = 0;
//只要知道虚表的地址,c就可以调用c++的虚表中的函数 为了让c调用时语法更方便,模拟虚表结构,使用起来语法就很方便可读了
#else
typedef HRESULT(__stdcall *MY_GET_CLASS_OBJECT)(const GUID* clsid, const GUID* iid, void** ppObject);
//类工厂的虚表中存放的是函数地址,所以定义函数指针保存函数地址
typedef struct _IMyClassFactoryVtable
{
HRESULT(__stdcall *QueryInterface)(void* This, const GUID* iid, void** ppv);
HRESULT(__stdcall *AddRefe)(void* This);
HRESULT(__stdcall *Releasee)(void* This);
HRESULT(__stdcall *CreateObjecte)(void* This, const GUID* iid, void**ppObject);
}IMyClassFactoryVtable;
//定义类工厂的虚表,用虚表指针保存虚表地址
typedef struct _IMyClassFactory
{
IMyClassFactoryVtable *vtable;
}IMyClassFactory;
//模拟接口的虚表函数
typedef struct _ISuperMathVtable
{
HRESULT(__stdcall *QueryInterface)(void* This, const GUID* iid, void** ppv);
HRESULT(__stdcall *AddRefe)(void* This);
HRESULT(__stdcall *Releasee)(void* This);
HRESULT(__stdcall *Add)(void* This, int n1, int n2, int* result);
HRESULT(__stdcall *Sub)(void* This, int n1, int n2, int* result);
}ISuperMathVtable;
//模拟接口的虚表
typedef struct _ISuperMath
{
ISuperMathVtable *vtable;
}ISuperMath;
#endif
2包含Unkown.h,组件作者编译自己的实现接口dll
SuperMath.h
#pragma once
#include "Unkown.h"
class CSuperMath :public ImpIUnknow<CSuperMath, ISuperMath>
{
public:
CSuperMath();
~CSuperMath();
virtual HRESULT __stdcall Add(long n1, long n2, long* result);
virtual HRESULT __stdcall Sub(long n1, long n2, long* result);
};
SuperMath.cpp
#include "stdafx.h"
#include "SuperMath.h"
//初始化GUID,查询直接查找IID_ISuperMath
CSuperMath::CSuperMath() :ImpIUnknow<CSuperMath, ISuperMath>(IID_ISuperMath)
{
}
CSuperMath::~CSuperMath()
{
}
HRESULT __stdcall CSuperMath::Add(long n1, long n2, long* result)
{
*result = n1 + n2;
return S_OK;
}
HRESULT __stdcall CSuperMath::Sub(long n1, long n2, long* result)
{
*result = n1 - n2;
return S_OK;
}
dllmain.cpp 注册dll时用管理员权限
// dllmain.cpp : 定义 DLL 应用程序的入口点。
#include "stdafx.h"
#include "Unkown.h"
#include "SuperMath.h"
//实现导出函数
//创建类工厂结构体
struct MY_FACTORY
{
const GUID *clsid;
HRESULT(*PFN_CREATE)(const GUID & clsid, const GUID & iid, void** ppObject);
};
//定义一个宏去添加类工厂
#define IMP_FACTORY(class_name) &CLSID_##class_name, CMyFactory<class_name, IMyClassFactory>::Create,
MY_FACTORY g_factorys[] = {
&CLSID_CSuperMath, CMyFactory<CSuperMath, IMyClassFactory>::Create,
//IMP_FACTORY(CSuperMath)
};
//导出一个类工厂对象
HRESULT __stdcall DllGetClassObject(const GUID & clsid, const GUID & iid, void** ppObject)
{
for (int i = 0; i < sizeof(g_factorys) / sizeof(g_factorys[0]); i++)
{
if (memcmp(&clsid, g_factorys[i].clsid, sizeof(GUID)) == 0)
{
return g_factorys[i].PFN_CREATE(clsid, iid, ppObject);
break;
}
}
return E_NOINTERFACE;
}
//组件目录要用户自己选择,所以要把组件目录写进注册表,windows有个regsvr32程序就是处理dll的注册表问题
//regsvr32调用4个导出函数来进行处理,记得要用管理员权限进行注册
//注册表字符串数组
const char* g_Regs[][3] = {
"CLSID\\{1A65A7FD-DEBE-4D1D-B1BA-4A5D8E81B736}", NULL, "CSuperMath",
"CLSID\\{1A65A7FD-DEBE-4D1D-B1BA-4A5D8E81B736}\\InprocServer32", NULL, NULL,
};
//导出函数属性设置为PRIVATE,不会把接口的符号写入到lib文件中,也就是不能隐式导入动态库(#pragma comment(lib,"xxx.lib"))
HINSTANCE g_hMoudle;
//写注册表
STDAPI DllRegisterServer()
{
//获取dll路径
char Path[MAX_PATH];
GetModuleFileName(g_hMoudle, Path, sizeof(Path));
//写注册表
for (int i = 0; i < sizeof(g_Regs) / sizeof(g_Regs[0]); i++)
{
const char* SubKey = g_Regs[i][0];//注册表子健
const char* ValueName = g_Regs[i][1];//注册表子健项的名字
const char* Value = g_Regs[i][2];注册表子健项的值
if (Value == NULL)//路径写进注册表
{
Value = Path;
}
RegSetKeyValue(HKEY_CLASSES_ROOT, SubKey, ValueName, REG_SZ, Value, strlen(Value));
}
return S_OK;
}
//删除注册表,删除时先把子目录删除后才能删除主目录
STDAPI DllUnregisterServer()
{
int count = sizeof(g_Regs) / sizeof(g_Regs[0]);
for (int i = count - 1; i >= 0; i--)
{
const char* SubKey = g_Regs[i][0];
RegDeleteKey(HKEY_CLASSES_ROOT, SubKey);
}
return S_OK;
}
//是否可以删除注册表,没有人引用dll的时候删除
STDAPI DllCanUnloadNow()
{
if (CMyFactory<CSuperMath, IMyClassFactory>::m_RefCount <= 0)
{
DllUnregisterServer();
}
return S_OK;
}
BOOL WINAPI DllMain(HINSTANCE hinstDLL,
DWORD fdwReason,
LPVOID lpvReserved)
{
g_hMoudle = hinstDLL;
return TRUE;
}
Source.def,导出为私有的后只能动态加载dll
LIBRARY
EXPORTS
DllGetClassObject PRIVATE
DllRegisterServer private
DllUnregisterServer private
DllCanUnloadNow private
3使用者调用组件dll
UseCom.cpp
#include <windows.h>
#include "../COM/Unkown.h"
//格式化CUID为字符串好查询注册表
void GUID2String(const GUID& iid, char* buffer)
{
//1A65A7FD - DEBE - 4D1D - B1BA - 4A 5D 8E 81 B7 36
wsprintf(buffer, "%08X-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X",
iid.Data1,
iid.Data2,
iid.Data3,
iid.Data4[0],
iid.Data4[1],
iid.Data4[2],
iid.Data4[3],
iid.Data4[4],
iid.Data4[5],
iid.Data4[6],
iid.Data4[7]);
}
//CoGetClassObject 获取类对象
HRESULT __stdcall MyGetClassObject(const GUID& clsid, const GUID& iid, void** ppObject)
{
//1.查注册表
char Guid[60];
GUID2String(clsid, Guid);
//拼接注册表子健目录字符串
char Subkey[260];
wsprintf(Subkey, "CLSID\\{%s}\\InprocServer32", Guid);
//从注册表查询到dll路径
char Path[MAX_PATH];
LONG size;
RegQueryValue(HKEY_CLASSES_ROOT, Subkey, Path, &size);
//2.加载dll
HRESULT hr;
HMODULE hDll = LoadLibrary(Path);
if (hDll == NULL)
return S_FALSE;
MY_GET_CLASS_OBJECT pfnGetClassObject = (MY_GET_CLASS_OBJECT)GetProcAddress(hDll, "DllGetClassObject");
if (pfnGetClassObject == NULL)
{
return S_FALSE;
}
hr = pfnGetClassObject(clsid, iid, ppObject);
if (FAILED(hr))
{
return hr;
}
return hr;
}
//CoCreateInstance 实例化对象
HRESULT __stdcall MyCreateInstan(const GUID& clsid, const GUID& iid, void** ppObject)
{
//创建类工厂对象
IMyClassFactory *pClassFactroy = NULL;
HRESULT hr = MyGetClassObject(clsid, IID_IMyClassFactory, (void**)&pClassFactroy);
if (FAILED(hr))
{
return hr;
}
//用类工厂创建对象
hr = pClassFactroy->CreateObject(iid, ppObject);
if (FAILED(hr))
{
return hr;
}
pClassFactroy->Release();
}
//智能指针
template<typename TYPE>
class MyPtr
{
public:
MyPtr(TYPE *pObject = nullptr)
{
m_pObject = pObject;
}
~MyPtr()
{
if (m_pObject != nullptr)
{
m_pObject->Release();
}
}
operator int() //转换运算符,对象如果赋值给int就会调用这个函数
{
return (int)m_pObject;
}
MyPtr& operator= (TYPE* pObject)
{
if (m_pObject != nullptr)
{
m_pObject->Release();
}
m_pObject = pObject;
return *this;
}
MyPtr& operator= (MyPtr& obj)
{
if (m_pObject != nullptr)
{
m_pObject->Release();
}
m_pObject = obj.m_pObject;
return *this;
}
TYPE * operator-> ()
{
return m_pObject;
}
private:
TYPE *m_pObject;
};
#if 0
//c语言调用
int main()
{
HRESULT hr;
HMODULE hDll = LoadLibrary("../DEbug/COM.dll");
if (hDll == NULL)
{
return 0;
}
MY_GET_CLASS_OBJECT pfnGetClassObject = (MY_GET_CLASS_OBJECT)GetProcAddress(hDll, "MyGetClassObject");
if (pfnGetClassObject == NULL)
{
return 0;
}
//获取虚表地址
IMyClassFactory *pClassFactroy = NULL;
hr = pfnGetClassObject(CLSID_CSuperMath, IID_IMyClassFactory, (void**)&pClassFactroy);
if (FAILED(hr))
{
return 0;
}
//调用虚函数
ISuperMath *pSuperMath = NULL;
hr = pClassFactroy->CreateObject(IID_ISuperMath, (void**)&pSuperMath1);
if (FAILED(hr))
{
return 0;
}
int result = 0;
pSuperMath->Add(1, 2, &result);
//释放对象
pSuperMath->Release();
pClassFactroy->Release();
}
#endif
//c++调用使用
int main()
{
MyPtr<ISuperMath> pSuperMath = NULL;
HRESULT hr = MyCreateInstan(CLSID_CSuperMath, IID_ISuperMath, (void**)&pSuperMath);
if (FAILED(hr))
{
return 0;
}
long result = 0;
pSuperMath->Add(1, 2, &result);
return 0;
}