C++ com编程学习详解

C++ COM编程学习详解攻略

什么是COM?

COM(Component Object Model)是一种面向对象的软件组件技术,主要用于在不同的应用程序之间通信。使用COM,你可以编写可重用的软件组件,这些组件可以跨越不同的编程语言,操作系统和网络。COM最初是由Microsoft开发的。

学习COM的前提条件

  • 了解C++语言,并熟练掌握面向对象编程。
  • 具备基本的Windows编程知识,例如Windows消息循环、窗口及控件的操作等。
  • 了解COM相关的基本概念,例如接口、类工厂等。

COM编程基本步骤

  1. 设计COM接口和实现类
  2. 在COM中,接口是对象与外部世界进行交互的唯一方式。需要先设计接口,然后再根据接口定义实现类。
  3. 接口可以使用IDL(Interface Definition Language)定义,也可以使用C++定义。

  4. 实现COM对象

  5. 实现COM对象要实现接口,同时需要了解对象的生命周期和资源管理方法。

  6. 注册COM组件

  7. 注册COM组件需要使用注册表。注册表可以使用系统提供的RegEdit.exe工具查看和编辑,也可以使用编程语言访问。

  8. 创建COM对象

  9. 在客户端程序中,需要实例化COM对象并调用其提供的接口。

示例一:计算器COM组件

以下是一个简单的计算器组件,包含加、减、乘、除四个操作。

1. 设计接口

[object, uuid(b5c61d71-29b4-487d-a198-50842ac0cd33)]
interface ICalculator : IUnknown
{
    HRESULT Add(double a, double b, [out] double* result);
    HRESULT Subtract(double a, double b, [out] double* result);
    HRESULT Multiply(double a, double b, [out] double* result);
    HRESULT Divide(double a, double b, [out] double* result);
};

定义了四个方法Add、Subtract、Multiply、Divide,每个方法都有两个输入参数和一个输出参数。其中第三个参数是一个指向输出结果的指针。

2. 实现类

#include <windows.h>
#include "Calculator.h"

class Calculator : public ICalculator
{
public:
    Calculator() :
        m_refCount(1)
    {}

    // IUnknown methods
    HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void** ppvObject)
    {
        if (riid == __uuidof(ICalculator) || riid == __uuidof(IUnknown))
        {
            *ppvObject = this;
            AddRef();
            return S_OK;
        }
        *ppvObject = nullptr;
        return E_NOINTERFACE;
    }

    ULONG STDMETHODCALLTYPE AddRef()
    {
        return InterlockedIncrement(&m_refCount);
    }

    ULONG STDMETHODCALLTYPE Release()
    {
        ULONG count = InterlockedDecrement(&m_refCount);
        if (count == 0)
        {
            delete this;
        }
        return count;
    }

    // ICalculator methods
    HRESULT STDMETHODCALLTYPE Add(double a, double b, double* result)
    {
        *result = a + b;
        return S_OK;
    }

    HRESULT STDMETHODCALLTYPE Subtract(double a, double b, double* result)
    {
        *result = a - b;
        return S_OK;
    }

    HRESULT STDMETHODCALLTYPE Multiply(double a, double b, double* result)
    {
        *result = a * b;
        return S_OK;
    }

    HRESULT STDMETHODCALLTYPE Divide(double a, double b, double* result)
    {
        if (b == 0)
        {
            return E_INVALIDARG;
        }
        *result = a / b;
        return S_OK;
    }

private:
    ULONG m_refCount;
};

实现了ICalculator接口,其中QueryInterface、AddRef和Release方法是实现IUnknown接口所必需的。对象的引用计数由m_refCount维护。

3. 注册COM组件

需要在注册表中添加以下记录:

[HKEY_CLASSES_ROOT\CLSID\{B5C61D71-29B4-487D-A198-50842AC0CD33}]
@="Calculator"

[HKEY_CLASSES_ROOT\CLSID\{B5C61D71-29B4-487D-A198-50842AC0CD33}\InProcServer32]
@="C:\\calc.dll"
"ThreadingModel"="Apartment"

其中{B5C61D71-29B4-487D-A198-50842AC0CD33}是所分配的GUID,C:\calc.dll是计算器组件的实现文件路径。

4. 创建COM对象

#include <windows.h>
#include <iostream>
#include "Calculator.h"

int main()
{
    // 初始化COM
    CoInitialize(nullptr);

    // 创建COM对象
    ICalculator* calculator = nullptr;
    HRESULT hr = CoCreateInstance(CLSID_Calculator, nullptr, CLSCTX_INPROC_SERVER,
        IID_ICalculator, (LPVOID*)&calculator);

    if (SUCCEEDED(hr))
    {
        double result = 0;
        calculator->Add(2, 3, &result);
        std::cout << "2 + 3 = " result << std::endl;

        calculator->Subtract(4, 1, &result);
        std::cout << "4 - 1 = " << result << std::endl;

        calculator->Multiply(2, 4, &result);
        std::cout << "2 * 4 = " << result << std::endl;

        calculator->Divide(8, 2, &result);
        std::cout << "8 / 2 = " << result << std::endl;

        calculator->Release();
    }

    // 反初始化COM
    CoUninitialize();

    return 0;
}

在程序中,使用CoCreateInstance方法实例化COM对象,调用其接口方法完成四则运算。

示例二:主机监视器COM组件

另一个示例是一个主机监视器组件,用于获取电脑的CPU使用率和内存占用情况。

1. 设计接口

[object, uuid(4589bac9-b3e9-4bcf-9987-3d5c8cf842bb)]
interface IHostMonitor : IUnknown
{
    HRESULT GetCpuUsage([out] double* usage);
    HRESULT GetMemoryUsage([out] double* usage);
};

定义了两个方法GetCpuUsage和GetMemoryUsage。

2. 实现类

#include <windows.h>
#include "HostMonitor.h"

static const int SAMPLING_TIME = 1000;

class HostMonitor : public IHostMonitor
{
public:
    HostMonitor() :
        m_refCount(1)
    {
        m_lastIdleTime.QuadPart = 0;
        m_lastKernelTime.QuadPart = 0;
        m_lastUserTime.QuadPart = 0;

        MEMORYSTATUSEX mem;
        mem.dwLength = sizeof(mem);
        GlobalMemoryStatusEx(&mem);
        m_totalMemory = mem.ullTotalPhys;
    }

    // IUnknown methods
    HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void** ppvObject)
    {
        if (riid == __uuidof(IHostMonitor) || riid == __uuidof(IUnknown))
        {
            *ppvObject = this;
            AddRef();
            return S_OK;
        }
        *ppvObject = nullptr;
        return E_NOINTERFACE;
    }

    ULONG STDMETHODCALLTYPE AddRef()
    {
        return InterlockedIncrement(&m_refCount);
    }

    ULONG STDMETHODCALLTYPE Release()
    {
        ULONG count = InterlockedDecrement(&m_refCount);
        if (count == 0)
        {
            delete this;
        }
        return count;
    }

    // IHostMonitor methods
    HRESULT STDMETHODCALLTYPE GetCpuUsage(double* usage)
    {
        FILETIME idleTime, kernelTime, userTime;
        if (GetSystemTimes(&idleTime, &kernelTime, &userTime))
        {
            __int64 deltaIdleTime = SubtractTimes(idleTime, m_lastIdleTime);
            __int64 deltaKernelTime = SubtractTimes(kernelTime, m_lastKernelTime);
            __int64 deltaUserTime = SubtractTimes(userTime, m_lastUserTime);

            if (deltaKernelTime + deltaUserTime > 0)
            {
                *usage = 1.0 - (double)deltaIdleTime / (double)(deltaKernelTime + deltaUserTime);
                *usage *= 100;
            }

            m_lastIdleTime = idleTime;
            m_lastKernelTime = kernelTime;
            m_lastUserTime = userTime;
        }

        return S_OK;
    }

    HRESULT STDMETHODCALLTYPE GetMemoryUsage(double* usage)
    {
        MEMORYSTATUSEX mem;
        mem.dwLength = sizeof(mem);
        if (GlobalMemoryStatusEx(&mem))
        {
            *usage = (double)(m_totalMemory - mem.ullAvailPhys) / (double)m_totalMemory;
            *usage *= 100;
        }

        return S_OK;
    }

private:
    ULONG m_refCount;
    FILETIME m_lastIdleTime;
    FILETIME m_lastKernelTime;
    FILETIME m_lastUserTime;
    unsigned long long m_totalMemory;

    __int64 SubtractTimes(const FILETIME& ft1, const FILETIME& ft2)
    {
        ULARGE_INTEGER u1, u2;
        u1.LowPart = ft1.dwLowDateTime;
        u1.HighPart = ft1.dwHighDateTime;
        u2.LowPart = ft2.dwLowDateTime;
        u2.HighPart = ft2.dwHighDateTime;
        return u1.QuadPart - u2.QuadPart;
    }
};

该类实现了IHostMonitor接口,其中GetCpuUsage方法获取CPU使用率,GetMemoryUsage方法获取内存占用情况。在GetCpuUsage方法中,使用GetSystemTimes方法获取了系统的运行时间,计算出CPU使用率。

3. 注册COM组件

与上一个示例类似,需要在注册表中添加以下记录:

[HKEY_CLASSES_ROOT\CLSID\{4589BAC9-B3E9-4BCF-9987-3D5C8CF842BB}]
@="HostMonitor"

[HKEY_CLASSES_ROOT\CLSID\{4589BAC9-B3E9-4BCF-9987-3D5C8CF842BB}\InProcServer32]
@="C:\\host.dll"
"ThreadingModel"="Apartment"

4. 创建COM对象

#include <windows.h>
#include <iostream>
#include "HostMonitor.h"

int main()
{
    // 初始化COM
    CoInitialize(nullptr);

    // 创建COM对象
    IHostMonitor* monitor = nullptr;
    HRESULT hr = CoCreateInstance(CLSID_HostMonitor, nullptr, CLSCTX_INPROC_SERVER,
        IID_IHostMonitor, (LPVOID*)&monitor);

    if (SUCCEEDED(hr))
    {
        double cpuUsage = 0;
        monitor->GetCpuUsage(&cpuUsage);
        std::cout << "CPU:" << cpuUsage << "%" << std::endl;

        double memoryUsage = 0;
        monitor->GetMemoryUsage(&memoryUsage);
        std::cout << "Memory:" << memoryUsage << "%" << std::endl;

        monitor->Release();
    }

    // 反初始化COM
    CoUninitialize();

    return 0;
}

在程序中,使用CoCreateInstance方法实例化COM对象,调用其接口方法完成获取CPU使用率和内存占用情况。

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:C++ com编程学习详解 - Python技术站

(0)
上一篇 2023年5月22日
下一篇 2023年5月22日

相关文章

  • 英语打字练习软件-c语言编写

    ​学习c语言的时候编写的英语打字练习软件,已经上传github 自取 https://github.com/grey-wood-wolf/typing-software   软件实际效果如下 在下载的压缩包里,运行exe文件就可使用,源码为ConsoleApplication1这个文件      部分代码如下: void welcom()//介绍 { int…

    C语言 2023年4月18日
    00
  • 解析C++多文件编程问题

    针对”解析C++多文件编程问题”,这里提供一份完整攻略,希望能够解决大家的疑惑。 什么是C++多文件编程问题? 在C++编程中,当你需要引用多个.cpp文件中的函数和变量时,你会发现编译器会提示未定义或者找不到引用的参数。这时,你需要将相关的头文件引入进来,在编译器中进行链接,才能解决这个问题。 解决C++多文件编程问题的方法 C++ 多文件编程的解决方法主…

    C 2023年5月23日
    00
  • Dev C++安装使用图文教程(使用Dev C++编写C语言程序)

    Dev C++安装使用图文教程(使用Dev C++编写C语言程序) 1. 下载安装Dev C++ 我们可以在Dev C++官网上下载最新版本的Dev C++安装文件。选择“Download”即可开始下载。 下载完成后,双击运行下载的安装包,按照提示安装即可完成Dev C++的安装。 2. 创建新的C语言项目 打开Dev C++,选择“File” – “New…

    C 2023年5月23日
    00
  • C语言用值传递数据

    C语言的值传递 C语言函数参数传递方式包括值传递和引用传递两种方式。值传递就是指在调用函数时,将实参的值复制一份传递给形参,函数内部对形参的修改不会影响到实参的值。C语言的值传递语法很简单,只需要在函数声明和函数定义时声明一个变量即可。 示例一:值传递函数的定义和调用方法 下面的代码演示了一个简单的值传递的函数定义和调用的方法。 #include <s…

    C 2023年5月9日
    00
  • 在Linux系统中使用GDB来调试C/C++程序的方法

    在Linux系统中使用GDB来调试C/C++程序的方法可以分为以下几个步骤: 1. 编译C/C++程序时添加编译选项 为了让程序在调试时保留符号表信息,需要在编译C/C++源代码时添加编译选项 -g。例如: $ gcc -g -o myprog myprog.c 这样编译出来的可执行文件中就包含了符号表信息,可以用于调试。 2. 启动GDB调试器 在终端中输…

    C 2023年5月24日
    00
  • php自定义中文字符串截取函数substr_for_gb2312及substr_for_utf8示例

    下面我将为您详细讲解“php自定义中文字符串截取函数substr_for_gb2312及substr_for_utf8示例”的攻略。 分析问题 中文字符串截取是一个比较常见的需求,但是在PHP中的substr函数并不支持中文字符集,如果直接使用原生substr函数截取中文字符串会导致出现乱码或者截取不准确的问题。所以我们需要自定义中文字符串截取函数来解决这个…

    C 2023年5月22日
    00
  • java jni调用c函数实例分享(java调用c函数)

    下面我将为您详细讲解“Java JNI调用C函数实例分享(Java调用C函数)”的攻略。 什么是JNI? JNI全称为Java Native Interface,即Java本地接口。它是一个开发者提供的桥梁,用于将Java虚拟机(JVM)连接到应用程序中的非Java代码(如C语言、C++等)。使用JNI可以使Java程序调用C语言等非java语言编写的代码或…

    C 2023年5月23日
    00
  • C++语言基础 命名空间

    C++是一门支持命名空间的语言,命名空间是C++中避免命名冲突的一个重要方式。我们可以通过使用命名空间,把定义在不同范围内的标识符分开,从而保证程序中的标识符不会冲突。 在C++中,命名空间是用关键字“namespace”来定义,如下所示: namespace MyNamespace { // 声明和定义各种变量、函数、类等成员 } 这里的“MyNamesp…

    C 2023年5月23日
    00
合作推广
合作推广
分享本页
返回顶部