com学习记录(一)——基本概念
com学习记录(二)——使用vs2019完成windows下的com组件编程
com学习记录(三)——在Linux环境下完成com组件编程
com学习记录(四)——在Linux环境下完成com组件编程分离服务端(动态库)客户端(可执行程序)
文件结构
同级目录下两个文件夹,一个lib文件夹用于服务端动态库生成,一个client文件夹用于客户端测试编写程序
Linux下没有unknwn.h系统头文件,所以在Linux环境编写com组件时应该自己定义这个类和接口
此项目分为两个文件夹,分别是服务端(生成动态库)和客户端(可执行文件)
**/com
************/lib
************************************base.h
************************************IUnknow.h
************************************interface.h
************************************component.h
************************************component.cpp
************************************Makefile
************/lib
************************************base.h
************************************IUnknow.h
************************************interface.h
************************************create.h
************************************create.cpp
************************************client.cpp
************************************Makefile
服务端(动态库)
自定义的IUnknown基类:IUnknow.h
/*
$file: IUnknow.h
$date: 2020年8月17日
$author: hehl
$describe: 自定义的IUnknown基类
$other:
*/
#ifndef __IUNKNOW_H__
#define __IUNKNOW_H__
#include "base.h"
class IUnknown
{
public:
virtual HRESULT QueryInterface(const IID& iid, void** ppv) = 0; // 第一个是强转,第二是一个指向指针数组的指针
virtual ULONG AddRef() = 0; // 增加引用计数
virtual ULONG Release() = 0; // 减少引用计数
};
#endif
基本数据类型:base.h
/*
$file: base.h
$date: 2020年8月17日
$author: hehl
$describe: 基本数据类型
$other:
*/
#ifndef __BASE_H__
#define __BASE_H__
typedef long HRESULT; // 32位机为四字节,64位机为八字节,和指针的类型长度一致
typedef unsigned long ULONG;
typedef struct _GUID
{
unsigned long Data1; // 32位 0x0000-0x0000
unsigned short Data2; // 16位 0x0000
unsigned short Data3; // 16位 0x0000
unsigned char Data4[8]; // 64位 0x0000-0x0000-0x0000-0x0000
}GUID; // guid 128位的全局唯一标识符(Globally Unique Identifier)
typedef GUID IID;
#define E_NOINTERFACE 0x80004002L // 错误码,先不管他
#define S_OK 0
#endif
接口:interface.h
/*
$file: interface.h
$date: 2020年8月17日
$author: hehl
$describe: 接口类
$other:
*/
#ifndef __INTERFACE_H__
#define __INTERFACE_H__
#include "IUnknow.h"
class IA : public IUnknown
{
public:
virtual void show_A() = 0;
};
const IID g_COM_A_IID = { 0x9F153BC9, 0xE06F , 0x7B23 , \
{0xF4, 0xFC, 0xA9, 0x6B, 0x64, 0xAB, 0xA9, 0xD3} };
#endif
组件类定义:component.h
/*
$file: component.h
$date: 2020年8月17日
$author: hehl
$describe: 组件类
$other:
*/
#ifndef __COMPONENT_H__
#define __COMPONENT_H__
#include <iostream>
#include "IUnknow.h"
#include "interface.h"
class com_A : public IA
{
public:
com_A();
~com_A();
private:
// IUnknown的接口
virtual HRESULT QueryInterface(const IID& iid, void** ppv);
virtual ULONG AddRef();
virtual ULONG Release();
// 组件的接口
virtual void show_A();
long m_cRef;
};
extern "C"
{
void test();
IUnknown* CreateInstance();
}
// 重载运算符,用于比较IID是否相等
bool operator==(const IID& guid1, const IID& guid2);
#endif
组件实现:component.cpp
/*
$file: component.cpp
$date: 2020年8月17日
$author: hehl
$describe: 组件实现
$other:
*/
#include "component.h"
using namespace std;
com_A::com_A()
{
cout << "com_A::com_A()" << endl;
}
com_A::~com_A()
{
cout << "com_A::~com_A()" << endl;
}
HRESULT com_A::QueryInterface(const IID& iid, void** ppv)
{
if (iid == g_COM_A_IID)
{
cout << "com_A::QueryInterface() to comA" << endl;
*ppv = static_cast<com_A*>(this); // 等价于 *ppv = (IX*)this;
}
else
{
cout << "com_A::QueryInterface() Interface is not support" << endl;
*ppv = NULL;
return E_NOINTERFACE;
}
reinterpret_cast<IUnknown*>(*ppv)->AddRef();
return S_OK;
}
ULONG com_A::AddRef()
{
m_cRef++;
cout << "com_A::AddRef() m_cRef:" + m_cRef << endl;
return m_cRef;
}
ULONG com_A::Release()
{
if (0 == (--m_cRef))
{
delete this;
return 0;
}
return m_cRef;
}
void com_A::show_A()
{
cout << "-----------------com_A::show_A()-----------------" << endl;
}
void test()
{
cout << "call test\n";
}
IUnknown* CreateInstance()
{
cout << "CreateInstance() in 1.2\n";
//IUnknown* ptr = static_cast<IA*>(new com_A);
IUnknown* ptr = new com_A();
if(ptr == NULL)
{
cout << "new error\n";
return NULL;
}
ptr->AddRef();
return ptr;
}
bool operator ==(const IID& guid1, const IID& guid2)
{
if (guid1.Data1 != guid2.Data1 || guid1.Data2 != guid2.Data2 || \
guid1.Data3 != guid2.Data3)
{
return false;
}
for (int i = 0; i < 8; i++)
{
if (guid1.Data4[i] != guid2.Data4[i])
{
return false;
}
}
return true;
}
生成动态库的Makefile:
all:
g++ -shared -fPIC -Xlinker --unresolved-symbols=ignore-in-shared-libs component.cpp -o libcom.so
-shared: 生成动态库
-fPIC: 加载时根据加载到的位置再次重定位
-Xlinker --unresolved-symbols=ignore-in-shared-libs: 检查函数未定义
在生成的.so目录下执行 ldd -r libcom.so 命令查看生成的so是否存在符号未定义的内容
客户端(可执行程序测试)
基本数据类型:base.h(同服务端)
/*
$file: base.h
$date: 2020年8月17日
$author: hehl
$describe: 基本数据类型
$other:
*/
#ifndef __BASE_H__
#define __BASE_H__
typedef long HRESULT; // 32位机为四字节,64位机为八字节,和指针的类型长度一致
typedef unsigned long ULONG;
typedef struct _GUID
{
unsigned long Data1; // 32位 0x0000-0x0000
unsigned short Data2; // 16位 0x0000
unsigned short Data3; // 16位 0x0000
unsigned char Data4[8]; // 64位 0x0000-0x0000-0x0000-0x0000
}GUID; // guid 128位的全局唯一标识符(Globally Unique Identifier)
typedef GUID IID;
#define E_NOINTERFACE 0x80004002L // 错误码,先不管他
#define S_OK 0
#endif
自定义的IUnknown基类:IUnknow.h(同服务端)
/*
$file: IUnknow.h
$date: 2020年8月17日
$author: hehl
$describe: 自定义的IUnknown基类
$other:
*/
#ifndef __IUNKNOW_H__
#define __IUNKNOW_H__
#include "base.h"
class IUnknown
{
public:
virtual HRESULT QueryInterface(const IID& iid, void** ppv) = 0; // 第一个是强转,第二是一个指向指针数组的指针
virtual ULONG AddRef() = 0; // 增加引用计数
virtual ULONG Release() = 0; // 减少引用计数
};
#endif
接口类:interface.h(同服务端,开发时需要提供此接口文件,并且保证一致性)
/*
$file: interface.h
$date: 2020年8月17日
$author: hehl
$describe: 接口类
$other:
*/
#ifndef __INTERFACE_H__
#define __INTERFACE_H__
#include "IUnknow.h"
class IA : public IUnknown
{
public:
virtual void show_A() = 0;
};
const IID g_COM_A_IID = { 0x9F153BC9, 0xE06F , 0x7B23 , \
{0xF4, 0xFC, 0xA9, 0x6B, 0x64, 0xAB, 0xA9, 0xD3} };
#endif
创建组件接口类:create.h
/*
$file: create.h
$date: 2020年8月17日
$author: hehl
$describe: 创建组件接口类
$other:
*/
#ifndef __CREATE_H__
#define __CREATE_H__
#include <iostream>
#include <dlfcn.h>
#include <string>
#include "IUnknow.h"
using namespace std;
class CREATE
{
public:
void* handle; // 动态库句柄
IUnknown* CreateIunknownkInstance(); // 创建组件的接口,在动态库中调用函数返回对象指针
void closeHandle(); // 关闭动态库句柄
};
#endif
创建组件接口实现:create.cpp
/*
$file: create.cpp
$date: 2020年8月17日
$author: hehl
$describe: 创建组件接口实现
$other:
*/
#include "create.h"
// #define TEST_CALL_LIB
IUnknown* CREATE::CreateIunknownkInstance()
{
#ifdef TEST_CALL_LIB
void (*pfunTest)();
#endif
IUnknown* (*pfunIUnknown)();
IUnknown* pIUnknown;
char *error;
handle = dlopen("../lib/libcom.so", RTLD_LAZY); // 打开动态库,路径根据动态库实际位置添加
if(handle == NULL)
{
cout << "dlopen error" << endl;
return NULL;
}
cout << "dlopen success" << endl;
#ifdef TEST_CALL_LIB
pfunTest = (void(*)())dlsym(handle, "test");
if((error = dlerror()) != NULL)
{
cout << "dlsym error:" << error << endl;
dlclose(handle);
return NULL;
}
pfunTest();
#endif
pfunIUnknown = (IUnknown*(*)())dlsym(handle, "CreateInstance");
if((error = dlerror()) != NULL)
{
cout << "dlsym2 error:" << error << endl;
dlclose(handle);
return NULL;
}
pIUnknown = pfunIUnknown();
if(NULL == pIUnknown)
{
cout << "error" << endl;
return NULL;
}
return pIUnknown;
}
void CREATE::closeHandle()
{
cout << "CREATE::closeHandle()" << endl;
dlclose(handle);
}
客户端主函数:client.cpp
/*
$file: client.cpp
$date: 2020年8月17日
$author: hehl
$describe: 客户端主函数
$other:
*/
#include <iostream>
#include "base.h"
#include "create.h"
#include "IUnknow.h"
#include "interface.h"
int main(int argc, char const *argv[])
{
HRESULT hr;
CREATE create;
IUnknown* pUnknown = create.CreateIunknownkInstance();
if(pUnknown == NULL)
{
cout << "CreateIunknownkInstance error" << endl;
return -1;
}
IA* pCOM_A = NULL;
hr = pUnknown->QueryInterface(g_COM_A_IID, (void**)&pCOM_A);
if (hr < 0)
{
cout << "Counld not get the IX interface." << endl;
}
else
{
cout << "Success get the IX interface." << endl;
pCOM_A->show_A();
pCOM_A->Release();
}
pUnknown->Release();
create.closeHandle();
return 0;
}
生成可执行程序client的Makefile
all:
g++ client.cpp create.cpp -ldl -o client
-ldl 链接动态库函数<dlfcn.h>
测试
运行成功帮忙点个赞,出问题的留言一起探讨~