COM组件笔记(一)
COM是什么:
COM是微软组件对象模型的简称。由于COM具有二进制代码共享的特性,所以它具备了高可开发性、高度可维护性和高度的可移植性(跨开发语言),以至于在Windows上面的诸多应用软件采用了COM来做整体的架构。比如微软的DirectX等。COM虽然流行于2000-2004年之间,由于它的普及面之广,应用软件种类之繁多再加上Windows对其默认支持很好,开发出来的软件无需依赖其他的开发包,所以被很多软件公司采用至今。
COM组件的三个优点。
①采用COM组件编写软件,可以很方便的进行模块划分,各个模块的独立性高,耦合度低,从而可以更方便的进行开发任务的分工。
②COM组件可以让程序员更方便的维护、升级软件,旧模块能够直接被新模块代替,而不影响软件的使用。
③COM组件具有跨平台移植性,如C++的MFC平台移植到C#的WinForm平台。(第一份工作中就是用到了C++开发的COM接口递交给C#调用)COM组件同时可以跨应用,所以C++调用可以被C#调用。
④ COM组件适用于Windows平台,而Linux不适用于COM组件
以下是基于组件的小Demo1,可以快速的了解组件是什么东西:

1 #define _CRT_SECURE_NO_WARNINGS 2 #include <iostream> 3 using namespace std; 4 5 // 预定义interface 6 #define interface struct 7 8 // 接口 IX 9 interface IX { 10 virtual void Fx1() = 0; 11 virtual void Fx2() = 0; 12 }; 13 14 // 接口 IY 15 interface IY { 16 virtual void Fy1() = 0; 17 virtual void Fy2() = 0; 18 }; 19 20 class CA : public IX, public IY 21 { 22 public: 23 virtual void Fx1() { 24 cout << "Fx1" << endl; 25 } 26 27 virtual void Fx2() { 28 cout << "Fx2" << endl; 29 } 30 31 virtual void Fy1() { 32 cout << "Fy1" << endl; 33 } 34 35 virtual void Fy2() { 36 cout << "Fy2" << endl; 37 } 38 39 }; 40 41 42 43 int main(int argc, char* argv[]) 44 { 45 // 创建组件的实例 46 CA* pA = new CA(); 47 48 // 获取IX的接口 49 IX* pIX = pA; 50 51 // 使用IX的接口 52 pIX->Fx1(); 53 pIX->Fx2(); 54 55 // 获取IY的接口 56 IY* pIY = pA; 57 pIY->Fy1(); 58 pIY->Fy2(); 59 60 getchar(); 61 return 0; 62 }
COM组件与COM接口函数原型:
1 interface IUnknown 2 { 3 virtual HRESULT QueryInterface(const IID& iid, void **pv) = 0; 4 virtual ULONG AddRef() = 0; 5 virtual ULONG Release() = 0; 6 }
① QueryInterface函数,可以通过它查询某个组件是否支持某个特定的接口。例如Demo1中,我们就可以通过这个函数查询CA这个组件类是否支持FX1这种接口,或者我们需要调用类似于FZ1这种函数,也可以查询是否有FZ1这种接口。如果支持,该函数会返回指向这个函数的指针,如果不支持,这个函数会返回null指针。
返回值 HRESULT :表示该函数是否正确执行,并不代表找不到这个接口。以下是HRESULT的返回值类型。
const IID& iid:interface ID 代表接口标识符。每个接口都可以设置其相应的接口标识符,本质上IID 是一个 GUID,以下是GUID的结构内部变量:
1 typedef struct _GUID 2 { 3 DWORD Data1; // 随机数 4 WORD Data2; // 和时间相关 5 WORD Data3; // 和时间相关 6 BYTE Data4[8]; // 和网卡MAC相关 7 }GUID;
// GUID 示例 // 8位 - 4位 - 4位 - 4位 - 12位 {234564DF-58SW-WE8F-QW8D-AASAD8769851} // 另一种展现形式 stactic const GUID guid = { aswertfsz, oaser452, 458sdwq,{58sa, 58s, oxas, oxe9, kjf7, ox2, ox5em oxb9}}
GUID一共16个字节,128位,VS可以通过以下方式创建GUID, GUID是唯一的。
void **pv:返回函数结果的参数变量。
以下是基于GUID的Demo2:

1 #define _CRT_SECURE_NO_WARNINGS 2 #include <iostream> 3 #include <Unknwn.h> 4 using namespace std; 5 6 // {4E22FE4C-FC61-4616-B13E-7AC19186000D} 7 static const IID IID_IX = 8 { 0x4e22fe4c, 0xfc61, 0x4616, { 0xb1, 0x3e, 0x7a, 0xc1, 0x91, 0x86, 0x0, 0xd } }; 9 10 // {26C7D646-9C25-4C88-AAC5-215FC0569CD5} 11 static const IID IID_IY = 12 { 0x26c7d646, 0x9c25, 0x4c88, { 0xaa, 0xc5, 0x21, 0x5f, 0xc0, 0x56, 0x9c, 0xd5 } }; 13 14 15 // 接口IX 16 interface IX : public IUnknown 17 { 18 virtual void Fx1() = 0; 19 virtual void Fx2() = 0; 20 }; 21 22 // 接口IY 23 interface IY : public IUnknown 24 { 25 virtual void Fy1() = 0; 26 virtual void Fy2() = 0; 27 }; 28 29 // 组件CA 30 class CA : public IX, public IY { 31 public: 32 virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID iid, void** ppv) { 33 if (iid == IID_IUnknown) { 34 // 如果未知的话,我们一般来说返回先继承的那个接口 35 *ppv = static_cast<IX*>(this); 36 } 37 else if (iid == IID_IX) { 38 // 返回IX接口 39 *ppv = static_cast<IX*>(this); 40 } 41 else if (iid == IID_IY) { 42 // 返回IY接口 43 *ppv = static_cast<IY*>(this); 44 } 45 else { 46 // 查询不到IID,*ppv返回NULL 47 *ppv = NULL; 48 return E_NOINTERFACE; // 函数返回值返回E_NOINTERFACE,表示调用的组件不支持iid的接口 49 } 50 51 AddRef(); //之后会提到 52 53 return S_OK; // 代表函数成功执行 54 } 55 virtual ULONG STDMETHODCALLTYPE AddRef() { 56 // 暂时不实现 57 return 0; 58 }; 59 virtual ULONG STDMETHODCALLTYPE Release() { 60 // 暂时不实现 61 return 0; 62 }; 63 64 public: 65 virtual void Fx1() { 66 cout << "Fx1" << endl; 67 } 68 69 virtual void Fx2() { 70 cout << "Fx2" << endl; 71 } 72 73 virtual void Fy1() { 74 cout << "Fy1" << endl; 75 } 76 77 virtual void Fy2() { 78 cout << "Fy2" << endl; 79 } 80 81 // 数据 82 public: 83 long m_IAA; 84 long m_IAB; 85 long m_IAC; 86 87 }; 88 89 int main(int argc, char* argv[]) 90 { 91 HRESULT hr; 92 93 // 创建组件 94 CA* pA = new CA(); 95 96 // 从组件查询IUnknown接口 97 IUnknown* pIUnknown = NULL; 98 hr = pA->QueryInterface(IID_IUnknown, (void**)&pIUnknown); 99 if (SUCCEEDED(hr)) { // 对于HRESULT返回值的判断,一般采用SUCCEEDED 100 // 从IUnknown查询IX接口 101 IX* pIX = NULL; 102 hr = pIUnknown->QueryInterface(IID_IX, (void**)&pIX); 103 if (SUCCEEDED(hr)) { 104 // 调用IX接口方法 105 pIX->Fx1(); 106 pIX->Fx2(); 107 } 108 109 IY* pIY = NULL; 110 hr = pIUnknown->QueryInterface(IID_IY, (void**)&pIY); 111 if (SUCCEEDED(hr)) { 112 pIY->Fy1(); 113 pIY->Fy2(); 114 } 115 116 if ((void*)pIX != (void*)pIY) { 117 cout << "pIX != pIY" << endl; 118 } 119 120 if ((void*)pIUnknown != (void*)pIY) { 121 cout << "pIUnknown != pIY" << endl; 122 } 123 124 if ((void*)pIUnknown != (void*)pIX) { 125 cout << "pIUnknown != pIX" << endl; 126 } 127 128 // 从IX查询IY 129 IY* pIY2 = NULL; 130 hr = pIX->QueryInterface(IID_IY, (void**)&pIY2); 131 if (SUCCEEDED(hr)) { 132 pIY2->Fy1(); 133 pIY2->Fy2(); 134 } 135 136 /* 137 * 从IX也可以查询到IUnknown 138 * 从IY也可以查询到IX 139 * 从IY也可以呃查询到IUnknowN 140 * 从CA也可以查询到IX 141 * 从CA也可以查询到IY 142 * 总结: 143 * 只要是CA所继承的接口,从CA的组件里都可以查询到 144 * 只要是CA所继承的接口,从CA所继承的其他接口里也都可以查询到 145 */ 146 } 147 148 delete pA; 149 150 getchar(); 151 return 0; 152 }
取自于B站视频 https://www.bilibili.com/video/BV1R7411x7U9?p=17&share_source=copy_web 《COM实用入门教程》第一讲, 感谢,收获颇丰。
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· 没有源码,如何修改代码逻辑?
· PowerShell开发游戏 · 打蜜蜂
· 在鹅厂做java开发是什么体验
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战