2013-03-24 9 views
7

Nie mam pojęcia, na czym polega problem.PInvokeStackImbalance nie powodowane przez CallingConvention

Mam mnóstwo połączeń p/invoke, które działają bez incydentów ... z wyjątkiem tego.

Udało mi się ograniczyć mój problem do następującego kodu przykładowego.

Jeśli usunę element struct (zarówno double, jak i int), działa poprawnie.

Zakładam, że problem jest w jakiś sposób związany z układem struktury - ale kiedy robię sizeof() w C i Marshal.SizeOf() w C#, oba zwracają tę samą wartość ... tak jeśli rozmiar struktury jest taki sam w C# i C, jaki może być problem?

Najwyraźniej brakuje mi czegoś podstawowego.

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

Budowanie Script

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

Nic nie wyskakuje ... do cholery, spróbuj zamiana kolejności pól w struct? (obie strony, naturalnie) – JerKimball

+0

@JerKimball Dzięki za spojrzenie. Zamiana kolejności pól w strukturze nie ma żadnego efektu. Nadal otrzymuję ostrzeżenie MDA PInvokeStackImbalance. – Steve

+0

Czy to może być problem między GCC a .NET i podwójnym typem? Zwrócenie struct z cdecl może prowadzić do problemów zależnych od kompilatora. Sprawdź to http://stackoverflow.com/questions/13786192/methods-type-signature-is-not-pinvoke-compatible-while-calling-dll-method –

Odpowiedz

9

Przede wszystkim le t mi pogratulować bardzo zadawanego pytania. To była przyjemność, gdy tylko otrzymałem cały kod potrzebny do odtworzenia problemu.

Problem wynika z nieco innych ABI używanych przez narzędzia gcc i Microsoft dla zwracanych wartości funkcji. Dla wartości zwracanych, które mogą pasować do rejestrów, na przykład int zwracane wartości, nie ma różnic. Ale ponieważ twoja struktura jest zbyt duża, aby zmieścić się w jednym rejestrze i istnieją różnice między interfejsami API w tej sytuacji.

W przypadku większych wartości zwracanych, wywołujący przekazuje ukrytą wskazówkę do funkcji. Ten ukryty wskaźnik jest wysyłany na stos przez rozmówcę. Funkcja zapisuje wartość zwracaną na adres pamięci określony przez ten ukryty wskaźnik. Różnica w ABI polega na tym, kto wyrzuca ukryty wskaźnik ze stosu. Narzędzia Microsoft używają ABI, która wymaga od wywołującego ukryć ukryty wskaźnik, ale domyślny ABC ABI prosi o to osobę, która to zrobiła.

Teraz, gcc jest prawie nieskończenie konfigurowalny, istnieje przełącznik, który pozwoli ci kontrolować ABI. I możesz sprawić, by gcc korzystał z tych samych reguł, co narzędzia Microsoft. Wykonanie tego wymaga callee_pop_aggregate_return function attribute.

Zmiana kodu C, aby być tak:

__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

Krwawy dobrze odpowiedział - i zbadane! – JerKimball

+0

Kocham cię. Dziękuję Ci. Jesteś moim bohaterem! – Steve

+0

David, szybkie pytanie kontrolne; Czy można bezpiecznie używać tego atrybutu callee_pop_aggregate_return dla WSZYSTKICH wyeksportowanych funkcji dla mojej kompilacji systemu Windows - czy powinienem używać TYLKO go, gdy jest on faktycznie odpowiedni? Pytam, ponieważ łatwiej będzie trzymać się zawsze z dodaniem makra prekompilatora na końcu moich deklaracji, zamiast określać, gdzie konkretnie będę tego potrzebował - więc jeśli będzie bezpieczny, będzie łatwiej. – Steve

Powiązane problemy