So zeigen Sie die Werte von Strukturen in C # aus C ++ an

8

abc.h-Datei

%Vor%

abc.cpp (DLL-Datei)

%Vor%

Struktur: -

%Vor%

Funktionsaufruf: -

%Vor%

Wenn ich versuche, sie in C # aufzurufen, bekomme ich in C # einen Müllwert. Während ich sie debugge, finde ich korrekte Speicherung der Werte. Jede Hilfe, wie die Werte übergeben werden müssen, wäre sehr nützlich.  Vielen Dank im Voraus.

    
TechBrkTru 11.07.2017, 13:04
quelle

2 Antworten

7

Die C # -Deklarationen sind auf dem richtigen Weg, nur dass die Details subtil falsch sind. Ein guter Ausgangspunkt ist dieser Beitrag , der zeigt, wie man einen Testcode schreibt, um zu überprüfen, ob die Strukturdeklarationen gut zusammenpassen . Tun Sie dies auf diesem bestimmten:

%Vor%

Nicht in der Nähe, Sie müssen eine genaue Übereinstimmung erhalten, um darauf hoffen zu können, dass das Marshalling korrekt durchgeführt wird. Die C # -Deklaration für die innere Struktur sollte wie folgt aussehen:

%Vor%

Die sp_BankNoteTypeList-Deklaration ist in Ordnung. Führen Sie den Test erneut aus, und Sie sollten nun auch 196 Bytes in C # erhalten. Die Änderungen notieren:

  • CharSet = CharSet.Ansi
    Dieser war notwendig, damit das CHAR-Mitglied korrekt marshallen konnte. Es ist ein Windows-Typedef für char , ein 8-Bit-Typ. CharSet.Auto wäre die richtige Wahl gewesen, wenn die native Struktur WCHAR verwendet hätte.
  • public uint cim_ulValues;
    Windows verwendet das LLP64-Datenmodell , das in 32- und 32-Bit eine ULONG beibehält. Bit- und 64-Bit-Programme. Das macht uint das korrekte C # Äquivalent anstelle von ulong.
  • private byte _cim_bConfigured;
    bool ist ein sehr kniffliger Typ mit schlechter Standardisierung. Es ist 1 Byte in C ++, 4 Byte in C, 2 Byte in COM-Interop, 1 Byte in verwaltetem Code. Das Standard-Marshalling setzt BOOL als übereinstimmenden nativen Typ voraus, so wie es in winapi gemacht wird. Wenn ich es privat als ein Byte mit einem öffentlichen Eigenschaften-Getter deklariere, ist das eine Möglichkeit, dies zu tun, und die, die ich hier bevorzuge, würde das [MarshalAs (UnmanagedType.U1)] - Attribut auf das Feld anwenden.
  • CallingConvention = CallingConvention.Stdcall
    Ziemlich wichtig, um dies explizit zu sein, ist die andere häufige Auswahl hier CallingConvention.Cdecl. Ich kann nicht aus dem nativen Snippet erkennen, welches ist korrekt, BNA_API ist ein Makro, aber Sie haben nicht die PInvokeStackImbalance MDA erwähnt, die sich darüber beschweren, so dass Stdcall mit einiger Wahrscheinlichkeit korrekt ist. Stellen Sie sicher, dass Sie es nicht ausgeschaltet haben.
  • [Out]out sp_BankNoteTypeList list
    Das [Out] -Attribut ist hier notwendig, um den pinvoke-Marshaller davon zu überzeugen, dass das Kopieren der Struktur erforderlich ist. Das ist ziemlich unintuitiv, die meisten Programmierer denken, dass out genug ist. Aber das ist ein C # -Sprachdetail, von dem der Marshaller nichts weiß. Das Zurückkopieren muss explizit angefordert werden, die Struktur ist nicht "blittierbar". Oder anders gesagt, das native Layout ist nicht dasselbe wie das intern verwaltete Layout. Das ByValArray macht das unvermeidlich.

Ziemlich viele Wäschelisten, ich hoffe, ich habe sie alle bekommen. Das Erhalten der Strukturgrößen ist 95% des Kampfes.

    
Hans Passant 16.07.2017 18:36
quelle
3

Ich habe den C ++ Teil neu erstellt, da ich Fehler im Zusammenhang mit WFSGetInfo und den zugehörigen Strukturen habe, bearbeite ich Felder mit einigen zufälligen Daten (ein Hinweis zu char[3] field: Ich bevölkere es mit 3 zufälligen Großbuchstaben. Mit Inspiration von MSDN und vielen anderen Fragen von SO habe ich eine Liste von Problemen im Code identifiziert. Nachdem ich diese Probleme behoben hatte, konnte ich die Daten richtig aus C # herausholen. Als Anmerkung verwende ich VStudio2015 .

Einige Grundnoten:

  • BNA_API für mich ist __declspec(dllexport)
  • Die Funktionsdeklaration befindet sich in einem

    %Vor%

    block, um C ++ name mangling zu vermeiden. Weitere Informationen finden Sie unter [MSDN]: Verzierte Namen

Probleme:

  1. Nicht übereinstimmende Aufrufkonvention (in meinem Fall wurde eine Ausnahme ausgelöst): Standardmäßig verwendet C ( C ++ ) __cdecl , während C # Marshaler verwendet __stdcall . Das funktioniert nicht gut, es beschädigt den Stapel, wenn ping ing / pop Ping-Argumente. Um dies zu beheben, müssen Sie den Standardwert in nur an einem Ort ändern:
    • C ++ : BNA_API int __stdcall BanknoteType(SP_CIMNOTETYPELIST *sp_BankNoteType); - dies ist die Methode, die ich gewählt habe
    • C # : DllImport(@"abc.dll", CallingConvention = CallingConvention.Cdecl)] - das funktioniert auch
  2. Einige der Typen aus C # sind anders (breiter) als ihre Korrespondenten aus C . Wenn Daten ausgerichtet sind, wenn solche Unstimmigkeiten auftreten, wird alles, was nach dem 1 st auftritt, durcheinandergebracht (wenn es versucht wird, es herauszubekommen). In struct sp_notetype s C # -Definition sollten Sie also haben: public uint cim_ulValues; . Überprüfen Sie [MSDN]: Marshaling-Argumente für die vollständige Liste
  3. (könnte eine Variante des vorherigen sein) Charset Ihrer Strukturen - in C , char (8bit breit) wird verwendet -, ändern Sie es von Charset.Auto in% Code%. Überprüfen Sie [SO]: C #, das C DLL aufruft, pass char * als Parameter nicht korrekt
CristiFati 16.07.2017 11:07
quelle

Tags und Links