2009-09-11 12 views
6

Mam więc macierzystą bazę kodu C++ innej firmy, z którą pracuję (pliki .lib i .hpp), której użyłem do zbudowania wrappera w C++/CLI do ewentualnego użycia w języku C#.Dostęp do wyjątku naruszenia/awarii z wywołania zwrotnego C++ do funkcji C#

Wystąpił szczególny problem podczas przełączania z trybu debugowania do wydania, ponieważ otrzymuję wyjątek naruszenia dostępu po zwróceniu kodu wywołania zwrotnego.

Kod z oryginalnych plików HPP dla formatu funkcja zwrotna:

typedef int (*CallbackFunction) (void *inst, const void *data); 

kod z C++/CLI otoki dla formatu funkcja zwrotna: (Wytłumaczę dlaczego oświadczyłem dwa chwilę)

public delegate int ManagedCallbackFunction (IntPtr oInst, const IntPtr oData); 
public delegate int UnManagedCallbackFunction (void* inst, const void* data); 

--Quickly, powodem oświadczyłem drugi „UnManagedCallbackFunction” jest to, że starałem się stworzyć „pośrednik” wywołania zwrotnego w opakowaniu jednostkowym, więc łańcuch zmieniono z macierzystym C++> C# w wersji Native C++> C++/CLI Wrapper> C# ... Pełny opis Problem nadal występuje, został on właśnie przeniesiony do Owijarki C++/CLI w tej samej linii (powrót).

I wreszcie, kod upaść z C#:

public static int hReceiveLogEvent(IntPtr pInstance, IntPtr pData) 
    { 
     Console.WriteLine("in hReceiveLogEvent..."); 
     Console.WriteLine("pInstance: {0}", pInstance); 
     Console.WriteLine("pData: {0}", pData); 

     // provide object context for static member function 
     helloworld hw = (helloworld)GCHandle.FromIntPtr(pInstance).Target; 
     if (hw == null || pData == null) 
     { 
      Console.WriteLine("hReceiveLogEvent: received null instance pointer or null data\n"); 
      return 0; 
     } 

     // typecast data to DataLogger object ptr 
     IntPtr ip2 = GCHandle.ToIntPtr(GCHandle.Alloc(new DataLoggerWrap(pData))); 
     DataLoggerWrap dlw = (DataLoggerWrap)GCHandle.FromIntPtr(ip2).Target; 

     //Do Logging Stuff 

     Console.WriteLine("exiting hReceiveLogEvent..."); 
     Console.WriteLine("pInstance: {0}", pInstance); 
     Console.WriteLine("pData: {0}", pData); 
     Console.WriteLine("Setting pData to zero..."); 
     pData = IntPtr.Zero; 
     pInstance = IntPtr.Zero; 
     Console.WriteLine("pData: {0}", pData); 
     Console.WriteLine("pInstance: {0}", pInstance); 

     return 1; 
    } 

Wszystko pisze do konsoli to zrobić i wtedy widzimy bał awarii na zwrot:

nieobsługiwany wyjątek w 0x04d1004c w helloworld.exe: 0xC0000005: Access naruszenie lokalizacji odczytu 0x04d1004c.

Gdybym krok do debuggera stąd, wszystko co widzę to, że ostatni wpis na stos wywołań to:> „04d1004c()”, które ocenia na wartość dziesiętną: 80805964

Która jest tylko ciekawe, jeśli spojrzeć na konsoli, która pokazuje:

entering registerDataLogger 
pointer to callback handle: 790848 
fp for callback: 2631370 
pointer to inst: 790844 
in hReceiveLogEvent... 
pInstance: 790844 
pData: 80805964 
exiting hReceiveLogEvent... 
pInstance: 790844 
pData: 80805964 
Setting pData to zero... 
pData: 0 
pInstance: 0 

teraz wiem, że między debugowania i uwalniania niektóre rzeczy są zupełnie inne w świecie Microsoft. Oczywiście obawiam się dopełnienia bajtów i inicjalizacji zmiennych, więc jeśli jest coś, czego tu nie dostarczam, daj mi znać, a dodam do (już długiego) postu. Sądzę też, że zarządzany kod NIE może zwalniać całego prawa własności, a następnie natywne elementy C++ (do których nie mam kodu) mogą próbować usunąć lub zabić obiekt pData, powodując awarię aplikacji.

Pełniejsze ujawnienie, wszystko działa dobrze (pozornie) w trybie debugowania!

Prawdziwy problem z drapaniem głowy, który doceni każdą pomoc!

Odpowiedz

3

myślę stos został zgnieciony z powodu niedopasowania konwencje wzywające: wypróbować umieścić atrybut

[UnmanagedFunctionPointer(CallingConvention.Cdecl)] 

na deklaracji zwrotna delegata.

+0

Dla wsparcia było to w większości przypadków poprawne. Po skontaktowaniu się z zewnętrznym dostawcą, dowiedzieliśmy się, że zostały skompilowane przy użyciu specyfikacji CDecl, a nie standardowego kodu wymaganego dla zgodności z kodami zarządzanymi: http://msdn.microsoft.com/en-us/library/367eeye0%28VS.80%29.aspx . Dodałem pytanie dotyczące StackOverflow, aby zapytać, dlaczego tak musi być? Mamy nadzieję, że ktoś da lepsze wyjaśnienie niż cytowany artykuł MSDN. – TomO

+0

W ustawieniach projektu jest domyślna konwencja wywołująca (C/C++), jeśli nie została określona przez __declspec(). Ta konwencja wywoływania nie była widoczna w kodzie. Co dzieje się z niedopasowanymi konwencjami jest jasne: jeśli odpowiedzialność za czyszczenie stosów nie jest zgodna, miażdży stos (nie resetuje się do stanu sprzed połączenia z powodu podwójnego oczyszczenia lub zbyt małej ilości). To zależy od ilości argumentów przekazywanych na stosie. http://en.wikipedia.org/wiki/Calling_convention – jdehaan

0

This doesn't directly answer your question, ale może prowadzić cię w dobrym kierunku jeśli chodzi o trybie debugowania porządku vs. trybie zwolnienia nie w porządku:

Od debuggera dodaje wiele informacji prowadzenia rejestrów na stos, na ogół dopełniając rozmiar i układ mojego programu w pamięci, miałem "szczęście" w trybie debugowania, bazując na 912 bajtach pamięci, które nie były zbyt ważne. Jednak bez debuggera zapisywałem dość ważne rzeczy, w końcu wychodząc poza własną przestrzeń pamięci, co spowodowało, że Interop usunął pamięć, której nie posiadał.

Jaka jest definicja DataLoggerWrap? Pole char może być zbyt małe dla danych, które otrzymujesz.

0

Nie jestem pewien, co próbujesz osiągnąć.

Kilka punktów:

1) Kolektor śmieci jest bardziej agresywny w trybie zwolnienia tak ze złych własności zachowanie można opisać nie jest niczym niezwykłym.

2) Nie rozumiem, co poniższy kod próbuje zrobić?

IntPtr ip2 = GCHandle.ToIntPtr(GCHandle.Alloc(new DataLoggerWrap(pData))); 
DataLoggerWrap dlw = (DataLoggerWrap)GCHandle.FromIntPtr(ip2).Target; 

użyć GCHandle.Alloc zablokować wystąpienie DataLoggerWrap w pamięci, ale to nigdy nie przechodzą go do niekontrolowana - więc dlaczego go zablokować? Ty też nigdy go nie uwolnisz?

Druga linia następnie odsyła referencję - dlaczego ścieżka okrężna? dlaczego referencja - nigdy jej nie używasz?

3) Ustawiłeś IntPtrs na wartość null - why? - nie będzie to miało wpływu poza zakresem funkcji.

4) Musisz wiedzieć, jaka jest umowa zwrotna. Kto jest właścicielem pData wywołania zwrotnego lub funkcji wywołującej?

0

Jestem z @jdehaan, z wyjątkiem CallingConvetion.StdCall może być odpowiedź, zwłaszcza, gdy lib 3rd party jest napisane na przykład w BC++.

Powiązane problemy