Marshal a C# struct to C++ VARIANT
- by jortan
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;