本文是在《C++ COM组件的编写》的基础上做了一些细节上的增添,可以让像我这样的资质驽钝者可以减少一点学习障碍。
新建一个dll项目
新建项目分两步即可
1.创建新项目,选择动态链接库(dll)
2. 填写com组件名称,修改项目路径,最后创建
创建后文件组织如图所示, framework.h和pch.h,pch.cpp不用管,保持原样即可,dllmain.cpp后面需要进行修改,或者说屏蔽。
添加代码
需要添加的代码参考文献里已经有了,后面我会再贴一下,因为参考文献里面的时间有点久远,已经无法直接复制了,而且个别代码有些问题需要修正,但主体没有问题。
头文件需要添加三个,分别为ICompTest.h,定义接口,CompTestClass.h实现接口,factory.h定义工厂类
源文件需要添加三个,factory.cpp实现工厂类,CompTestClass.cpp实现接口,CompTest.cpp实现的是注册,反注册,获取对象什么的。
在工程所在目录创建一个文件,命名为mydef.def,内容会贴在后面
创建好文件后要添加到项目中,添加路径如下:右键项目->属性->链接器->输入->模块定义文件,手动输入
编译运行
编译时会报错DllMain函数重定义,这个时候需要将dllMain.cpp里面的这个函数定义注释掉就可以了
注册
注册命令:regsvr32.exe ComTest2.dll
反注册命令:regsvr32.exe ComTest2.dll -u
管理员启动cmd,切换到库所在路径,运行注册命令,成功后注册表如图所示:
测试
新建一控制台程序,输入如下代码作为客户端:
#include <iostream>
#include "ICompTest.h"
using namespace std;
int main()
{
CoInitialize(NULL); //初始化COM库,使用默认的内存分配器
IUnknown* pUnknown = NULL;
GUID CLSID_CompTestClass;
HRESULT hResult = CLSIDFromProgID(L"COMCTL.CompTest", &CLSID_CompTestClass); //获取ProgID为COMCTL.CompTest组建的CLSID
if (S_OK != hResult) {
printf("Can't find CLSID!\n");
return -1;
}
else {
LPOLESTR szCLSID;
StringFromCLSID(CLSID_CompTestClass, &szCLSID); //将其转化为字符串形式用来输出
wprintf(L"find CLSID \"%s\"\n", szCLSID);
CoTaskMemFree(szCLSID); //调用COM库的内存释放
}
//用此CLSID创建一个COM对象并获取IUnknown接口
hResult = CoCreateInstance(CLSID_CompTestClass, NULL, CLSCTX_INPROC_SERVER, IID_IUnknown, (void**)&pUnknown);
if (S_OK != hResult || NULL == pUnknown) {
printf("Create Object Failed!\n");
return -1;
}
ICompTest* pCompTest = NULL;
hResult = pUnknown->QueryInterface(IID_ICompTest, (void**)&pCompTest);//通过此结构查询我们自己的ICompTest接口
cout << pCompTest->HelloWorld() << endl;//调用我们自己接口中的函数
pCompTest->Release(); //释放自己的接口
pUnknown->Release(); //释放IUnknown接口
CoUninitialize(); //COM库反初始化
return 0;
}
运行结果如图:
源码
ICompTest.h
//ICompTest.h
#pragma once
#include <unknwn.h>
// {81A80687-6CC4-4996-8DD2-F058907FDCA8}
static const GUID IID_ICompTest =
{ 0x81a80687, 0x6cc4, 0x4996, { 0x8d, 0xd2, 0xf0, 0x58, 0x90, 0x7f, 0xdc, 0xa8 } };
class ICompTest :
public IUnknown
{
public:
virtual int _stdcall HelloWorld() = 0;
};
CompTestClass.h
//CompTestClass.h
#pragma once
#include "ICompTest.h"
// {9CA9DBE8-C0B1-42c9-B6C7-856BE5756855}
static const GUID CLSID_CompTestClass =
{ 0x9ca9dbe8, 0xc0b1, 0x42c9, {0xb6, 0xc7, 0x85, 0x6b, 0xe5, 0x75, 0x68, 0x55 } };
class CompTestClass :
public ICompTest
{
public:
CompTestClass();
~CompTestClass();
//要实现IUnknown接口
virtual HRESULT _stdcall QueryInterface(const IID& riid, void** ppvObject);
virtual ULONG _stdcall AddRef();
virtual ULONG _stdcall Release();
//要实现ICompTest接口
virtual int _stdcall HelloWorld();
ULONG ObjNum();
static int Init();
protected:
ULONG m_Ref;
public:
static ULONG m_objNum;
static CRITICAL_SECTION m_cs;
};
factory.h
//factory.h
#pragma once
#include <unknwn.h>
class CompTestFactory :
public IClassFactory
{
public:
CompTestFactory();
~CompTestFactory();
//要实现IUnknown接口
virtual HRESULT _stdcall QueryInterface(const IID& riid, void** ppvObject);
virtual ULONG _stdcall AddRef();
virtual ULONG _stdcall Release();
//要实现IClassFactory接口
virtual HRESULT _stdcall CreateInstance(IUnknown* pUnkOuter, const IID& riid, void** ppvObject);
virtual HRESULT _stdcall LockServer(BOOL fLock);
protected:
ULONG m_Ref;
};
CompTestClass.cpp
//CompTestClass.cpp
#include "pch.h"
#include "CompTestClass.h"
ULONG CompTestClass::m_objNum = 0;//组件中CompTestClass对象的个数,用于判断是否可以卸载本组建,如值为0则可以卸载
CRITICAL_SECTION CompTestClass::m_cs;//为了多线程调用对m_objNum加的锁
CompTestClass::CompTestClass()
{
m_Ref = 0;
//autoLock tlock(m_cs);
m_objNum++; //构造了一个对象
}
CompTestClass::~CompTestClass()
{
//autoLock tlock(m_cs);
m_objNum--; //释放了一个对象
}
HRESULT _stdcall CompTestClass::QueryInterface(const IID& riid, void** ppvObject)
{
if (IID_IUnknown == riid) {
*ppvObject = (IUnknown*)this;
((IUnknown*)(*ppvObject))->AddRef();
}
else if (IID_ICompTest == riid) {
*ppvObject = (ICompTest*)this;
((ICompTest*)(*ppvObject))->AddRef();
}
else {
*ppvObject = NULL;
return E_NOINTERFACE;
}
return S_OK;
}
ULONG _stdcall CompTestClass::AddRef()
{
m_Ref++;
return m_Ref;
}
ULONG _stdcall CompTestClass::Release()
{
m_Ref--;
if (0 == m_Ref) {
delete this;
return 0;
}
return m_Ref;
}
int _stdcall CompTestClass::HelloWorld()//ICompTest接口的实现,返回一个整数89
{
return 89;
}
int CompTestClass::Init()
{
m_objNum = 0;
InitializeCriticalSection(&m_cs);
return 0;
}
ULONG CompTestClass::ObjNum()
{
return m_objNum;
}
factory.cpp
//factory.cpp
#include "pch.h"
#include "factory.h"
#include "CompTestClass.h"
CompTestFactory::CompTestFactory()
{
m_Ref = 0;
}
CompTestFactory::~CompTestFactory()
{
}
HRESULT _stdcall CompTestFactory::QueryInterface(const IID& riid, void** ppvObject)
{
if (IID_IUnknown == riid) {
*ppvObject = (IUnknown*)this;
((IUnknown*)(*ppvObject))->AddRef();
}
else if (IID_IClassFactory == riid) {
*ppvObject = (IClassFactory*)this;
((IClassFactory*)(*ppvObject))->AddRef();
}
else {
*ppvObject = NULL;
return E_NOINTERFACE;
}
return S_OK;
}
ULONG _stdcall CompTestFactory::AddRef()
{
m_Ref++;
return m_Ref;
}
ULONG _stdcall CompTestFactory::Release()
{
m_Ref--;
if (0 == m_Ref) {
delete this;
return 0;
}
return m_Ref;
}
HRESULT _stdcall CompTestFactory::CreateInstance(IUnknown* pUnkOuter, const IID& riid, void** ppvObject)//最重要的函数,这个函数创建CompTestClass对象,并返回所需接口
{
if (NULL != pUnkOuter) {
return CLASS_E_NOAGGREGATION;
}
HRESULT hr = E_OUTOFMEMORY;
CompTestClass::Init();
CompTestClass* pObj = new CompTestClass();
if (NULL == pObj) {
return hr;
}
hr = pObj->QueryInterface(riid, ppvObject);
if (S_OK != hr) {
delete pObj;
}
return hr;
}
HRESULT _stdcall CompTestFactory::LockServer(BOOL fLock)
{
return NOERROR;
}
CompTest.cpp文章来源:https://www.toymoban.com/news/detail-509978.html
//CompTest.cpp
#include "pch.h"
#include <iostream>
#include <windows.h>
#include "factory.h"
#include "CompTestClass.h"
using namespace std;
HMODULE g_hModule; //dll进程实例句柄
ULONG g_num; //组件中CompTestClass对象的个数,用于判断是否可以卸载本组建,如值为0则可以卸载
int myReg(LPCWSTR lpPath) //将本组件的信息写入注册表,包括CLSID、所在路径lpPath、ProgID
{
HKEY thk, tclsidk;
//打开键HKEY_CLASSES_ROOT\CLSID,创建新键为CompTestClass的CLSID,
//在该键下创建键InprocServer32,并将本组件(dll)所在路径lpPath写为该键的默认值
if (ERROR_SUCCESS == RegOpenKey(HKEY_CLASSES_ROOT, L"CLSID", &thk)) {
if (ERROR_SUCCESS == RegCreateKey(thk, L"{9CA9DBE8-C0B1-42c9-B6C7-856BE5756855}", &tclsidk)) {
HKEY tinps32k, tprogidk;
if (ERROR_SUCCESS == RegCreateKey(tclsidk, L"InprocServer32", &tinps32k)) {
if (ERROR_SUCCESS == RegSetValue(tinps32k, NULL, REG_SZ, lpPath, wcslen(lpPath) * 2)) {
}
RegCloseKey(tinps32k);
}
RegCloseKey(tclsidk);
}
RegCloseKey(thk);
}
//在键HKEY_CLASSES_ROOT下创建新键为COMCTL.CompTest,
//在该键下创建子键,并将CompTestClass的CLSID写为该键的默认值
if (ERROR_SUCCESS == RegCreateKey(HKEY_CLASSES_ROOT, L"COMCTL.CompTest", &thk)) {
if (ERROR_SUCCESS == RegCreateKey(thk, L"CLSID", &tclsidk)) {
if (ERROR_SUCCESS == RegSetValue(tclsidk,
NULL,
REG_SZ,
L"{9CA9DBE8-C0B1-42c9-B6C7-856BE5756855}",
wcslen(L"{9CA9DBE8-C0B1-42c9-B6C7-856BE5756855}") * 2)) {
}
}
}
//这样的话一个客户端程序如果想要使用本组件,首先可以以COMCTL.CompTest为参数调用CLSIDFromProgID函数
//来获取CompTestClass的CLSID,再以这个CLSID为参数调用CoCreateInstance创建COM对象
return 0;
}
extern "C" HRESULT _stdcall DllRegisterServer()
{
WCHAR szModule[1024];
DWORD dwResult = GetModuleFileName(g_hModule, szModule, 1024); //获取本组件(dll)所在路径
WCHAR szModule1[1024] = { 'b','e','g', 'i', 'n', '\0'};
MessageBox(NULL, szModule1, L"", MB_OK);
if (0 == dwResult) {
return -1;
}
MessageBox(NULL, szModule, L"", MB_OK);
myReg(szModule);//将路径等信息写入注册表
return 0;
}
int myDelKey(HKEY hk, LPCWSTR lp)
{
if (ERROR_SUCCESS == RegDeleteKey(hk, lp)) {
}
return 0;
}
int myDel() //删除注册时写入注册表的信息
{
HKEY thk;
if (ERROR_SUCCESS == RegOpenKey(HKEY_CLASSES_ROOT, L"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, L"COMCTL.CompTest", &thk)) {
myDelKey(thk, L"CLSID");
}
myDelKey(HKEY_CLASSES_ROOT, L"COMCTL.CompTest");
return 0;
}
extern "C" HRESULT _stdcall DllUnregisterServer()
{
myDel();//删除注册时写入注册表的信息
return 0;
}
STDAPI DllCanUnloadNow(void)//用于判断是否可以卸载本组建, 由CoFreeUnusedLibraries函数调用
{
if (0 == g_num) {//如果对象个数为0,则可以卸载
return S_OK;
}
else {
return S_FALSE;
}
return S_OK;
}
STDAPI DllGetClassObject(_In_ REFCLSID rclsid, _In_ REFIID riid, _Outptr_ LPVOID FAR * ppv)//用于创建类厂并返回所需接口,由CoGetClassObject函数调用
{
WCHAR szModule1[1024] = { 'b','e','g', 'i', 'n', ' ', 'g', 'e', 't', 'c', 'l', ' \0' };
szModule1[11] = '\0';
MessageBox(NULL, szModule1, L"", MB_OK);
if (CLSID_CompTestClass == rclsid) {
CompTestFactory* pFactory = new CompTestFactory();//创建类厂对象
if (NULL == pFactory) {
return E_OUTOFMEMORY;
}
HRESULT result = pFactory->QueryInterface(riid, ppv);//获取所需接口
return result;
}
else {
return CLASS_E_CLASSNOTAVAILABLE;
}
return S_OK;
}
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;
}
mydef.def文章来源地址https://www.toymoban.com/news/detail-509978.html
//mydef.def
LIBRARY "CompTest"
EXPORTS
DllCanUnloadNow
DllGetClassObject
DllUnregisterServer
DllRegisterServer
到了这里,关于[com]使用VS2022创建一个自定义com组件--C++的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!