C语言调用COM组件

为了更好的理解C的做法,我们要先看一看C++调用COM组件是怎么做的。


一、C++方式

从 Windows 7 开始,任务栏可以显示进度条,就以这个接口为例吧。

ITaskbarList4 *pTaskbar = nullptr;
HRESULT hResult = ::CoCreateInstance(CLSID_TaskbarList, nullptr, CLSCTX_INPROC_SERVER, __uuidof(ITaskbarList4), reinterpret_cast<void**>(&pTaskbar));

上面的代码中,ITaskbarList4是一个接口(一个只有纯虚函数的类),CoCreateInstance的作用是创建一个ITaskbarList4接口的实例。创建成功后,就可以使用pTaskbar了。

hResult = pTaskbar->HrInit();


二、C方式

在C++代码中,我们用到了接口(类),而C语言是没有类的,那么应该如何做呢?

我们知道,COM组件是二进制兼容的典范。要在C中使用ITaskbarList4接口,我们必须提供一个与ITaskbarList4在内存模型上相同的C变量。

#define STDMETHODCALLTYPE __stdcall

typedef struct ITaskbarList4Vtbl
{
    HRESULT(STDMETHODCALLTYPE *QueryInterface)(ITaskbarList4 *This, const IID *riid, void **ppvObject);
    ULONG(STDMETHODCALLTYPE *AddRef)(ITaskbarList4 *This);
    ULONG(STDMETHODCALLTYPE *Release)(ITaskbarList4 *This);
    HRESULT(STDMETHODCALLTYPE *HrInit)(ITaskbarList4 *This);
    HRESULT(STDMETHODCALLTYPE *AddTab)(ITaskbarList4 *This, HWND hwnd);
    HRESULT(STDMETHODCALLTYPE *DeleteTab)(ITaskbarList4 *This, HWND hwnd);

    // 省略其他代码

} ITaskbarList4Vtbl;

typedef struct ITaskbarList4
{
    ITaskbarList4Vtbl *pVtbl;
} ITaskbarList4;

上面的C代码中的ITaskbarList4与C++代码中的ITaskbarList4的内存模型是相同的(参看著名的图书《深入探索C++对象模型》),而pVtbl是C++的ITaskbarList4的虚函数表,ITaskbarList4 *This是C++中的this指针。

将类ITaskbarList4的虚函数表全部用C写出来后,就可以使用C调用COM组件了。当然了,这种做法的优雅程度比不上C++方式。

ITaskbarList4* pTaskbar = NULL;
HRESULT hr = 0L;

hr = CoCreateInstance(&CLSID_TaskbarList, NULL, CLSCTX_INPROC_SERVER, &IID_ITaskbarList4, (void**)&pTaskbar);

hr = pTaskbar->lpVtbl->HrInit(pTaskbar);

hr = pTaskbar->lpVtbl->SetProgressValue(pTaskbar, hWnd, 20ui64, 100ui64);

C代码比C++代码多个一个参数,而这个参数传递的正是接口自身。这个参数其实就是C++中的this指针,在C语言中,必须显示地提供它,而在C++中,this指针是隐藏的,所以我们才看到C代码多了一个参数。


三、一般规则

一般而言,如果C++版的COM接口为

class IFunc
{
public:
    virtual R_1 Func_1(参数表1)= 0;
    virtual R_2 Func_2(参数表2) = 0;
    // ......
    virtual R_n Func_3(参数表n) = 0;
};

那么对应的C结构是:

struct Func;
typedef struct Func Func;

typedef struct FuncVtbl // 虚函数表
{
    R_1(__stdcall Func_1)(Func *This, 参数表1);
    R_2(__stdcall Func_2)(Func *This, 参数表2);
    // ......
    R_n(__stdcall Func_n)(Func *This, 参数表n);
} FuncVtbl;

struct Func
{
    FuncVtbl *pVtbl;
};

你可能感兴趣的:(组件,C语言,调用,com,纯C)