Marshal a C# struct to C++ VARIANT

Posted by jortan on Stack Overflow See other posts from Stack Overflow or by jortan
Published on 2013-10-31T15:06:35Z Indexed on 2013/11/05 15:54 UTC
Read the original article Hit count: 346

Filed under:
|
|
|

To start with, I'm not very familiar with the COM-technology, this is the first time I'm working with it so bear with me. I'm trying to call a COM-object function from C#.

This is the interface in the idl-file:

[id(6), helpstring("vConnectInfo=ConnectInfoType")] 
HRESULT ConnectTarget([in,out] VARIANT* vConnectInfo);

This is the interop interface I got after running tlbimp:

    void ConnectTarget(ref object vConnectInfo);

The c++ code in COM object for the target function:

STDMETHODIMP PCommunication::ConnectTarget(VARIANT* vConnectInfo)
{
    if (!((vConnectInfo->vt & VT_ARRAY) && (vConnectInfo->vt & VT_BYREF)))
    {
        return E_INVALIDARG;
    }

    ConnectInfoType *pConnectInfo = (ConnectInfoType *)((*vConnectInfo->pparray)->pvData);
...
}

This COM-object is running in another process, it is not in a dll.

I can add that the COM object is also used from another program written in C++. In that case there is no problem because in C++ a VARIANT is created and pparray->pvData is set to the connInfo data-structure and then the COM-object is called with the VARIANT as parameter.

In C#, as I understand, my struct should be marshalled as a VARIANT automatically.

These are two methods I've been using (or actually I've tried a lot more...) to call this method from C#:

    private void method1_Click(object sender, EventArgs e)
    {
        pcom.PCom PCom = new pcom.PCom();
        pcom.IGeneralManagementServices mgmt = (pcom.IGeneralManagementServices)PCom;
        m_ci = new ConnectInfoType();
        fillConnInfo(ref m_ci);
        mgmt.ConnectTarget(m_ci);
    }

In the above case the struct gets marshalled as VT_UNKNOWN. This is a simple case and works if the parameter is not a struct (eg. works for int).

    private void method4_Click(object sender, EventArgs e)
    {
        ConnectInfoType ci = new ConnectInfoType();
        fillConnInfo(ref ci);

        pcom PCom = new pcom.PCom();
        pcom.IGeneralManagementServices mgmt = (pcom.IGeneralManagementServices)PCom;

        ParameterModifier[] pms = new ParameterModifier[1];
        ParameterModifier pm = new ParameterModifier(1);
        pm[0] = true;
        pms[0] = pm;
        object[] param = new object[1];
        param[0] = ci;
        object[] args = new object[1];
        args[0] = param;
        mgmt.GetType().InvokeMember("ConnectTarget", BindingFlags.InvokeMethod, null, mgmt, args, pms, null, null);
    }

In this case it gets marshalled as VT_ARRAY | VT_BYREF | VT_VARIANT. The problem is that when debugging the "target-function" ConnectTarget I cannot find the data I send in the SAFEARRAY-struct (or in any other place in memory either)

What do I do with a VT_VARIANT?

Any ideas on how to get my struct-data?

Update:

The ConnectInfoType struct:

    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
    public class ConnectInfoType
    {
        public short network;
        public short nodeNumber;
        [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 51)]
        public string connTargPassWord;
        [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 8)]
        public string sConnectId;
        [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 16)]
        public string sConnectPassword;
        public EnuConnectType eConnectType;
        public int hConnectHandle;
        [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 8)]
        public string sAccessPassword;
    };

And the corresponding struct in c++:

typedef struct ConnectInfoType
{
    short network;
    short nodeNumber;
    char connTargPassWord[51];
    char sConnectId[8];
    char sConnectPassword[16];
    EnuConnectType eConnectType;
    int hConnectHandle;
    char sAccessPassword[8];
} ConnectInfoType;

© Stack Overflow or respective owner

Related posts about c#

Related posts about c++