C++ Methoden aus DLL in C# aufrufen



  • Die String-Konstanten für die verwendeten Namen dürften kein Problem sein - der unsigned long *Size dahinter dagegen schon. Dort wird ein Zeiger auf eine unsigned-Variable erwartet, also kannst du keine Konstante übergeben (vermutlich will die Funktion dort die Größe reinschreiben, die sie selber ermittelt hat).

    Hast du denn in deinem Beispiel auch irgendwo die tatsächliche Verarbeitungsfunktion an "Methode1" zugewiesen?


  • Administrator

    Wird irgendetwas an den übergebenen Strings verändert? Ich nehme mal an, dass nichts daran geändert wird. Dann müsste die Sache wie folgt aussehen:

    [DllImport("Pfad zur DLL")] 
    protected static extern int Methode1(string DatName, string BinName, string CaName, out uint Size, int NBCode, int CaType);
    
    // Aufruf:
    uint size;
    int result = Methode1("Pfad", "Pfad", "WhatEver", out size, 0, 0);
    

    Unter der Annahme, dass unsigned long einen unsigned 32 Bit Typ darstellt und das Methode1 einen von dir hingesetzten Platzhalter darstellt (Niemand wird doch ernsthaft eine Methode Methode1 nennen?).

    Und im übrigen:
    Bring den Typen um, welcher den gezeigten Code als C++ Code bezeichnet.

    Grüssli



  • @CStoll
    Ich habe im C++ Code in verschiedenen Kombinationen getestet, bei denen ich die Übergabeparameter durch echte Werte ersetzt habe. Und es gibt immer einen Fehler, wenn ich entweder den Parameter long, oder das 2. char BinName durch einen direkten Wert ersetze. Ersetze ich das 1. oder 3. char durch einen Wert "Wert", wird keiner Fehler geworfen, selbst dann nicht, wenn ich Quatsch reinschreibe.

    @Dravere
    Nein an den Strings wird nichts geändert. Ich habe mir die Übergabeparameter im Debuggermodus in C++ mehrmals angeschaut, sie bleiben gleich. Ich habe wie beschrieben, die Werte, die die Übergabeparameter annehmen, selbst als Werte in die Methode eingetragen --> Fehler.

    "Methode1" ist eine von mir geänderte Bezeichnung, das ist korrekt. Die ganze Sache ist die, dass der vorhandene Code und die DLL von einem Mikrochiphersteller stammt, zu dem sich der Kontakt als sehr schwierig erweist, und ich eben keine weiteren Quellen habe. Du musst mal die GUI der ganzen Demoanwendung sehen, katastrophal, zeigen darf ich die aber nicht. Ich habe deinen Code eben getestet, er wirft jetzt zumindest keine AccessViolationException mehr, aber die Ausgabedatei wird nicht erzeugt. Ich werde das am morgen nochmal prüfen, bin zu müde grad, melde mich wieder, Danke für eure Hilfestellungen.



  • Probier mal

    [DllImport("Pfad zur DLL")] 
    protected static extern int Methode1(
        [MarshalAs(UnmanagedType.LPStr)] string DatName,
        [MarshalAs(UnmanagedType.LPStr)] string BinName,
        [MarshalAs(UnmanagedType.LPStr)] string CaName,
        out uint Size,
        int NBCode,
        int CaType);
    

    Das [MarshalAs(UnmanagedType.LPStr)] sollte dafür sorgen dass die Strings als Zeiger auf char Arrays übergeben werden, und nicht als Zeiger auf wchar_t Arrays.

    Ansonsten könnte man noch versuchen das out uint Size durch ref uint Size zu ersetzen. Macht aber nur Sinn, wenn die Funktion den übergebenen Wert auch liest, und nicht bloss ein Ergebnis reinschreibt. Der gezeigte Beispielcode sieht aber nicht so aus als würde das der Fall sein.

    Das uint passt auf jeden Fall. Ein unsigned long ist auf Windows standardmässig 32 Bit (auch auf 64 Bit Windows), und C#'s uint ist auch 32 Bit.


  • Administrator

    hustbaer schrieb:

    Das [MarshalAs(UnmanagedType.LPStr)] sollte dafür sorgen dass die Strings als Zeiger auf char Arrays übergeben werden, und nicht als Zeiger auf wchar_t Arrays.

    Wenn man CharSet im DllImportAttribute nicht explizit angibt, so steht es unter C# immer auf CharSet.Ansi . Somit werden auch alle Strings als Ansi-Strings übergeben. Daher wird es kaum daran liegen.
    http://msdn.microsoft.com/en-us/library/system.runtime.interopservices.charset.aspx

    Auto - Automatically marshal strings appropriately for the target operating system. The default is Unicode on Windows NT, Windows 2000, Windows XP, and the Windows Server 2003 family; the default is Ansi on Windows 98 and Windows Me. Although the common language runtime default is Auto, languages may override this default. For example, by default C# marks all methods and types as Ansi.

    Zuerst sollte besser mal geklärt werden, was denn nun der Fehler ist. Wenn nur keine Ausgabedatei erzeugt wird, dann müsste doch zumindest ein Fehlercode zurückgegeben werden. Zumindest sieht die Methode danach aus, als ob sie einen Fehlercode als Rückgabewert hätte.

    Grüssli



  • CableGuy8419 schrieb:

    @CStoll
    Ich habe im C++ Code in verschiedenen Kombinationen getestet, bei denen ich die Übergabeparameter durch echte Werte ersetzt habe. Und es gibt immer einen Fehler, wenn ich entweder den Parameter long, oder das 2. char BinName durch einen direkten Wert ersetze. Ersetze ich das 1. oder 3. char durch einen Wert "Wert", wird keiner Fehler geworfen, selbst dann nicht, wenn ich Quatsch reinschreibe.

    Meinst du hier Fehler im Sinne von "lässt sich nicht compilieren" (wenn ja, mit welcher Meldung?) oder "stürzt ab, wenn die Funktion ausgeführt wird" (Exception, SegFault o.ä.)?

    PS

    Dravere schrieb:

    Und im übrigen:
    Bring den Typen um, welcher den gezeigten Code als C++ Code bezeichnet.

    Es ist zumindest innerhalb einer Klassen-Methode 😃 :p



  • Guten Morgen,

    @hustbaer
    Habe deinen Vorschlag umgesetzt. Die Methode wird Fehlerfrei durchlaufen, jedoch wird wieder keine Ausgabedatei erzeugt.

    @Dravere
    Die Methode gibt, warum auch immer, jedes mal eine 0 zurück, ganz egal ob in C#, C++, ob ein Fehler auftritt oder nicht.

    @CStoll
    Kompilieren gin, nur beim Aufruf der Methode wurde eine Exception geworfen. Leider weiss ich nicht mehr welche das war, und ich habe den C++ Codeteil nicht hier, sondern in der Firma. Ich glaube aber er hat wegen den Typen gemeckert a lá *"MeinString" cannot be casted to char



  • CableGuy8419 schrieb:

    Ich glaube aber er hat wegen den Typen gemeckert a lá *"MeinString" cannot be casted to char

    "Wegen Typen meckern" macht nur der Compiler. Zur Laufzeit könnte ich mir höchstens vorstellen, daß die Funktion den Parameter als Ausgabe verwenden will - das kracht normalerweise mit einem SegFault oder ähnlichem, wenn du String-Literale übergeben willst.
    (Ein String-Literal ala "Hallo" ist eigentlich ein const char* , auch wenn der Compiler es erlaubt, einen normalen char-Zeiger darauf zu verwenden - wenn du den aber verändern willst, passiert so etwas)



  • Morgen kann ich es dir sagen, wenn ich wieder in der Firma bin. Hab eben nochmal geschaut, hab aber statt dem ganzen C++ Code nur eine Headerdatei und eine Klasse als Textdatei mitgenommen.
    Ich probiere jetzt nochmal das ganze in C# mit den gegebenen Informationen und versuche zu kombinieren, irgendwie muss das Ding doch laufen.

    Danke euch allen für eure Hilfe. Mein Mittagsbier geht auf euch.



  • @Dravere:
    Danke für die Aufklärung.

    Ich hatte beim MarshalAsAttribute geguckt, und da stand default sei UnmanagedType.LPTStr, was CharSet.Auto entspricht.



  • @CStoll

    Also ich habe jetzt das ganze C++ Projekt vor mir:
    Er meckert, sobald ich bei den Übergabeparametern einen String oder die long Variable ausschreibe, statt den Variablennamen zu verwenden. Er meckert dann zur Laufzeit mit folgendem Fehler:
    Unbehandelte Ausnahme in EP4.exe (EP3.dll): 0x00000005: Access Violation

    Daraus müsste doch resultieren:
    Wenn ich schon in C++ statt den Variablennamen (wie DatName) keinen String ausschreiben kann (wie "Ich bin der Pfad"), dann dürfte man das in C# auch nicht machen, oder? Die Sache mit den Pointern ist echt ne Ecke verzwickter als ich dachte. Vor allem dann, wenn man aus der Java-Welt kommt und in C++/C# keine tiefen Kenntnisse hat...



  • Nun ich vermute, dass die Methode nun zwar fehlerfrei ausgeführt werden kann, aber keine Ausgabedatei erzeugt, da wahrscheinlich eine andere Methode zuvor ausgeführt werden muss.

    Diese andere Methode aus C++, in C# zu schreiben, ist noch komplizierter als die vorherige.
    Ich zeige euch das mal:
    Hier wird die Methode aus der DLL im C++ Code aufgerufen (relevante Auszüge):
    [cpp]
    void * objptr;
    strcpy(((CEP3 😉 objptr)->cfgFileName,cfgFileName);
    i=ISD151XX_Init(objptr,&myAppConfig);[/cpp]

    Es gibt 2 Übergabeparameter. Einen Objektpointer und ein struct.
    MyAppConfig konnte ich bereits lösen, es handelt sich dabei um ein struct, das ich 1 zu 1 im C# Code schreiben konnte. Nur was repräsentiert der objptr, das verstehe ich nicht. Könnt ihr mir bei dieser Funktion auch helfen, ich vermute dass diese Methode zuerst ausgeführt werden muss, und dann die Komprimierungsmethode von gestern.


  • Administrator

    CableGuy8419 schrieb:

    Nun ich vermute, ...

    Also in Java kannst du mit Vermutungen vielleicht noch etwas anfangen, aber bei C und C++ ist das eine ganz gefährliche Sache. Denn auch wenn etwas zu funktionieren scheint, muss dies nicht der Fall sein. In C und C++ hast du sogenanntes undefiniertes Verhalten. Da kann etwas korrekt kompilieren, das Resultat ist aber völlig undefiniert. Deshalb muss man immer genau wissen, was man eigentlich tut. Einfach ein wenig zu vermuten und auszuprobieren ist da keine gute Idee.
    Irgendwo MUSS es eine Dokumentation zu der Bibliothek geben oder zumindest jemanden, den man fragen kann. Sonst ist die Bibliothek schlicht und einfach nicht verwendbar.

    CableGuy8419 schrieb:

    Hier wird die Methode aus der DLL im C++ Code aufgerufen (relevante Auszüge):

    void * objptr;
    strcpy(((CEP3 *) objptr)->cfgFileName,cfgFileName);
    i = ISD151XX_Init(objptr,&myAppConfig);
    

    Dieser Code macht überhaupt keinen Sinn! Hast du womöglich etwas weggelassen? objptr ist ein reiner Zeiger, welcher aber nicht initialisiert wird und dadurch ins Nirvana zeigt. Da ist undefiniertes Verhalten garantiert.

    Grüssli



  • CableGuy8419 schrieb:

    @CStoll

    Also ich habe jetzt das ganze C++ Projekt vor mir:
    Er meckert, sobald ich bei den Übergabeparametern einen String oder die long Variable ausschreibe, statt den Variablennamen zu verwenden. Er meckert dann zur Laufzeit mit folgendem Fehler:
    Unbehandelte Ausnahme in EP4.exe (EP3.dll): 0x00000005: Access Violation

    Ja, das ist das, was ich dort oben skizzieren wollte - deine Funktion will den übergebenen String manipulieren, da stellt sich das Betriebssystem quer.
    Ich würde mal davon ausgehen, daß du den Parameter für deine C# Funktion als ref oder out definieren mußt, um die Änderungen mitzubekommen.

    Was von deinem ISD151XX_Init()-Beispielcode zu halten ist, hat Dravere bereits zusammengefasst.



  • Ja das sind 3 Zeilen Auszug aus einem größeren Stück Code.
    Da ich mich seit mehreren Tagen mit C++/C# und dem ganzen Quellcode beschäftige, vergisst man allzu oft, seine Fragen ausführlicher zu stellen.
    Morgen wird sich ein C++ Erfahrener zu mir gesellen, ich denke wenn er den Code vor sich liegen hat, könnte er das schneller lösen, mal sehen.

    THX für die Anteilnahme.


Anmelden zum Antworten