7

ここで何が問題なのかわかりません。

何事もなく機能しているp/invoke呼び出しがたくさんあります...これを除いて。

私は問題を次のサンプルコードに減らすことができました。

いずれかの構造体メンバー(doubleまたはint)を削除すると、正常に機能します。

問題は構造体のレイアウトに何らかの形で関連していると思いますが、Cでsizeof()を実行し、C#でMarshal.SizeOf()を実行すると、両方とも同じ値を返します...したがって、構造体のサイズがC#とCで同じですが、問題は何でしょうか?

私は明らかにここで基本的な何かが欠けています。

SampleDLLCode.c

#pragma pack(1)

typedef struct SampleStruct {
    double structValueOne;
    int structValueTwo;
} SampleStruct;

__declspec(dllexport) SampleStruct __cdecl SampleMethod(void);
SampleStruct SampleMethod(void) { 
    return (SampleStruct) { 1, 2 };
}

スクリプトを作成する

gcc -std=c99 -pedantic -O0 -c -o SampleDLLCode.o SampleDLLCode.c
gcc -shared --out-implib -o SampleDLL.dll SampleDLLCode.o 

C#コード

using System;
using System.Runtime.InteropServices;

namespace SampleApplication
{
    [StructLayout(LayoutKind.Sequential, Pack=1)]
    public struct SampleStruct {
        public double structValueOne;
        public int structValueTwo;
    } 

    class Program
    {
        [DllImport("SampleDLL.dll", CallingConvention = CallingConvention.Cdecl)]
        public static extern SampleStruct SampleMethod();

        static void Main(string[] args)
        {
            SampleStruct sample = SampleMethod();
        }
    }
}
4

1 に答える 1

9

まず、よくある質問をおめでとうございます。問題を再現するために必要なすべてのコードを受け取ったことは、かつては嬉しかったことです。

この問題は、関数の戻り値にgccツールとMicrosoftツールで使用されるABIがわずかに異なることが原因です。レジスタに収まる戻り値、たとえばint戻り値の場合、違いはありません。ただし、構造体が大きすぎて1つのレジスタに収まらないため、その状況ではAPI間に違いがあります。

戻り値が大きい場合、呼び出し元は非表示のポインターを関数に渡します。この隠しポインターは、呼び出し元によってスタックにプッシュされます。この関数は、その隠しポインタで指定されたメモリアドレスに戻り値を書き込みます。ABIの違いは、誰がその隠しポインターをスタックからポップするかということです。MicrosoftツールはABIを使用しており、呼び出し元は非表示のポインターをポップする必要がありますが、デフォルトのgccABIは呼び出し先にそれを行うように要求します。

現在、gccはほぼ無限に構成可能であり、ABIを制御できるようにするスイッチがあります。また、gccにMicrosoftツールと同じルールを使用させることができます。そのためには、callee_pop_aggregate_return関数属性が必要です。

Cコードを次のように変更します。

__declspec(dllexport) SampleStruct __cdecl SampleMethod(void) 
    __attribute__((callee_pop_aggregate_return(0)));
    // specifies that caller is responsible for popping the hidden pointer

SampleStruct SampleMethod(void) { 
    return (SampleStruct) { 1, 2 };
}
于 2013-03-24T11:41:38.633 に答える