ATL/COM被一个有关问题搞得头发晕眼发花元气大伤,特请高人加持

ATL/COM被一个问题搞得头发晕眼发花元气大伤,特请高人加持

void test()
{
CoInitialize(NULL);
CLSID clsid;
HRESULT hr = CLSIDFromProgID(L"Test.my1", &clsid); 
IUnknown *pUnk = NULL;
hr = CoCreateInstance(clsid, NULL, CLSCTX_INPROC_SERVER, IID_IUnknown, (void **)&pUnk);
IDispatch *pDisp = NULL;
pUnk->QueryInterface(IID_IDispatch, (void **)&pDisp);

//执行到这里可以正常获得 pUnk和pDisp 这两个指针,我的问题是从这里开始怎么遍历其它
//其它接口以及方法。
//
//如果用VBS脚本只需要一句话 Dim T1     : Set T1 = CreateObject("Test.my1")
//就可以正常获取T1里面的变量和方法,没想到VC里面变得如此复杂,不知怎么办了。

CoUninitialize();
}



请前辈出手帮助,谢谢。
------解决思路----------------------
继续使用QueryInterface 来查询其他的接口,前提是知道其他的IID
------解决思路----------------------
。。。。。

VC编译器有个#import指令
#import "Test.dll"   //COM组件的dll文件
然后VC编译器便会生成一个tlh文件,里面声明了该COM组件的所有接口
接下来的调用就和VB差不了多少了,根据tlh文件中的声明,用CoCreateInstance创建一个接口指针,
然后输入p->
VC的智能提示就来了,都什么成员就列出来了。。

你可以在网上多搜索一下VC调用COM组件的资料,也可以看一下诸如《深入解析ATL(第2版)》、《ATL开发指南》的PDF书
------解决思路----------------------
如果再结合上ATL中的CComPtr、CComQIPtr这些智能指针,以及CComBSTR、CComVariant这些类,调用COM接口其实是很方便的。。。
比如使用ATL智能指针后,这些复杂的参数
CoCreateInstance(clsid, NULL, CLSCTX_INPROC_SERVER,    IID_IUnknown,
QueryInterface(IID_IDispatch,
都不用管,不用写,ATL强大的模板会帮你解决。
------解决思路----------------------
我告诉你一个简单的办法。

不需要引入类型库,也可以调用。

#include <windows.h>


#define CCOM_CALL(ic, method)  ((ic)->lpVtbl->method)


extern bool __cohelper_disp_id( IDispatch * , unichar const * , DISPID * );

extern bool __cohelper_disp_invoke( IDispatch * , DISPID , u_long , u_int , VARIANT * , VARIANT * );

extern bool __cohelper_disp_call( IDispatch * , unichar const * , u_long , u_int , VARIANT * , VARIANT * );


#include "cohelper.h"


bool __cohelper_disp_id( IDispatch *dsp, unichar const *fn, DISPID *pid )
{
    HRESULT     hr;

    hr = CCOM_CALL(dsp, GetIDsOfNames(dsp, &IID_NULL, (OLECHAR **)&fn, 1, LOCALE_SYSTEM_DEFAULT, pid));
    if(FAILED(hr)) {
        cua_set_error(hr);
        return false;
    }

    return true;
}

bool __cohelper_disp_invoke( IDispatch *dsp, DISPID id, u_long mm, 
                             u_int vc, VARIANT *args, VARIANT *ret )
{
    HRESULT     hr;
    DISPID      dp;
    DISPPARAMS  pps;

    pps.cArgs = vc;
    pps.cNamedArgs = 0;
    pps.rgdispidNamedArgs = 0;
    pps.rgvarg = args;

    if(mm == DISPATCH_PROPERTYPUT && vc == 1) {
        dp = DISPID_PROPERTYPUT;
        pps.rgdispidNamedArgs = &dp;
        pps.cNamedArgs = 1;
    }

    hr = CCOM_CALL(dsp, Invoke(dsp, id, &IID_NULL, LOCALE_SYSTEM_DEFAULT, (WORD)mm, &pps, ret, 0, 0));
    if(FAILED(hr)) {
        cua_set_error(hr);
        return false;
    }

    return true;
}


bool __cohelper_disp_call( IDispatch *dsp, unichar const *fn, u_long mm,
                           u_int vc, VARIANT *args, VARIANT *ret )
{
    DISPID      id;

    return __cohelper_disp_id(dsp, fn, &id) && 
           __cohelper_disp_invoke(dsp, id, mm, vc, args, ret);
}


------解决思路----------------------
可以用oleviewer来查看所有的接口
------解决思路----------------------
你的ATL项目的“中间目录”下(VS项目默认配置,解决方案通过高版本VS加载升级了的情况可自行查找),编译成功后会生成 .tlb 文件,这个文件可以使用 import 来生成接口定义文件。

在不方便提供 .dll 文件或者 .dll更新太频繁(比如修改了bug)的情况下,你可以仅向他人提供该类型库文件,只要接口定义没有变化。


不过四楼的方法挺有意思的。收藏回家看看