[英]Why is C++ getting wrong parameter values from C# for a function returning a custom struct
Suppose I have the following C# Console application calling a C++ library.假设我有以下 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));
}
}
And the following header file in CPlusPlusTargetLibrary.dll
:以及 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
);
And the following corresponding CPP file for that header:以及该 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
);
}
}
Given 5
as the unsignedInput
in C# and -8
as the signedInput
, why is it that Add
correctly receives -8
and 5
when the breakpoint on the return
statement in its C++ implementation is hit, but Compose
receives 5
and 710724189
?给定5
作为 C# 中的unsignedInput
和-8
作为signedInput
,为什么Add
在其 C++ 实现中的return
语句上的断点被命中时正确接收-8
和5
,但Compose
接收5
和710724189
?
Also, as a side, the immutable struct NumberContainer
fails to initialize throwing a write access violation.
此外,作为一个方面,不可变结构NumberContainer
无法初始化引发write access violation.
exception once Number1(number1)
is hit - albeit this is after the faulty values have been received so this probably isn't relevant to the 1st problem I'm trying to resolve.一旦Number1(number1)
被命中就会出现异常——尽管这是在收到错误值之后,所以这可能与我试图解决的第一个问题无关。
If I flip the call order, Compose
then receives 5
and 1817521252
but I cannot proceed any further to see what Add
receives due to the aforementioned exception.如果我翻转调用顺序, Compose
会收到5
和1817521252
,但由于上述异常,我无法继续查看Add
收到的内容。
From debugging, I have found that placing a breakpoint on the opening curly brace of either implementation shows incorrect parameter values;通过调试,我发现在任一实现的左大括号上放置断点都会显示不正确的参数值; after this line, the parameter values are both correct for Add
but for Compose
the 1st parameter is consistently correct if it were the 2nd parameter whilst the 2nd parameter appears to be some random value in memory.在此行之后,参数值对于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 the same end result). 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相同的最终结果)。
With the help of this article I found the answer: https://www.codeproject.com/Articles/66243/Marshaling-with-C-Chapter-3-Marshaling-Compound-Ty在这篇文章的帮助下,我找到了答案: https://www.codeproject.com/Articles/66243/Marshaling-with-C-Chapter-3-Marshaling-Compound-Ty
All I had to do was mark my C# NumberContainer
struct with the StructLayoutAttribute
like this, and the parameter values started coming in correctly!我所要做的就是用这样的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}";
}
I believe what was happening before was the struct was being assumed to be 32-bit by C++ and thus the first parameter of -8
(a 32-bit signed integer) was making up the 2nd half of the default 64-bit NumberContainer
struct on the C++ side.我相信之前发生的事情是结构被 C++ 假定为 32 位,因此-8
的第一个参数(一个 32 位有符号整数)构成了默认 64 位NumberContainer
结构的第二半C++ 侧。 However, having said this: if I change Size
to 32
or 128
and do a rebuild, it weirdly continues to pass the correct parameter values in!但是,话虽如此:如果我将Size
更改为32
或128
并进行重建,它会奇怪地继续传递正确的参数值! If Size
is omitted from the attribute though, then the parameter values come in incorrectly as before.但是,如果属性中省略了Size
,则参数值会像以前一样错误地输入。
I seem to have resolved the original problem and the immutable struct successfully instantiates and returns from the C++ code, but the aforementioned mystery about the Size
parameter is still unsolved.我似乎已经解决了最初的问题,并且不可变结构成功实例化并从 C++ 代码返回,但上述关于Size
参数的谜团仍未解开。 If someone has an answer to this then I'll happily accept it over my own.如果有人对此有答案,那么我会很乐意接受它而不是我自己的。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.