2013-03-24 2 views
7

Ich habe keine Ahnung, was das Problem hier ist.PInvokeStackImbalance nicht durch CallingConvention

Ich habe eine Tonne p/invoke Anrufe, die ohne Vorfall arbeiten ... außer diesem.

Ich habe es geschafft, mein Problem auf den folgenden Beispielcode zu reduzieren.

Wenn ich entweder Strukturelement (entweder das Double oder das Int) entfernt, funktioniert es gut.

Ich nehme an, das Problem ist irgendwie mit dem Layout der Struktur verbunden - aber wenn ich eine sizeof() in C und eine Marshal.SizeOf() in C#, sie beide geben den gleichen Wert zurück ... so Wenn die Strukturgröße in C# und C gleich ist, was könnte das Problem sein?

Ich vermisse hier offensichtlich etwas Grundlegendes.

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 }; 
} 

Build-Skript

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

C# -Code

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(); 
     } 
    } 
} 
+0

Nichts springt ... für das Heck von ihm, versuchen Sie die Reihenfolge der Felder in der Struktur tauschen? (beide Seiten, natürlich) – JerKimball

+0

@JerKimball Danke fürs schauen. Das Vertauschen der Reihenfolge der Felder in der Struktur hat keine Auswirkungen. Ich bekomme immer noch die PInvokeStackImbalance MDA Warnung. – Steve

+0

Könnte das ein Problem zwischen GCC vs .NET und dem Doppeltyp sein? Die Rückgabe einer Struktur mit cdecl kann zu compilerabhängigen Problemen führen. Überprüfen Sie dies http://stackoverflow.com/questions/13786192/methods-type-signature-is-not-pinvoke-compatible-while-calling-dll-method –

Antwort

9

Vor allem le Ich gratuliere Ihnen zu einer sehr gut gestellten Frage. Es war eine Freude, einmal den gesamten Code zu erhalten, der zur Reproduktion des Problems benötigt wurde.

Das Problem liegt an den etwas anderen ABIs, die von GCC- und Microsoft-Tools für Funktionsrückgabewerte verwendet werden. Für Rückgabewerte, die in Register passen, zB int Rückgabewerte gibt es keine Unterschiede. Aber da Ihre Struktur zu groß ist, um in ein einzelnes Register zu passen, gibt es in dieser Situation Unterschiede zwischen den APIs.

Für größere Rückgabewerte übergibt der Aufrufer einen versteckten Zeiger auf die Funktion. Dieser versteckte Zeiger wird vom Aufrufer auf den Stack geschoben. Die Funktion schreibt den Rückgabewert in die Speicheradresse, die von diesem versteckten Zeiger angegeben wird. Der Unterschied in den ABIs liegt darin, wer den versteckten Zeiger vom Stapel löscht. Die Microsoft-Tools verwenden eine ABI, bei der der Aufrufer den ausgeblendeten Zeiger aufrufen muss. Der standardmäßige gcc ABI fordert den Aufrufer jedoch auf, dies zu tun.

Jetzt, gcc ist fast unendlich konfigurierbar, gibt es einen Schalter, mit dem Sie den ABI steuern können. Und Sie können gcc die gleichen Regeln wie die Microsoft-Tools verwenden. Dies erfordert die callee_pop_aggregate_return function attribute.

Ihren C-Code ändern, so zu sein:

__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 }; 
} 
+2

Bloody gut beantwortet - und untersucht! – JerKimball

+0

Ich liebe dich. Vielen Dank. Du bist mein Held! – Steve

+0

David, schnelle Folgefrage; Ist es sicher, das callee_pop_aggregate_return-Attribut für ALLE meine exportierten Funktionen für meine Windows-Version zu verwenden - oder sollte ich es NUR verwenden, wenn es tatsächlich anwendbar ist? Ich frage, weil es einfacher ist, am Ende meiner Deklarationen immer ein Precompiler-Makro hinzuzufügen, anstatt zu bestimmen, wo genau ich es brauche - wenn es also sicher ist, wird es einfacher. – Steve

Verwandte Themen