Reputation: 393
Hi I'm new to C# and C++ programming and trying to use an unmanaged C++ dll in a C# project (.NET 3.5). I'm stuck on this error:
System.Runtime.InteropServices.SafeArrayTypeMismatchException: Specified array was not of the expected type.
Here is the header file for the DLL
#ifdef FUNCTIONLIB_EXPORTS
#define FUNCTIONLIB_API __declspec(dllexport)
#else
#define FUNCTIONLIB_API __declspec(dllimport)
#endif
typedef struct _FunctionOpt
{
char UserName[32];
char Password[32];
char ServerIP[128];
int ServerPort;
int Index;
char TargetSubChannel;
long Timeout;
char Filepath[256];
bool isFirst;
DWORD SyncTime;
char *pOutBuf;
int OutBufSize;
unsigned short OutImgResW;
unsigned short OutImgResH;
}STFUNCTIONOPT, *PSTFUNCTIONOPT;
FUNCTIONLIB_API int FunctionLib_Snapshot( PSTFUNCTIONOPT pstFunctionOpt );
I don't have access to the C++ code so I can only change the following C# code. My relevant code is as follows:
public unsafe struct PSTFUNCTIONOPT
{
public char[] UserName;
public char[] Password;
public char[] ServerIP;
public int ServerPort;
public int Index;
public char TargetSubChannel;
public long Timeout;
public char[] Filepath;
public bool isFirst;
public uint SyncTime;
public char *pOutBuf; // example C++ usage: myStruct.pOutBuf = (char*)malloc(1920 * 1080 * 3);
public int OutBufSize;
public ushort OutImgResW;
public ushort OutImgResH;
}
// need to use dumpbin to get entrypoint name due to c++ mangling
[DllImport(@"C:\Location\To\Project\bin\FunctionLibLib.dll", EntryPoint = "?FunctionLib_Snapshot@@YAHPAU_FunctionOpt@@@Z")]
public static extern int FunctionLib_Snapshot(PSTFUNCTIONOPT pstFunctionOpt);
public unsafe int FunctionLib_Run()
{
PSTFUNCTIONOPT stFunctionOpt = new PSTFUNCTIONOPT();
stFunctionOpt.UserName = ("uname").ToCharArray();
stFunctionOpt.Password = ("pword").ToCharArray();
stFunctionOpt.ServerIP = ("192.168.1.1").ToCharArray();
stFunctionOpt.ServerPort = 80;
stFunctionOpt.Index = 255;
stFunctionOpt.Timeout = 15000;
stFunctionOpt.Filepath = ("c:\\temp\\test.jpg").ToCharArray();
stFunctionOpt.isFirst = true;
stFunctionOpt.SyncTime = 0;
//stFunctionOpt.pOutBuf = new char*[10000]; // not sure how to do this yet
stFunctionOpt.OutBufSize = 10000;
// get result from DLL
return FunctionLib_Snapshot(stFunctionOpt);
}
How do I properly pass the struct to this unmanaged DLL? The error seems like something simple but I haven't been able to narrow down the problem. Any help is appreciated!
Upvotes: 3
Views: 3772
Reputation: 942119
Several problems:
public char[] UserName;
That needs to be marshaled as an embedded string:
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)]
public string UserName;
Repeat that for the other fields that are declared like this.
public char *pOutBuf; // example C++ usage: myStruct.pOutBuf = (char*)malloc(1920 * 1080 * 3);
It is not clear whether the C++ function allocates that buffer or the client code is supposed to do that. If it is the former then you have a Big Problem, you cannot call the free() function to release the buffer again. Your only hope is that your C# code is supposed to allocate it, not unlikely. In which case it is:
public byte[] pOutBuf;
...
stFunctionOpt.pOutBuf = new byte[10000];
stFunctionOpt.OutBufSize = stFunctionOpt.pOutBuf.Length;
The pinvoke declaration is wrong:
[DllImport(@"C:\Location\To\Project\bin\FunctionLibLib.dll", EntryPoint = "?FunctionLib_Snapshot@@YAHPAU_FunctionOpt@@@Z")]
public static extern int FunctionLib_Snapshot(PSTFUNCTIONOPT pstFunctionOpt);
The argument is ref PSTFUNCTIONOPT pstFunctionOpt
. Do drop the P from the structure name, it is not a pointer type. Avoid hard-coding the path to the DLL, that isn't going to work on your user's machine. Just make sure that you have a copy of the DLL in your build output directory.
Upvotes: 4
Reputation: 391
I think you are missing a couple of things from your structure definition
[StructLayout(LayoutKind.Sequential)]
attribute to your structure[MarshalAs(UnmanagedType.ByValTStr, SizeConst = your_array_lenght)]
, moreover if you do that, the marshaler will handle conversions between string and char array.Your struct definition should then look something like
[StructLayout(LayoutKind.Sequential)]
public struct PSTFUNCTIONOPT
{
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)]
public string UserName;
...
[MarshalAs(UnmanagedType.LPStr)]
public string pOutBuf;
}
For more info check out http://msdn.microsoft.com/en-us/library/s9ts558h.aspx
Upvotes: 1