簡體   English   中英

為什么 C++ 從 C# 獲取錯誤的參數值,因為 function 返回自定義結構

[英]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語句上的斷點被命中時正確接收-85 ,但Compose接收5710724189

此外,作為一個方面,不可變結構NumberContainer無法初始化引發write access violation. 一旦Number1(number1)被命中就會出現異常——盡管這是在收到錯誤值之后,所以這可能與我試圖解決的第一個問題無關。

如果我翻轉調用順序, Compose會收到51817521252 ,但由於上述異常,我無法繼續查看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更改為32128並進行重建,它會奇怪地繼續傳遞正確的參數值! 但是,如果屬性中省略了Size ,則參數值會像以前一樣錯誤地輸入。

我似乎已經解決了最初的問題,並且不可變結構成功實例化並從 C++ 代碼返回,但上述關於Size參數的謎團仍未解開。 如果有人對此有答案,那么我會很樂意接受它而不是我自己的。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM