structs in arrays



  • Sieht so aus, als ob ein Java-Programmierer sich mit C++ versucht. Wieso wird der EventReader im Freispeicher angelegt? Wo löschst Du den wieder?



  • krümelkacker schrieb:

    Sieht so aus, als ob ein Java-Programmierer sich mit C++ versucht. Wieso wird der EventReader im Freispeicher angelegt? Wo löschst Du den wieder?

    Oh das hab ich vergessen, das delete eventReader stand mal ganz am Ende der Methode, da kommt der Fehler aber ebenso und noch noch dazu werden nach dem delete alle Texte die ich vorher ausgelesen habe durch komische Symbole ersetzt.

    Aber du hast recht, mit Java komme ich wesentlich besser zurecht, das ist irgendwie angenehmer zu programmieren, hier muss man auf sovieles aufpassen 😞



  • Gast84 schrieb:

    Oh das hab ich vergessen, das delete eventReader stand mal ganz am Ende der Methode

    Wie gesagt, man muss das Ding nicht im Freispeicher anlegen, wenn es "am Ende" sowieso nicht mehr benötigt wird. Dafür gibt es ja den automatischen Speicherbereich, wo "normale" lokale Variablen auch leben.

    void foo()
    {
      CEventGetter eventReader;
      ...
      eventGetter.GetText(...);
      ...
    }
    

    oder

    void foo()
    {
      CEventGetter eventReader (param1, param2, param3, ...);
      ...
    }
    

    um den "event reader" mit bestimmten Parametern zu initialisieren. Folgendes:

    void foo()
    {
      CEventGetter eventReader ();
      ...
    }
    

    ist eine "syntaktische" Falle. Dies kann auf zweierlei Arten interpretiert werden (Funktionsdeklaration und Konstruktoraufruf). Diese Ambiguität muss der Compiler zugunsten der Funktionsdeklaration auflösen. Das ist leider so. (Google "most vexing parse").

    Gast84 schrieb:

    Aber du hast recht, mit Java komme ich wesentlich besser zurecht

    Das ist erstmal normal. Es dauert eine Weile, bis man den Dreh raus hat. 🙂



  • Ja, ohne "new" wird er ja wieder zerstört sobald ich die Methode verlasse. Das Problem war ja das wenn ich ihn zerstöre (oder zerstören lasse am Ende der Methode) alle Texte die ich mir über den Reader geholt habe durch kryptische Zeichen ersetzt werden. Daher dacht ich ich lass ihn bis Programmende bestehen und hau ihn dort dann erst weg.

    Ich glaub aber ich hab den Übeltäter gefunden, bitte nicht gleich schreien, das war nur zu Testzwecken !
    Ich habe diese Zeile:

    EventData* currentData;
    

    Ersetzt durch diese:

    EventData* currentData = new EventData;
    

    Und am Ende der Methode dann delete currentData; aufgerufen. Da zerhauts den Heap schon dort. D.h. es muss am currentData liegen, ich habe aber keine Ahnung wieso. Ich mach doch nichts wildes mit der Variablen 😞



  • Doch machst du -) Du zerschiesst dir den ganzen Speicher!

    Ändere zuersteinmal deine EventData-Struktur ab:
    entweder 'std::string' oder aber wenigstens z.B. 'char ...[100]' anstatt "char *", denn für die char-Zeiger müßtest du auch ersteinmal Speicher reservieren bevor du darauf zugreifen kannst (das hatte dir SeppJ ja auch schon geschrieben).

    Nichts für ungut, aber m.E. solltest du dein Programm noch mal neu strukturieren (und dabei auf jedliche Zeiger zuersteinmal verzichten!!!).



  • Hallo,

    okay ich hab mich jetzt mit einem C++ Buch mal mit den CStrings auseinander gesetzt und weiß jetzt immerhin mal warum SeppJ sagte dass das "verdächtig" aussieht...
    Dass das so nicht funktioniert wundert mich nicht mehr wirklich.

    ich hab jetzt <string> eingebunden und das Struct besteht nun aus lauter Strings. Das funktioniert soweit auch, das Problem ist der Einstieg der Methode:

    void GetEventText2(short arrSize, EventData entries[])
    

    Weil ich hier ein Array lauter solcher Structs übergeben bekomme. Sobald ich mein Struct aber von char* ändere in char ...[100] oder string, kommt hier nur noch Mist an. Da stehen dann bloß noch irgendwelche kryptischen Zeichen drinne (bei char ...[100]) oder es ist alles vertauscht und durcheinander (bei string). Die Übergabe erfolgt aus einen C# Programm und dort ist das Struct mit strings gemacht. Wieso ging das bei dem falschen char* mit dem ich den Speicher zerschieße und wenn ichs richtig mache gehts nicht mehr ?

    Je mehr ich mich damit auseinander setze desto mehr wünsche ich mir wieder Java 😞



  • Du kannst Arrays nicht als Parameter übergeben. Deswegen benutzt man auch lieber std::vector oder std::tr1::array.



  • Janjan schrieb:

    Du kannst Arrays nicht als Parameter übergeben. Deswegen benutzt man auch lieber std::vector oder std::tr1::array.

    Okay, dann übergeb ich stattdessen einen Pointer auf das Array, das geht doch.
    der Methodenaufruf sieht so aus:

    NativeMethods.GetEventText2((short)specializedEntries.Length, specializedEntries);
    

    Und die aufgerufene Methode so:

    void GetEventText2(short arrSize, EventData* entries)
    

    Sowas geht doch normalerweise problemlos. Wieso hier nicht ? Liegt das daran das ich eine DLL aufrufe ? Dürfte laut Internet aber keinen Unterschied machen.

    Werd mir jetzt mal den Vector anschauen, nur wenns an der Übergabe an eine DLL liegt wird mir das evt auch nix helfen 😞



  • So, ich definiere meine Frage neu, weil ich glaub langsam wirds unübersichtlich, weil mein Problem nicht mehr bei Pointern oder Speicherverletzungen liegt, das hab ich hinbekommen 🙂

    Ich hab eine realtiv großes Programm, geschrieben in C#. Darin gibt es irgendwann mal ein Array welches voller Structs ist. Dieses Array muss jetzt an eine DLL übergeben werden, oder eben ein Pointer auf das Array.
    Die DLL ist in C++ geschrieben und muss den Pointer auf das Array entgegennehmen, weil die DLL inerhalb der Structs des Arrays Werte ändern muss.
    Wenn ein Pointer übergeben wird braucht die DLL ja auch keinen Rückgabewert (wenn ich mein Buch richtig verstanden habe).

    Das Problem ist jetzt das der Pointer, welcher in der DLL ankommt, zwar auf das erste Arrayfeld und somit das erste Struct zeigt, Werte im Struct aber anders oder vertauscht sind. Mit jedem weiteren ArrayFeld nach hinten wirds schlimmer. Aber Feld 18 stehen in den Structs dann die ersten kryptischen Zeichen bzw. Meldungen wie <Schlechter Pointer>.

    Wenn ich in C++ als Test 2 Methoden schreibe die sich so ein Array übergeben dann funktioniert das einwandfrei. Liegt das jetzt also an der Schnittstelle zwischen C# und C++ ? Oder daran weils ne DLL ist ? Laut Internet kann man Werte genauso an eine DLL übergeben wie auch an eine normale Methode.



  • Das hat aber jetzt eher was mit C# zu tun (bzw. dem Marshalling).

    Wie sehen deine struct-Definitionen in C# und C++ jetzt aus?
    Und wie die genaue DllImport-Deklaration und dessen Aufruf?



  • Also das ist das C++ Struct in der DLL:

    struct EventData
    {
       string eventID1;
       string eventID2;
       string priorityClass;
       string obNumber;
       string dataIDZ23;
       string dataIDZ1;
       string suppDataZ11;
       string suppDataZ12;
       string suppDataZ21;
       string suppDataZ22;
       string suppDataZ31;
       string suppDataZ32;
       string eventText;	
    };
    

    Dies ist das Struct in dem C# Hauptprogramm:

    public struct EventData
    {
       public string eventID1;
       public string eventID2;
       public string priorityClass;
       public string obNumber;
       public string dataIDZ23;
       public string dataIDZ1;
       public string suppDataZ11;
       public string suppDataZ12;
       public string suppDataZ21;
       public string suppDataZ22;
       public string suppDataZ31;
       public string suppDataZ32;
       public string eventText;
    }
    

    Der DLL Import im C# Programm sieht so aus:

    [DllImport("Event2DBAccess.dll")]
       public static extern void
       GetEventText2(short arrSize, [In, Out] [MarshalAs(UnmanagedType.LPArray)] EventData[] entries);
    

    Die Structs sind mMn vollkommen einheitlich, es muss irgendwo an der Übergabe liegen. Habs schon zusätzlich mit dem CallConvetion Parameter versucht, weil ich da irgendwo was gelesen hatte, aber ohne Erfolg. Bei In und Out bin ich mir auch nicht ganz sicher, ein Out brauch ich doch gar nicht wenn ich in der DLL mit dem Pointer arbeite, oder ?



  • Ah ich vergass die DLL Definiton im C++ Programm.
    Die steht im Header:

    #define DLLEXPORT __declspec(dllexport)
    extern "C" DLLEXPORT void GetEventText2(short arrSize, EventData* entries);
    


  • Also da muß ich dich enttäuschen, aber ein C# String ist nicht binär-kompatibel mit einem C++ std::string (außerdem verwendet C# intern Unicode (2 Bytes), während ein std::string nur ein Byte pro Zeichen speichert).

    Du mußt dich intensiv mit dem Thema "Marshalling" beschäftigen. Schau mal intensiv die MSDN Doku zu der Klasse 'Marshal' an.

    Desweiteren kann ich dir ein anderes Forum (http://www.mycsharp.de/wbb2/forum) empfehlen, wo auch schon sehr viele Fragen zum Thema "Marshalling" gestellt und beantwortet wurden:
    http://www.mycsharp.de/wbb2/search.php?searchstring=Marshal%2A&boardids%5B%5D=*&send=send // Edit: leider versagt hier die Forensoftware (daher Link einfach kopieren)

    Dein eigentliches Vorhaben, direkt in C++ die C#-Strukturen zu beschreiben, wirst du aber so nicht hinbekommen.
    Der übliche Weg ist es in C# einen StringBuilder zu erstellen (mit einer konstanten Kapazität) und in der C- bzw. C++-DLL dann auf einem char[] zu arbeiten.
    Bei Strukturen wird es dann entsprechend komplex und wenn du dann auch noch Arrays von Strukturen hast wird es noch komplexer...

    Nichtsdestotrotz viel Erfolg bei der Suche nach einer Lösung und der Umsetzung.

    Evtl. mußt du auch der einfachhalthalber den Zwischenweg über C++/CLI nehmen...



  • Ich habs ja befürchtet...

    Aber nur damit ichs richtig verstehe, das klingt jetzt so als wäre das was ich machen möchte gar nicht möglich.
    Das ich aus C# jetzt dieses Array (auf irgendeine Weise) in die C++ DLL gebe, es dort bearbeite und dann (auf irgendeine andere Weise) zurückgebe ist theoretisch schon möglich ?

    Danke für die Links, dann werd ich mal anfangen zu lesen 🙂



  • So, es scheint jetzt zu laufen, in dem C# Sharp Forum hat man mir helfen können, danke nochmal Th69 für den Link !
    Wollte jetzt schnell die Lösung noch hier verewigen falls mal jmd vor dem selben Problem steht oder es einen noch interessiert:

    Es mussten lediglich die 2 Structs angepasst werden:

    Struct in C++:

    struct EventData
    {
    	char eventID1[5];
    	char eventID2[5];
    	char obNumber[5];
    	char priorityClass[5];
    	char dataIDZ1[5];
    	char dataIDZ23[5];
    	char suppDataZ11[5];
    	char suppDataZ12[5];
    	char suppDataZ21[5];
    	char suppDataZ22[5];
    	char suppDataZ31[5];
    	char suppDataZ32[5];
    	char eventText[2500];
    };
    

    Und im C# Struct muss jeder String "gemarshalled" werden:

    public struct EventData
            {
                [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 5)]
                public string eventID1;
                [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 5)]
                public string eventID2;
                [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 5)]
                public string priorityClass;
                [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 5)]
                public string obNumber;
                [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 5)]
                public string dataIDZ23;
                [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 5)]
                public string dataIDZ1;
                [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 5)]
                public string suppDataZ11;
                [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 5)]
                public string suppDataZ12;
                [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 5)]
                public string suppDataZ21;
                [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 5)]
                public string suppDataZ22;
                [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 5)]
                public string suppDataZ31;
                [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 5)]
                public string suppDataZ32;
                [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 2500)]
                public string eventText;
            }
    

    Dadurch wird jeder String als Zeichenkette fester Größe in dem Struct abgelegt und ist mit dem charFelder Struct in C++ kompatibel.

    So braucht die C++ DLL auch keinen Rückgabewert sondern ich kann mit dem Pointer auf das C# Array direkt die Werte dort ändern.

    Ich habe noch nicht alle Testfälle abgedeckt aber bis jetzt sieht es so aus als wäre das die Lösung gewesen 🙂


Anmelden zum Antworten