C++中COM组件初始化方法实例分析

C++中COM组件初始化方法实例分析

什么是COM组件

COM(Component Object Model)是一种基于Windows操作系统的二进制接口标准,用于组件化应用程序的开发和集成。COM组件是可以独立被调用和管理的二进制对象模块,因为它们可以被跨语言、跨平台地使用。

COM组件初始化方法

COM组件的初始化方法有两种:基于CoCreateInstance的懒加载和基于DllGetClassObject的预加载。懒加载是在调用COM对象之后才会将自己放在进程内存空间中,而预加载则是在进程启动时就将所有COM对象放置在内存空间中。

基于CoCreateInstance的懒加载

基于CoCreateInstance的懒加载方法的示例代码如下:

#include <objbase.h>
#include "MyCOMObject.h"

HRESULT MyCOMObjectCreateInstance(REFCLSID rclsid, REFIID riid, void** ppvObj)
{
    MyCOMObject* pObj = new MyCOMObject();
    if (!pObj)
    {
        return E_OUTOFMEMORY;
    }

    HRESULT hr = pObj->QueryInterface(riid, ppvObj);
    if (FAILED(hr))
    {
        delete pObj;
        pObj = NULL;
    }

    return hr;
}

int main()
{
    CoInitialize(NULL);

    IMyCOMObject* pObj = NULL;
    HRESULT hr = CoCreateInstance(CLSID_MyCOMObject, NULL, CLSCTX_INPROC_SERVER,
                                  IID_IMyCOMObject, (void**)&pObj);
    if (FAILED(hr))
    {
        return hr;
    }

    // 使用COM对象
    pObj->DoSomething();

    // 释放COM对象
    pObj->Release();

    CoUninitialize();
}

在这个示例代码中,MyCOMObjectCreateInstance是用于创建COM对象实例的函数,这个函数会在调用CoCreateInstance函数时被调用。在这个函数里,我们通过new运算符创建了一个MyCOMObject的实例,并且通过QueryInterface函数将其转换成了需要创建的COM组件的接口类型。如果创建COM对象实例失败会返回错误码,成功则将COM对象指针传递回去。

main函数中,首先调用CoInitialize函数进行COM初始化,然后使用CoCreateInstance函数创建COM对象实例,COM对象的GUID、所在.dll文件以及请求的接口类型都在这里确定。创建COM对象实例的函数MyCOMObjectCreateInstance就会被调用,它会返回COM组件的指针句柄,在之后的代码中我们通过这个句柄调用COM对象的方法。

基于DllGetClassObject的预加载

基于DllGetClassObject的预加载方法的示例代码如下:

#include <windows.h>
#include <objbase.h>
#include "MyCOMObject.h"

HRESULT MyCOMObjectCreateInstance(REFCLSID rclsid, REFIID riid, void** ppvObj)
{
    MyCOMObject* pObj = new MyCOMObject();
    if (!pObj)
    {
        return E_OUTOFMEMORY;
    }

    HRESULT hr = pObj->QueryInterface(riid, ppvObj);
    if (FAILED(hr))
    {
        delete pObj;
        pObj = NULL;
    }

    return hr;
}

class MyCOMClassFactory : public IClassFactory
{
public:
    // IUnknown
    STDMETHOD(QueryInterface)(REFIID riid, LPVOID FAR* ppvObj)
    {
        if (IsEqualIID(riid, IID_IUnknown) ||
            IsEqualIID(riid, IID_IClassFactory))
        {
            *ppvObj = static_cast<IClassFactory*>(this);
            AddRef();
            return S_OK;
        }

        *ppvObj = NULL;
        return E_NOINTERFACE;
    }
    STDMETHOD_(ULONG, AddRef)()
    {
        return InterlockedIncrement(&m_cRef);
    }
    STDMETHOD_(ULONG, Release)()
    {
        LONG lRefCount = InterlockedDecrement(&m_cRef);
        if (lRefCount == 0)
        {
            delete this;
        }

        return lRefCount;
    }

    // IClassFactory
    STDMETHOD(CreateInstance)(LPUNKNOWN pUnkOuter,
                              REFIID riid,
                              LPVOID FAR* ppvObj)
    {
        return MyCOMObjectCreateInstance(CLSID_MyCOMObject, riid, ppvObj);
    }
    STDMETHOD(LockServer)(BOOL fLock)
    {
        // Not implemented for this example
        return E_NOTIMPL;
    }

private:
    LONG m_cRef;
};

class MyCOMModule : public IUnknown
{
public:
    // IUnknown
    STDMETHOD(QueryInterface)(REFIID riid, VOID** ppvObj)
    {
        if (IsEqualIID(riid, IID_IUnknown) ||
            IsEqualIID(riid, IID_IClassFactory))
        {
            *ppvObj = this;
            AddRef();
            return S_OK;
        }

        *ppvObj = NULL;
        return E_NOINTERFACE;
    }

    STDMETHOD_(ULONG, AddRef)()
    {
        return InterlockedIncrement(&m_cRef);
    }

    STDMETHOD_(ULONG, Release)()
    {
        LONG lRefCount = InterlockedDecrement(&m_cRef);
        if (lRefCount == 0)
        {
            delete this;
        }

        return lRefCount;
    }

    MyCOMModule()
    {
        m_pClassFactory = new MyCOMClassFactory;
    }

    ~MyCOMModule()
    {
        if (m_pClassFactory)
        {
            m_pClassFactory->Release();
            m_pClassFactory = NULL;
        }
    }

    STDMETHOD(RegisterServer)()
    {
        // Not implemented for this example
        return S_OK;
    }

    STDMETHOD(UnregisterServer)()
    {
        // Not implemented for this example
        return S_OK;
    }

    STDMETHOD(GetClassObject)(REFCLSID rclsid, REFIID riid, VOID** ppvObj)
    {
        if (IsEqualCLSID(rclsid, CLSID_MyCOMObject))
        {
            return m_pClassFactory->QueryInterface(riid, ppvObj);
        }

        return E_FAIL;
    }

private:
    MyCOMClassFactory* m_pClassFactory;
    LONG m_cRef;
};

MyCOMModule* g_pModule = NULL;

BOOL APIENTRY DllMain(HMODULE hModule,
                      DWORD  ul_reason_for_call,
                      LPVOID lpReserved)
{
    switch (ul_reason_for_call)
    {
    case DLL_PROCESS_ATTACH:
        g_pModule = new MyCOMModule();
        break;

    case DLL_PROCESS_DETACH:
        if (g_pModule)
        {
            g_pModule->Release();
            g_pModule = NULL;
        }
        break;
    }
    return TRUE;
}

在这个示例代码中,我们需要用到创建COM对象实例的函数MyCOMObjectCreateInstance,它与前面基于CoCreateInstance懒加载的示例代码相同。

另外,我们需要自定义一个实现了IClassFactory接口的类MyCOMClassFactory,在其中实现了CreateInstanceLockServer方法,其中CreateInstance方法调用之前提到的MyCOMObjectCreateInstance函数来创建COM对象实例并返回指针句柄。LockServer方法则用于锁定COM组件非核心部分的类对象在内存中的计数器。

我们还定义了一个自定义的MyCOMModule类,实现了IUnknown接口,其中包括了一个m_pClassFactory成员变量,它的类型是之前我们定义的MyCOMClassFactoryMyCOMModule还重载了GetClassObject方法,根据传进来的clsid来创建COM对象实例。在DLLMain函数中会实例化MyCOMModule类并将其存储在全局变量中。当调用CoCreateInstance时,COM会调用MyCOMModuleGetClassObject函数来创建COM对象。

最后,我们需要将这个DLL文件注册到系统中,可以使用regsvr32.exe来执行这个操作。

小结

本文介绍了C++中COM组件的初始化方法,包括基于CoCreateInstance的懒加载和基于DllGetClassObject的预加载。在示例代码中,我们使用了一个简单的COM对象MyCOMObject来说明这两种初始化方法的使用。为了预加载COM对象的示例代码更加复杂,我们添加了自定义的MyCOMClassFactoryMyCOMModule类。

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:C++中COM组件初始化方法实例分析 - Python技术站

(0)
上一篇 2023年6月20日
下一篇 2023年6月20日

相关文章

  • 史上最全宽带连接错误解决办法(图文)

    下面是详细的“史上最全宽带连接错误解决办法(图文)”攻略。 一、前言 本文旨在提供一些宽带连接错误的解决办法,帮助用户更快速地排除问题,以便在使用互联网时更加便利。 二、常见宽带连接错误及解决办法 1. 连接不到网络 如果无法连接到网络,首先需要检查以下几个问题: 是否已经开启电脑/路由器/光猫等网络设备 是否正确连接了网络线 是否已经正确设置IP地址和DN…

    other 2023年6月26日
    00
  • 电脑自动重启怎么办 电脑频繁重启的解决方法

    电脑自动重启怎么办:电脑频繁重启的解决方法 电脑频繁自动重启是一种比较常见的问题,会给我们的工作和学习带来影响。然而,它通常并不是一个严重的问题,并且大多数情况下都可以通过以下方法轻松解决。 前期准备 在开始解决电脑自动重启的问题之前,您需要进行以下准备: 备份重要文件,以防意外数据丢失。 关闭自动重启:打开“控制面板” -> “系统和安全” -&gt…

    other 2023年6月26日
    00
  • JS延迟加载(setTimeout) JS最后加载

    JS延迟加载(setTimeout)与JS最后加载是两种优化网页加载速度的方式。 JS延迟加载(setTimeout) JS延迟加载就是在页面加载完成后,通过setTimeout()函数在一定的时间后再加载JS文件。这种方式能够优化首屏渲染速度,减少占资源JS文件的下载量,从而实现快速展示内容。 实现方式:在标签内使用标签引入JS文件,在文件引入后使用set…

    other 2023年6月25日
    00
  • 阿里云DataV专业版发布,为可视化创造更多可能!

    阿里云DataV专业版发布,为可视化创造更多可能! 阿里云DataV是一款专业的大数据可视化产品,它可以帮助用户将复杂的数据呈现为直观、易于理解的可视化图表,从而更好地帮助用户进行数据分析和决策。 近日,阿里云正式发布了DataV专业版,为可视化创造更多的可能性。 DataV专业版具有以下特点: 1.更加智能的数据分析功能 DataV专业版针对大数据分析市场…

    其他 2023年3月28日
    00
  • 如何在Flutter中嵌套Android布局

    如何在Flutter中嵌套Android布局 在Flutter中,可以通过使用PlatformView来嵌套Android布局。PlatformView允许在Flutter应用程序中嵌入原生视图,这样就可以在Flutter界面中使用Android布局。 下面是在Flutter中嵌套Android布局的完整攻略: 步骤1:创建Android布局 首先,我们需要…

    other 2023年7月28日
    00
  • taskset命令详解

    taskset命令详解 在Linux操作系统中,taskset命令可以帮助我们将进程绑定到特定的CPU核心上。本文将详细介绍taskset命令的用法和相关注意事项。 命令语法 taskset [options] [mask] pid 其中,options代表命令选项,mask代表进程的CPU掩码,pid代表需要进行操作的进程ID。 CPU掩码 CPU掩码是一…

    其他 2023年3月28日
    00
  • 机械师T90值得买吗?机械师T90悍狼游戏本深度评测+拆解

    很抱歉,作为文本模型,我无法以标准的markdown格式回答您的问题。但是,我可以为您提供一份详细的攻略,包含机械师T90悍狼游戏本的深度评测和拆解,并提供两个示例说明。 机械师T90悍狼游戏本深度评测+拆解 1. 外观设计 机械师T90悍狼游戏本采用了坚固耐用的金属外壳,具有独特的工业风格设计。其黑色外观和红色背光键盘给人一种高端大气的感觉。 2. 性能表…

    other 2023年10月17日
    00
  • Java编程实现递增排序链表的合并

    要实现递增排序链表的合并,可以采用归并排序的思想:将两个已经排好序的链表合并成一个更大的有序链表。 步骤如下: 首先,判断两个链表是否为空,若有一个为空,则返回另一个链表。 然后,比较两个链表的头结点的值,将值小的头结点作为新链表的头结点。 接着,递归地对剩余的部分进行合并,将小的节点插入到新链表的末尾。 下面是Java代码实现: public class …

    other 2023年6月27日
    00
合作推广
合作推广
分享本页
返回顶部