IDispatch::Invoke pass pointer of typedef struct as argument

0

I need call function GetRect whose argument is pointer of structure EdmRect which has no 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);
};

Please tell me how i can do it correct?

Now i can't do it because always received E_INVALIDARG.

First i tried use VT_RECORD as 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;
    }
}

But function GetRecordInfoFromTypeInfo return E_INVALIDARG

Also i tried use VT_PTR and VT_PTR | VT_BYREF and VT_USERDEFINED and set pointer to VARIANT::byref, isn't worked.

Please help.

c
windows
winapi
com
asked on Stack Overflow Sep 15, 2017 by lirik90

1 Answer

1

IDispatch only supports Automation compatible types. The list more or less described here:

2.2.29.1 _wireVARIANT

So, there are I8, I4, I2, I1, INT, UI8, UI4, UI2, UI1, UINT, R8, R4, CY, ERROR, BOOL, DECIMAL, NULL, EMPTY, DATE, BSTR, UNKNOWN, DISPATCH, BYREFs and SAFEARRAYs of all these, and RECORD (which was the latest addition historically).

That's it. No raw C structure, no VT_PTR, etc.

Other VT_* are reserved for PROPVARIANT which is a similar, but quite different beast, not supported by IDispatch (it works for blittable-compatible types).

So you can use a C structure, but it must be properly defined in type library (or/and in registry). If it's defined, you can call it an Automation UDT (user defined type).

To pass single UDTs or safearrays of UDTs through type-library driven marshaling for v-table binding, C and C++ Automation clients need the header file generated from the IDL that describes the UDT

Visual Basic clients require the type library generated from the IDL file. However to pass single UDTs or safearrays of UDTs for late binding, the Automation client must have the information necessary to store the type information of the UDT into a VARIANT (and if late-binding, the UDT must be self-described).

This is how you describe UDT within .IDL files

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

Since EdmRect is not a UDT (well, it appears it's not, but I don't have the original tlb at hand to confirm), you can only call it using early-binding, something like that:

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

So you need a .H header file for IEdmState7. If the vendor does not provide one (IMHO, he should, or the source .IDL), but you have a TLB, then all hope is not lost.

One solution is to use Visual Studio #import directive (there's the community edition that everyone can use), keep the .TLH and .TLI generated files and adapt them to your compiler (in this case, I suggest you use raw_interfaces_only).

Another solution is to use OLE/COM Object Viewer (run as admin...) or a tool called OleWoo wich is a nice replacement for the older one.

It should create a .H file from the.TLB. Note you only need the interface that don't support IDispatch, you don't need the whole .H file, if IDispatch is convenient for you.

answered on Stack Overflow Sep 15, 2017 by Simon Mourier • edited Sep 15, 2017 by Simon Mourier

User contributions licensed under CC BY-SA 3.0