[com]使用VS2022创建一个自定义com组件--C++

本文详细介绍了如何在VisualStudio2022中创建一个C++COM组件,包括新建DLL项目、编写接口和实现、注册及测试步骤。通过示例代码展示了从创建项目到实现接口、注册组件以及在客户端程序中使用该组件的过程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

使用VS2022创建一个自定义com组件--C++

本文是在《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,内容会贴在后面
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

//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

//mydef.def
LIBRARY "CompTest"
EXPORTS
DllCanUnloadNow
DllGetClassObject
DllUnregisterServer
DllRegisterServer
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值