[英]Passing struct containing fixed sized array from c# to c++ to be populated is returning odd values
[英]Why is C++ getting wrong parameter values from C# for a function returning a custom struct
假设我有以下 C# 控制台应用程序调用 C++ 库。
internal static class Program
{
private struct NumberContainer
{
internal int Number1 { get; }
internal uint Number2 { get; }
internal NumberContainer(int number1, uint number2)
{
Number1 = number1;
Number2 = number2;
}
public override string ToString()
=> $"{Number1}, {Number2}";
}
[DllImport("CPlusPlusTargetLibrary.dll", CallingConvention = CallingConvention.Cdecl)]
private static extern int Add(int number1, uint number2);
[DllImport("CPlusPlusTargetLibrary.dll", CallingConvention = CallingConvention.Cdecl)]
private static extern NumberContainer Compose(int signedInput, uint unsignedInput);
private static void Main(string[] args)
{
var unsignedInput = args.Any() && uint.TryParse(args.First(), out uint unsignedValue) ? unsignedValue : 0;
var signedInput = args.Count() > 1 && int.TryParse(args.ElementAt(1), out int signedValue) ? signedValue : 0;
Console.WriteLine("Adding {0} and {1}...", signedInput, unsignedInput);
Console.WriteLine("Result = {0}", Add(signedInput, unsignedInput));
Console.WriteLine("Composing {0} and {1}...", signedInput, unsignedInput);
Console.WriteLine("Composed {0}", Compose(signedInput, unsignedInput));
}
}
以及 CPlusPlusTargetLibrary.dll 中的以下CPlusPlusTargetLibrary.dll
文件:
#pragma once
struct NumberContainer
{
const signed long Number1;
const unsigned long Number2;
NumberContainer(const signed long number1, const unsigned long number2)
: Number1(number1),
Number2(number2)
{
}
};
extern "C" __declspec(dllexport) signed long __cdecl Add
(
signed long number1,
unsigned long number2
);
extern "C" __declspec(dllexport) NumberContainer __cdecl Compose
(
signed long signedInput,
unsigned long unsignedInput
);
以及该 header 的以下相应 CPP 文件:
#include "pch.h"
#include "TargetFunctions.h"
extern "C"
{
__declspec(dllexport) signed long __cdecl Add(signed long number1, unsigned long number2)
{
return number1 + number2;
}
__declspec(dllexport) NumberContainer __cdecl Compose(signed long number1, unsigned long number2)
{
return NumberContainer
(
number1,
number2
);
}
}
给定5
作为 C# 中的unsignedInput
和-8
作为signedInput
,为什么Add
在其 C++ 实现中的return
语句上的断点被命中时正确接收-8
和5
,但Compose
接收5
和710724189
?
此外,作为一个方面,不可变结构NumberContainer
无法初始化引发write access violation.
一旦Number1(number1)
被命中就会出现异常——尽管这是在收到错误值之后,所以这可能与我试图解决的第一个问题无关。
如果我翻转调用顺序, Compose
会收到5
和1817521252
,但由于上述异常,我无法继续查看Add
收到的内容。
通过调试,我发现在任一实现的左大括号上放置断点都会显示不正确的参数值; 在此行之后,参数值对于Add
都是正确的,但对于Compose
,如果它是第二个参数,则第一个参数始终正确,而第二个参数似乎是 memory 中的某个随机值。 I'm thinking something's offset the arguments so that the first one is being discarded somewhere between the C# and C++ but what is doing that, I am not sure (I've also tried using __stdcall
with CallingConvention.StdCall
and CallingConvention.Winapi
both to相同的最终结果)。
在这篇文章的帮助下,我找到了答案: https://www.codeproject.com/Articles/66243/Marshaling-with-C-Chapter-3-Marshaling-Compound-Ty
我所要做的就是用这样的StructLayoutAttribute
标记我的 C# NumberContainer
结构,然后参数值开始正确输入!
[StructLayout(LayoutKind.Sequential, Size = 64)]
private struct NumberContainer
{
internal int Number1 { get; }
internal uint Number2 { get; }
internal NumberContainer(int number1, uint number2)
{
Number1 = number1;
Number2 = number2;
}
public override string ToString()
=> $"{Number1}, {Number2}";
}
我相信之前发生的事情是结构被 C++ 假定为 32 位,因此-8
的第一个参数(一个 32 位有符号整数)构成了默认 64 位NumberContainer
结构的第二半C++ 侧。 但是,话虽如此:如果我将Size
更改为32
或128
并进行重建,它会奇怪地继续传递正确的参数值! 但是,如果属性中省略了Size
,则参数值会像以前一样错误地输入。
我似乎已经解决了最初的问题,并且不可变结构成功实例化并从 C++ 代码返回,但上述关于Size
参数的谜团仍未解开。 如果有人对此有答案,那么我会很乐意接受它而不是我自己的。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.