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日

相关文章

  • 电脑桌面右键新建菜单中没有Word/Excel/PPT等文档怎么办?

    若电脑桌面右键新建菜单中没有Word/Excel/PPT等文档,可能是由于Office软件未正常安装或被卸载导致相关菜单项丢失。 解决方法如下: 步骤一:检查Office软件是否正常安装 首先,需要确认电脑已安装Office软件且安装是完整的。可以通过以下操作来确认: 点击Windows开始按钮,并在搜索框中输入“控制面板”。 在弹出的控制面板窗口中,选择“…

    other 2023年6月27日
    00
  • linux下的定时器:alarm()与setitimer()

    Linux下的定时器:alarm()与setitimer() 在Linux系统中,我们可以使用多种方式实现定时器的功能。其中,两种常用的方式是使用alarm()和setitimer()函数。本文将详细介绍这两个函数的使用方法及差异。 alarm()函数 alarm()函数定义在<unistd.h>头文件中,其原型如下: unsigned int …

    其他 2023年3月28日
    00
  • zepeto进不去怎么办 zepeto一直在加载解决方法

    问题描述:在使用zepeto的过程中,有些用户会遇到无法进入或者一直在加载的情况。这种情况可能是由于网络连接问题或者其他因素导致的。本文将提供一些可能的解决方法供大家参考。 解决方法一:检查网络连接并重试1. 检查手机的网络连接状态,确保网络连接正常。2. 如果网络连接正常,但是仍然无法进入zepeto,可以尝试等待一段时间后重试。 解决方法二:清除缓存和数…

    other 2023年6月25日
    00
  • 右键发送(sendto),创建快捷方式到自定义的位置

    以下是详细的攻略: 安装SendTo Toys工具 首先,我们需要安装一个名为”SendTo Toys”的免费工具,它可以帮助我们创建自定义的”Send to”菜单项。 浏览器中打开http://gabrieleponti.com/software/send-to-toys,下载并安装SendTo Toys工具。 安装完成后,在”开始菜单”中打开”SendT…

    other 2023年6月27日
    00
  • Runtime.getRuntime().exec 路径包含空格的解决

    当路径中包含空格时,使用Runtime.getRuntime().exec()方法执行命令可能会失败。这是因为空格被解释为命令参数的分隔符,导致执行命令时无法正确解析路径。要解决这个问题,可以通过一些技巧来处理路径中的空格,下面是具体方法: 方法一:将路径用引号包起来 我们可以将路径用引号包起来,从而避免空格被解释为分隔符。例如,下面的Java代码演示了如何…

    other 2023年6月26日
    00
  • 使用Windows批处理和WMI设置Python的环境变量方法

    关于“使用Windows批处理和WMI设置Python的环境变量方法”的完整攻略,以下是详细的步骤和示例说明: 1. 了解Windows批处理和WMI Windows批处理(Batch)是指一类以批量处理命令为基础的脚本语言。在Windows操作系统中,可以使用Windows批处理快速进行一系列操作,例如安装程序、打开应用、复制文件等等。WMI(Window…

    other 2023年6月27日
    00
  • arcgis布局视图如何调整大小? arcgis改变布局视图方向以及大小的技巧

    ArcGIS布局视图如何调整大小 在ArcGIS中,可以通过以下步骤来调整布局视图的大小: 打开ArcGIS软件并加载你的布局视图。 在布局视图中,选择“布局”选项卡。 在“布局”选项卡中,点击“页面和打印设置”按钮。 在弹出的对话框中,选择“页面设置”选项卡。 在“页面设置”选项卡中,你可以调整布局视图的大小。你可以选择预设的页面大小,也可以手动输入自定义…

    other 2023年9月5日
    00
  • iOS数据持久化UserDefaults封装器使用详解

    iOS数据持久化UserDefaults封装器使用详解 什么是UserDefaults UserDefaults 是 iOS 中一种轻量级的数据持久化技术,允许我们在应用程序结束后仍然可以保存一些数据,并在下次应用启动时恢复这些数据。 UserDefaults 使用键值对的方式来存储数据。其本质上是一个plist文件,保存在沙盒中的Library/Prefe…

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