IDispatch ::调用typedef struct的传递指针作为参数

时间:2017-09-15 09:31:03

标签: c windows winapi com

我需要调用函数 GetRect ,其参数是结构 EdmRect 的指针,没有 GUID

typedef struct tagEdmRect {
    long mlLeft;
    long mlTop;
    long mlRight;
    long mlBottom;
} EdmRect;

[
  odl,
  uuid(60F6AEB0-7AEA-49F5-A4EF-DCBB1F2E6284),
  helpstring("IEdmState7 Interface"),
  dual,
  oleautomation
]
interface IEdmState7 : IEdmState6 {
    [id(0x0000000e), helpstring("GetRect")]
    HRESULT GetRect([out] EdmRect* poRect);
};

请告诉我如何才能做到正确?

现在我不能这样做因为总是收到 E_INVALIDARG

首先我尝试使用 VT_RECORD 作为VARTYPE:

GUID LIBID_RecInf1Lib = { 0x5fa2c692, 0x8393, 0x4f31, { 0x9b, 0xdb, 0x05, 0xe6, 0xf8, 0x07, 0xd0, 0xd3 } };

ITypeLib* typeLib = nullptr;
hRes = LoadRegTypeLib(LIBID_RecInf1Lib, 5, 17, LOCALE_SYSTEM_DEFAULT, &typeLib);
if (FAILED(hRes))
    _com_issue_error(hRes);

uint typeCount = typeLib->GetTypeInfoCount();
for (uint i = 0; i < typeCount; ++i)
{
    BSTR name;
    typeLib->GetDocumentation(i, &name, NULL, NULL, NULL);

    if (wcscmp(name, L"EdmRect") == 0)
    {    
        ITypeInfo* typeInfo = nullptr;
        hRes = typeLib->GetTypeInfo(i, &typeInfo);
        if (FAILED(hRes))
            _com_issue_error(hRes);

        IRecordInfo* erRecInfo = nullptr;
        hRes = GetRecordInfoFromTypeInfo(typeInfo, &erRecInfo);
        qDebug() << erRecInfo;
        if (FAILED(hRes))
            _com_issue_error(hRes);

        EdmRect rect = { 0 };

        VARIANTARG* v = new VARIANTARG[1];
        VariantInit(&v[0]);
        v[0].vt = VT_RECORD;
        v[0].pvRecord = &rect;
        v[0].pRecInfo = erRecInfo;

        DISPPARAMS i_params = {v, NULL, 1, 0};

        auto func_id = dispIDofName("GetRect", disp); // This line correct
        hRes = disp->Invoke(func_id, IID_NULL, LOCALE_SYSTEM_DEFAULT, DISPATCH_METHOD, &i_params, NULL, NULL, NULL);
        if (FAILED(hRes))
            _com_issue_error(hRes);

        break;
    }
}

但是功能 GetRecordInfoFromTypeInfo 返回 E_INVALIDARG

我也尝试使用 VT_PTR VT_PTR | VT_BYREF VT_USERDEFINED 并设置指向VARIANT :: byref的指针不起作用。

请帮忙。

1 个答案:

答案 0 :(得分:1)

IDispatch仅支持自动化兼容类型。这里列出的列表或多或少:

2.2.29.1 _wireVARIANT

所以,有I8,I4,I2,I1,INT,UI8,UI4,UI2,UI1,UINT,R8,R4,CY,ERROR,BOOL,DECIMAL,NULL,EMPTY,DATE,BSTR,UNKNOWN,DISPATCH ,所有这些的BYREF和SAFEARRAY,以及RECORD(历史上最新的补充)。

就是这样。没有原始C结构,没有VT_PTR等。

其他VT_ *是为PROPVARIANT保留的,这是一个类似但非常不同的野兽,IDispatch不支持(它适用于blittable兼容类型)。

因此可以使用C结构,但必须在类型库(或/和注册表)中正确定义。如果已定义,则可以将其称为Automation UDT(用户定义的类型)。

  

通过类型库驱动传递单个UDT或UDT的安全阵列   编组用于v表绑定,C和C ++自动化客户端需要   从描述UDT的IDL生成的头文件

     

Visual Basic客户端需要从IDL生成的类型库   文件。但是最近要传递单个UDT或UDT的安全阵列   绑定,自动化客户端必须具有必要的信息   将UDT的类型信息存储到VARIANT中(如果存储的话)   后期绑定,UDT必须自我描述。)

这就是您描述UDT within .IDL files

的方式
library udttest
{
    typedef [uuid(C1D3A8C0-A4AA-11D0-819C-00A0C90FFFC3)]
    struct_tagUDT
    {
        unsigned long a1;
        BSTR pbstr;
    } UDT;
}

由于EdmRect 一个UDT(好吧,它似乎不是,但我手边没有原始的tlb来确认),你只能使用早期绑定来调用它,那样:

IEdmState7 *pState;
disp->QueryInterface(IID_IIEdmState7, (void**)&pState);
EdmRect rect;
pState->GetRect(&rect);
...

因此,您需要IEdmState7的.H头文件。如果供应商没有提供一个(恕我直言,他应该,或源.IDL),但你有一个TLB,那么所有希望都不会丢失。

一种解决方案是使用Visual Studio #import directive(每个人都可以使用社区版),保留.TLH和.TLI生成的文件并使其适应您的编译器(在这种情况下,我建议您使用raw_interfaces_only)。

另一个解决方案是使用OLE/COM Object Viewer(以管理员身份运行...)或名为OleWoo的工具,它可以替代旧版本。

它应该从.TLB创建一个.H文件。请注意,您只需要不支持IDispatch的界面,如果IDispatch对您来说很方便,则不需要整个.H文件。