Reputation: 55
I have a simple C++ win32 DLL developed in visual studio 2017 and compiled in 64 bit environment having the following code:
typedef struct sum {
struct {
int num1;
int num2;
} nums;
} sum1;
extern "C" {
__declspec(dllexport) int initialize(sum1 *summing)
{
int res;
res = summing->nums.num1 + summing->nums.num2;
return res;
}
}
The above code contains a method which returns the sum of two integers by taking a typedef struct as an argument.
I have a C# client application which consumes this Win32 C++ DLL using PInvoke. Following is the code of my C# client application:
[StructLayout(LayoutKind.Sequential)]
public struct nums
{
public int a;
public int b;
}
[StructLayout(LayoutKind.Sequential)]
public struct mydef
{
public IntPtr sum;
}
public class LibWrap
{
[DllImport("C++.dll", EntryPoint = "initialize")]
public static extern int Initialize(ref mydef mydef);
}
class Program
{
static void Main(string[] args)
{
mydef mydef = new mydef();
nums nums;
nums.a = 6;
nums.b = 6;
IntPtr buffer1 = Marshal.AllocCoTaskMem(Marshal.SizeOf(nums));
Marshal.StructureToPtr(nums, buffer1, false);
mydef.sum = buffer1;
int res = LibWrap.Initialize(ref mydef);
Console.WriteLine(res);
}
}
With the above code, I am expecting '12' as output, but instead I am getting '-1504178328' as output.
I am a C# developer with no experience in C++ at all. Please help me to solve this problem.
Upvotes: 1
Views: 1403
Reputation: 595762
On the C# side, you are not handling the nested struct correctly. Try this instead:
[StructLayout(LayoutKind.Sequential)]
public struct mynums {
public int num1;
public int num2;
}
[StructLayout(LayoutKind.Sequential)]
public struct sum1 {
public mynums nums;
}
public class LibWrap {
[DllImport("C++.dll", EntryPoint = "initialize")]
public static extern int Initialize(ref sum1 summing);
}
class Program {
static void Main(string[] args) {
sum1 mysum;
mysum.nums.num1 = 6;
mysum.nums.num2 = 6;
int res = LibWrap.Initialize(ref mysum);
Console.WriteLine(res);
}
}
That being said, having a struct whose sole data member is another struct is redundant and unnecessary. You should remove the outer struct altogether, eg:
struct nums {
int num1;
int num2;
};
extern "C" {
__declspec(dllexport) int initialize(nums *summing) {
return summing->num1 + summing->num2;
}
}
[StructLayout(LayoutKind.Sequential)]
public struct nums {
public int num1;
public int num2;
}
public class LibWrap {
[DllImport("C++.dll", EntryPoint = "initialize")]
public static extern int Initialize(ref nums summing);
}
class Program {
static void Main(string[] args) {
nums mynums;
mynums.num1 = 6;
mynums.num2 = 6;
int res = LibWrap.Initialize(ref mynums);
Console.WriteLine(res);
}
}
Upvotes: 0
Reputation: 8498
Use a simpler P/Invoke wrapper:
public static class LibWrap
{
[DllImport("C++.dll", EntryPoint = "initialize")]
public static extern int Initialize(ref Nums nums);
[StructLayout(LayoutKind.Sequential)]
public struct Nums
{
public int a;
public int b;
}
}
and use it like this:
void CSharpExample()
{
LibWrap.Nums nums;
nums.a = 6;
nums.b = 7;
int res = LibWrap.Initialize(ref nums);
Console.WriteLine(res);
}
In your example, you don't need any memory allocation and marshaling, because:
LibWrap.Nums
is a struct, thus local variable nums
in CSharpExample()
is allocated completely on stack. LibWrap.Nums
by ref
to LibWrap.Initialize
will pass the pointer to local variable nums
on stack. LibWrap.Initialize
is called synchronously, so that the pointer you pass to it isn't used anywhere after LibWrap.Initialize
function exits. This is important because the pointer becomes invalid as soon as CSharpExample()
exits.Upvotes: 2