2012-11-29 12 views
9

Myślę, że zasadniczo zrozumiałem, jak pisać C# delegatów dla wywołań zwrotnych, ale ten jest mylący. C++ definicja jest następująca:Delegat C# do wywołania C++

typedef int (__stdcall* Callback)(
long lCode, 
long lParamSize, 
void* pParam 
); 

i moja C# podejście byłoby:

unsafe delegate int CallbackDelegate (int lCode, int lParamSize, IntPtr pParam); 

Mimo to wydaje się być nieprawidłowy, ponieważ pojawia się błąd PInvokeStackInbalance, co oznacza moją definicję delegata jest źle.

Pozostałe parametry funkcji są ciągami lub intami, co oznacza, że ​​nie mogą powodować błędu, a jeśli po prostu przekazuję IntPtr.Zero zamiast delegata (co oznaczałoby, że wskazuję na istniejąca funkcja oddzwaniania) Otrzymuję błąd AccessViolation, który ma również sens.

Co robię źle?

EDIT: funkcja

Pełne C++ to:

int 
__stdcall 
_Initialize (
const char*   FileName, 
Callback   cbFunction, 
int     Code, 
const char*   Name, 
unsigned int  Option, 
unsigned int  Option2 
); 

Moje C# wersja jest:

[DllImport("MyDll.dll", CallingConvention = CallingConvention.StdCall)] 
public static extern int _Initialize (string FileName, CallbackDelegate cbFunction, int Code, string Name, uint Options, uint Options2); 

Funkcję (do testowania) nazywany po prostu wewnątrz głównego rutyny aplikacja konsolowa:

static void Main(string[] args) 
{ 
    CallbackDelegate del = new CallbackDelegate(onCallback); 

    Console.Write(_Initialize("SomeFile.dat", del, 1000, "", 0, 4)); 
    Console.Read(); 
} 

gdzie onCallback to:

static int onCallback(int lCode, int lParamSize, IntPtr pParam) 
{ 
    return 0; 
} 

pojawia się błąd PInvokeStackInbalance na linii, gdzie ja nazywam _Initialize, jeśli mijam IntPtr.Zero zamiast delegata i zmienić definicję funkcji do IntPtr zamiast CallbackDelegate następnie Otrzymuję AccessViolationException.

+0

'Reszta parametrów funkcji są stringi lub ints'. Nie ukrywaj informacji. –

+1

Czy próbowałeś nie używać niebezpieczeństwa? tj .: delegować int CallbackDelegate (int lCode, int lParamSize, IntPtr pParam); – DougEC

+0

@HansPassant Nie próbuję ukrywać informacji, próbowałem zostawić rzeczy, które uważałem za nieistotne. Będę edytować pytanie i dodać informacje. – Valandur

Odpowiedz

3

.NET long jest 64-bitowy. C++ long może być tylko 32-bitowe. Sprawdź za pomocą kompilatora C++, który skompilował tę definicję, do jakiego rozmiaru jest long s.

+0

Dobre wejście, Niestety nie wydaje się, aby rozwiązać problem. Nie mogę sprawdzić kompilatora C++, ponieważ mam plik .dll i .h, ale zmiana długiego na Int32 nie pomaga. – Valandur

3
[UnmanagedFunctionPointerAttribute(CallingConvention.StdCall)] 
unsafe delegate int CallbackDelegate (int lCode, int lParamSize, IntPtr pParam); 

Jeśli .NET zakłada cdecl zamiast stdcall, Twój stos będzie z pewnością zapewniony.

4

Dodałem twój kod do mojego obecnego projektu, w którym robię dużo interakcji C#/C++ w VS2012. I chociaż nienawidzę używać "działało na mojej maszynie", to działało dobrze dla mnie. Kod, na którym go uruchomiłem, jest wymieniony poniżej, aby zilustrować to, czego nie zrobiłem i podstawowe zmiany.

Moja sugestia jest taka, że ​​tworzymy nową natywną bibliotekę dll z funkcją skrótową dla _Initialize taką jak poniżej i zobaczmy, czy działa, gdy możesz kontrolować obie strony interfejsu. Jeśli to działa, ale prawdziwa dll tego nie robi, sprowadza się do ustawień kompilatora po stronie natywnej lub ich błąd jest po stronie natywnej i tupie na stosie.

extern "C" { 

    typedef int (__stdcall* Callback)(long lCode,long lParamSize,void* pParam); 

    TRADITIONALDLL_API int __stdcall _Initialize (const char* FileName,Callback cbFunction, int     Code, 
           const char*   Name,unsigned int  Option,unsigned int  Option2) 
    { 
     cbFunction(0, 0, nullptr); 
     return 0; 
    } 
} 

Po stronie C#, dodałem deklaracje do mojej klasy interfejs:

public delegate int CallbackDelegate(int lCode, int lParamSize, IntPtr pParam); 

[DllImport("XXX.dll", CallingConvention = CallingConvention.StdCall)] 
public static extern int _Initialize(string FileName, CallbackDelegate cbFunction, int Code, string Name, uint Options, uint Options2); 

a następnie w moim głównym:

private int onCallback(int lCode, int lParamSize, IntPtr pParam) 
{ 
     return 0; 
} 
XXXInterface.CallbackDelegate del = new XXXInterface.CallbackDelegate(onCallback); 

Console.Write(XXXInterface._Initialize("SomeFile.dat", del, 1000, "", 0, 4)); 
+0

Dziękuję za sprawdzenie tego, powinienem był to zrobić sam. Próbowałem tego również i wydaje się, że działa. Co się również okazało, że aplikacja wydaje się działać, jeśli po prostu uruchomić plik exe, BEZ debuggera ... – Valandur

+0

To jest dziwne. Wygląda na to, że istnieje spora szansa na to, że dll w języku C++ wpływa na stos. Czy jest jakiś sposób, aby uzyskać źródło dla biblioteki dll natywnych, dzięki czemu można je uruchomić w połączeniu w debugerze? Nie znam innego dobrego sposobu podejścia i prawie na pewno nie da się go naprawić za pomocą źródła. –

+0

Nie, niestety nie mogę wziąć w swoje ręce kodu źródłowego, w przeciwnym razie myślę, że byłoby to o wiele łatwiejsze. Próbowałem wywoływać funkcję C++ z programu C++, a to wydaje się działać doskonale. Dlatego myślę, że biblioteka dll jest w porządku, a ja po prostu popełniam błąd z funkcją zwrotną. Dzięki za pomoc. – Valandur

Powiązane problemy