See the question and my original answer on StackOverflow

IDispatch only supports Automation compatible types. The list more or less described here: _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)]
        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;

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.