将 C# 结构传递给 C++ 非托管 DLL 返回不正确的结果
Passing C# struct to C++ unmanaged DLL returning incorrect result
我有一个在 visual studio 2017 年开发并在 64 位环境中编译的简单 C++ win32 DLL,代码如下:
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;
}
}
上面的代码包含一个方法,该方法通过将 typedef 结构作为参数来 returns 两个整数的总和。
我有一个 C# 客户端应用程序,它使用 PInvoke 使用此 Win32 C++ DLL。以下是我的 C# 客户端应用程序的代码:
[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);
}
}
使用上面的代码,我期望输出“12”,但我得到的是“-1504178328”作为输出。
我是一名完全没有 C++ 经验的 C# 开发人员。请帮我解决这个问题。
使用更简单的 P/Invoke 包装器:
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;
}
}
并像这样使用它:
void CSharpExample()
{
LibWrap.Nums nums;
nums.a = 6;
nums.b = 7;
int res = LibWrap.Initialize(ref nums);
Console.WriteLine(res);
}
在您的示例中,您不需要任何内存分配和封送处理,因为:
LibWrap.Nums
是一个结构,因此 CSharpExample()
中的局部变量 nums
完全分配在堆栈上。
- 通过
ref
将托管结构 LibWrap.Nums
传递给 LibWrap.Initialize
会将指针传递给堆栈上的局部变量 nums
。
LibWrap.Initialize
被同步调用,因此您传递给它的指针在 LibWrap.Initialize
函数退出后不会在任何地方使用。这很重要,因为一旦 CSharpExample()
退出,指针就会失效。
在 C# 方面,您没有正确处理嵌套结构。试试这个:
[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);
}
}
也就是说,拥有一个其唯一数据成员是另一个结构的结构是多余且不必要的。您应该完全删除外部结构,例如:
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);
}
}
我有一个在 visual studio 2017 年开发并在 64 位环境中编译的简单 C++ win32 DLL,代码如下:
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;
}
}
上面的代码包含一个方法,该方法通过将 typedef 结构作为参数来 returns 两个整数的总和。
我有一个 C# 客户端应用程序,它使用 PInvoke 使用此 Win32 C++ DLL。以下是我的 C# 客户端应用程序的代码:
[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);
}
}
使用上面的代码,我期望输出“12”,但我得到的是“-1504178328”作为输出。
我是一名完全没有 C++ 经验的 C# 开发人员。请帮我解决这个问题。
使用更简单的 P/Invoke 包装器:
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;
}
}
并像这样使用它:
void CSharpExample()
{
LibWrap.Nums nums;
nums.a = 6;
nums.b = 7;
int res = LibWrap.Initialize(ref nums);
Console.WriteLine(res);
}
在您的示例中,您不需要任何内存分配和封送处理,因为:
LibWrap.Nums
是一个结构,因此CSharpExample()
中的局部变量nums
完全分配在堆栈上。- 通过
ref
将托管结构LibWrap.Nums
传递给LibWrap.Initialize
会将指针传递给堆栈上的局部变量nums
。 LibWrap.Initialize
被同步调用,因此您传递给它的指针在LibWrap.Initialize
函数退出后不会在任何地方使用。这很重要,因为一旦CSharpExample()
退出,指针就会失效。
在 C# 方面,您没有正确处理嵌套结构。试试这个:
[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);
}
}
也就是说,拥有一个其唯一数据成员是另一个结构的结构是多余且不必要的。您应该完全删除外部结构,例如:
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);
}
}