C ++ Struct in C #
I am using a DLL written in C ++ in my C # project using DllImport and one of the functions I am using looks like this:
[DllImport("dds.dll", CharSet = CharSet.Auto)]
private static extern int Par(
ddTableResults2 tableResult,
ref parResults ParResult,
int vul
);
The parResults structure is defined in C ++ as follows:
struct parResults {
/* index = 0 is NS view and index = 1
is EW view. By 'view' is here meant
which side that starts the bidding. */
char parScore[2][16];
char parContractsString[2][128];
};
C ++ function start
int STDCALL Par(struct ddTableResults * tablep, struct parResults *presp,
int vulnerable)
How can I define the above structure in C # to send this structure as an en reference to a DLL function?
This is what I've tried but doesn't work at all and I just get an access violation error.
[StructLayout(LayoutKind.Sequential)]
public struct parResults
{
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 32)]
public char[,] parScore;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 256)]
public char[,] parContractsString;
public parResults(int x)
{
parScore = new char[2,16];
parContractsString = new char[2,128];
}
}
source to share
This is a pretty tricky structure for a marshal in C #. There are various ways to try it, but I think it would be cleanest to represent character arrays as byte and marshal arrays to and from strings manually. Here's a demo of what I mean:
C ++
#include <cstring>
struct parResults {
char parScore[2][16];
char parContractsString[2][128];
};
extern "C"
{
__declspec(dllexport) void foo(struct parResults *res)
{
strcpy(res->parScore[0], res->parContractsString[0]);
strcpy(res->parScore[1], res->parContractsString[1]);
}
}
FROM#
using System;
using System.Text;
using System.Runtime.InteropServices;
namespace ConsoleApplication1
{
class Program
{
[StructLayout(LayoutKind.Sequential)]
class parResults
{
private const int parScoreCount = 2;
private const int parScoreLen = 16;
private const int parContractsStringCount = 2;
private const int parContractsStringLen = 128;
[MarshalAs(UnmanagedType.ByValArray,
SizeConst = parScoreCount * parScoreLen)]
private byte[] parScoreBuff;
[MarshalAs(UnmanagedType.ByValArray,
SizeConst = parContractsStringCount * parContractsStringLen)]
private byte[] parContractsStringBuff;
public string getParScore(int index)
{
string str = Encoding.Default.GetString(parScoreBuff,
index * parScoreLen, parScoreLen);
int len = str.IndexOf('\0');
if (len != -1)
str = str.Substring(0, len);
return str;
}
public void setParScore(int index, string value)
{
byte[] bytes = Encoding.Default.GetBytes(value);
int len = Math.Min(bytes.Length, parScoreLen);
Array.Copy(bytes, 0, parScoreBuff, index * parScoreLen, len);
Array.Clear(parScoreBuff, index * parScoreLen + len,
parScoreLen - len);
}
public string parContractsString(int index)
{
string str = Encoding.Default.GetString(parContractsStringBuff,
index * parContractsStringLen, parContractsStringLen);
int len = str.IndexOf('\0');
if (len != -1)
str = str.Substring(0, len);
return str;
}
public void setParContractsString(int index, string value)
{
byte[] bytes = Encoding.Default.GetBytes(value);
int len = Math.Min(bytes.Length, parContractsStringLen);
Array.Copy(bytes, 0, parContractsStringBuff,
index * parContractsStringLen, len);
Array.Clear(parContractsStringBuff,
index * parContractsStringLen + len,
parContractsStringLen - len);
}
public parResults()
{
parScoreBuff = new byte[parScoreCount * parScoreLen];
parContractsStringBuff =
new byte[parContractsStringCount * parContractsStringLen];
}
};
[DllImport(@"...", CallingConvention = CallingConvention.Cdecl)]
static extern void foo([In,Out] parResults res);
static void Main(string[] args)
{
parResults res = new parResults();
res.setParContractsString(0, "foo");
res.setParContractsString(1, "bar");
foo(res);
Console.WriteLine(res.getParScore(0));
Console.WriteLine(res.getParScore(1));
Console.ReadLine();
}
}
}
Here I have used a class to represent the structure. Since a class in C # is a reference, we do not declare parameters of this type with ref
. I also used it __cdecl
for convenience so that there was no need to figure out what the decorated function name would be. But your library used __stdcall
, so you need to stick with that.
The class demonstrates sending data in both directions. You could probably simplify the code if the data flow was more limited.
source to share