Announcement

Collapse
No announcement yet.

Dll in C# einbinden

Collapse
X
  • Filter
  • Time
  • Show
Clear All
new posts

  • #16
    stdcall wäre in Delphi relevant beim Import. Beim Export ist es ohne Bedeutung.
    Ich habe schon ewig kein Delphi mehr benutzt die Doku würde ich aber so interpretieren wie ich es in Erinnerung habe. Schließlich muss die Delphi Seite wissen in welcher Reihenfolge es die Parameter vom Stack zu lesen hat. Oder überhaupt auf dem Stack suchen muss. Die Parameter könnten ja auch in Registern stecken und gar nicht auf dem Stack (Standard von Delphi ist ja fastcall und nicht pascal Aufrufkonvention).

    Nur zur Sicherheit wir reden hier von einer expliziten 32bit .Net Anwendung und einer 32bit Dll oder? Nur dann macht die Aufrufskonventionsdiskussion in dieser Form Sinn.

    Edit: Wenn ich die Doku zitiere sollte ich sie auch verlinken.

    Comment


    • #17
      Vermutlich macht es Sinn, noch einmal kurz die History meiner Irrfahrt aufzuzeigen:

      Da ist das ursprüngliche Produkt, dessen Hersteller nicht mehr existiert und nicht mehr erreichbar ist und von daher "as it is" behandelt werden muss.
      Dieses wurde mit zwei DLLs ausgeliefert:

      Die eine (mal Org1.dll genannt) ist die mit dem JNI-Wrapper; das Thema habe ich ad acta gelegt.

      Für die zweite (Org2.dll) habe ich in den Tiefen des Internets einen Delphi-Wrapper gefunden und den folgenden Weg dann eingeschlagen:

      Also habe ich mein altes Delphi (2006) noch mal in einer VM (XP 32bit) installiert und ein Delphi-Programm geschrieben, um zu testen, ob der gefundene Wrapper auch zu org2.dll passt und funktioniert. Das war erfreulicherweise auch der Fall. Der gefundene Wrapper importiert die Funktionen der DLL mit stdcall.

      Meine Schlussfolgerung: Dann kann ich org2.dll auch mit stdcall (was in C# ja auch Standard ist) in C# importieren. War aber nicht so und das ist mein eigentlicher Knackpunkt.

      Verzweifelter nächster Versuch: Ich habe den gefundenen Delphi-Wrapper wiederum in eine eigene Delphi-DLL (myDelphi.dll) gepackt, um alle Möglichkeiten zu haben, die Übergabe-Konventionen zu testen. Dieser Schritt sollte eigentlich überflüssig sein, aber ich will ja jetzt wissen, woran es liegt! ...

      Was ich nun, Dank Deines Verweises auf die Doku testen werde, ist der Delphi-Export als stdcall. Ich wusste gar nicht, dass Delphi das kann. Aber auch das wird mein Problem nicht lösen, da ich bei meinem C#-Import bereits alle CallingConventions durchgetestet habe und die auch in der Lage sein müssten auf beide Delphi-Varianten (stdcall und fastcall) einzugehen.

      Übrigens: Ich bin doch angenehm überrascht, dass sich überhaupt noch jemand um Delphi bemüht. Nach der Borland-Inprise-Borland-CodeGear-Embarcadero-Odyssee, war ich der Meinung, es sei jämmerlich zugrunde gegangen ...

      Danke aber auch Euch für Eure Mühe. Ohne Euch zuviel strapazieren zu wollen:
      Wie würdet Ihr zwei Routinen, die in Delphi folgendermaßen importiert wird, in C# importieren?

      Code:
      procedure RAM_ReadBlock(DevNumber : Byte; Addr: Word; var BArray : Byte; ByteCnt : DWORD); stdcall; external 'ORG2.DLL';
      
      function OpenDevice(DevNumber : Byte; DriverName, ID, KeyWord : String): Word; stdcall; external 'ORG2.DLL';
      Der Import in Delphi als stdcall sagt mir eigentlich, dass die ORG2.DLL ebenfall mit stdcall exportiert. Oder liege ich da falsch?

      Comment


      • #18
        Originally posted by mec View Post
        Danke aber auch Euch für Eure Mühe. Ohne Euch zuviel strapazieren zu wollen:
        Wie würdet Ihr zwei Routinen, die in Delphi folgendermaßen importiert wird, in C# importieren?

        Code:
        procedure RAM_ReadBlock(DevNumber : Byte; Addr: Word; var BArray : Byte; ByteCnt : DWORD); stdcall; external 'ORG2.DLL';
        
        function OpenDevice(DevNumber : Byte; DriverName, ID, KeyWord : String): Word; stdcall; external 'ORG2.DLL';
        Der Import in Delphi als stdcall sagt mir eigentlich, dass die ORG2.DLL ebenfall mit stdcall exportiert. Oder liege ich da falsch?
        Wenn du wirklich mit String arbeiten kannst dann hast du eine DLL-Schnittstelle die nur mit Delphi/C++-Builder funktionieren wird. Der Delphi String der hier verwendet wird kann so nicht in C#/.NET importiert werden. Hier müsste die Funktion mit P(Wide)Char arbeiten damit das gehen würde.

        Comment


        • #19
          Das vorherige ist die Import-Deklaration des gefundenen Delphi-Wrappers und die funktioniert auch (in Delphi).

          Mein Pendant in C# wäre:
          Code:
          unsafe static UInt16 OpenDevice(byte devNumber, string driverName, string iD, string keyWord)
          {
              // eine Nullterminierung zum Schluss schadet auch nicht
              driverName += (char)0;
              iD += (char)0;
              keyWord += (char)0;
          
              fixed (char* pDriverName = driverName, pID = iD, pKeyWord= keyWord)
              {
                  return OpenDevice(devNumber, pDriverName, pID, pKeyWord);
              }
          }
          
          [DllImport("org1.dll")]
          unsafe static extern UInt16 OpenDevice(byte devNumber, char* driverName, char* iD, char* keyWord);
          Und das sollte doch eigentlich funktionieren, oder? Wide sollte der char* automatisch sein.

          Comment


          • #20
            fixed (char* pDriverName = driverName, pID = iD, pKeyWord= keyWord)
            Nicht probiert aber ich bezweifle das das geht.

            Um aus einem managed string einen char Pointer zu machen den man zum Interop verwenden kann sollte man sich der Marshal Klasse bedienen.
            Code:
            char* pDriverName = (char*)Marshal::StringToHGlobalUni(driverName).ToPointer();
            Das sollte aber die Marshaling Magie hinter dem DllImport Attribute bereits machen wenn die andere Seite den ein irgendwie geartetes char Array erwartet und keinen Delphi String sollte es reichen auch in der Methodensignatur der Importierten Methode einfach String oder je nach Anwendungsfall besser StringBuilder zu erwähnen.

            Comment


            • #21
              Ja, das funktioniert. An anderer Stelle (DLL) kann ich auch direkt String übergeben. Und der String ist ja auch schon die Kür.

              Aber warum funtioniert eine einfache Funktion wie
              Code:
              function GetWord(aWord : word) : word; stdcall;
              nicht?

              Comment


              • #22
                Du hast für word überall ushort bzw. UInt16 auf C# Seite verwendet?
                Du sagst funktioniert nicht. Wie äußert sich das?




                Ja, das funktioniert.
                Dann hat vermutlich noch ein Marshalling von char* nach char* stattgefunden. Dein gepinnter char* existiert ja weiterhin auf dem managed Heap den der Delphi Teil eigentlich nicht sehen kann. Der muss erst auf den unmanged Heap verschoben werden.

                Comment


                • #23
                  Es äussert sich im besten Falle so, dass einfach nicht korrekte Werte zurückgeliefert werden.

                  Im ungünstigeren Fall (bei mehreren Parametern) erhalte ich die von MS so schön übersetzte Meldung, dass "der Stapel gestört wurde". Damit meinen die wohl eine Speicherverletzung.

                  Was den unmanaged Heap angeht, so sollte das doch mit unsafe eigentlich abgehandelt sein. Für byte-Arrays gilt ja das Gleiche wie für Strings. Und das funktioniert sonst auch ... bei anderen nicht .NET-DLLs

                  Comment


                  • #24
                    Ich meinte jetzt die konkrete Fehlermeldung von der GetWord Methode mit nur einem Parameter.

                    Ich denke auch das da eigentlich wenig schief gehen kann aber vielleicht liefert die konkrete Fehlermeldung da einen Hinweis. Wenn das bei der Methode (isoliert ausgeführt von allen anderen Interop Methoden die ebenfalls am Stack rumspielen) auch die Exception über den 'unbalanced Stack' ist (in english auch nicht viel besser als im deutschen) dann gehe ich davon aus das stdcall auf Delphi Seite nicht zieht und der Parameter immer noch in einem Register gesucht wird und nicht auf dem Stack was dazu führt das Delphi den Stack im Anschluss nicht aufräumt. Die Net. Runtime merkt das der Stackpointer vor dem Methodenaufruf woanders steht als nach dem Methodenaufruf und meckert (so stelle ich es mir gerade zumindest vor).


                    Was den unmanaged Heap angeht, so sollte das doch mit unsafe eigentlich abgehandelt sein.
                    Du meinst wahrscheinlich das pinnen des Pointers per fixed Direktive und nicht unsafe. Ich glaube nicht. Byte vielleicht noch aber Char sicher nicht.

                    Comment


                    • #25
                      Das Problem ist (teilweise) gelöst.

                      Die ursprüngliche DLL scheint selbst in Delphi geschrieben zu sein. Denn wenn ich die berühmte BORLNDMM.DLL ins Anwendungsverzeichnis packe, funktioniert es zumindest mit den Strings (mit unsafed und fixed übrigens). Leider löst das Fehlen der DLL keine (DllNotFound-)Exception aus, so dass ich da erst jetzt drauf gekommen bin. Hätte es eigentlich von früher wissen müssen, habe leider auch schon zu lange kein Delphi mehr gemacht.

                      Es geht langsam voran ... jetzt noch die bytes ... morgen

                      Comment

                      Working...
                      X