Native Delphi Dll aus C# aufrufen

  • C#
  • .NET (FX) 1.0–2.0

Es gibt 16 Antworten in diesem Thema. Der letzte Beitrag () ist von OlafSt.

    Native Delphi Dll aus C# aufrufen

    Aaalso,

    Ich hab ne simple Delphi DLL geschrieben:

    C#-Quellcode

    1. library Project2;
    2. { Important note about DLL memory management: ShareMem must be the
    3. first unit in your library's USES clause AND your project's (select
    4. Project-View Source) USES clause if your DLL exports any procedures or
    5. functions that pass strings as parameters or function results. This
    6. applies to all strings passed to and from your DLL--even those that
    7. are nested in records and classes. ShareMem is the interface unit to
    8. the BORLNDMM.DLL shared memory manager, which must be deployed along
    9. with your DLL. To avoid using BORLNDMM.DLL, pass string information
    10. using PChar or ShortString parameters. }
    11. uses
    12. SysUtils,
    13. Classes;
    14. {$R *.res}
    15. Function ReturnSecretToken(key : Integer ) : string;
    16. Begin
    17. Result := 'MyToken_' + IntToStr(key);
    18. End;
    19. end.


    Soll mir also den string zurückgeben.
    Hab sie kompiliert und in C# folgendes geschrieben:

    C#-Quellcode

    1. using System;
    2. using System.Collections.Generic;
    3. using System.Linq;
    4. using System.Text;
    5. using System.Runtime.InteropServices;
    6. namespace CallNativeDll
    7. {
    8. class Program
    9. {
    10. static void Main(string[] args)
    11. {
    12. Console.WriteLine("Invoking native dll...");
    13. Console.WriteLine(InvokeNative());
    14. Console.ReadKey();
    15. }
    16. [DllImport("runtime.dll", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.StdCall)]
    17. public static extern IntPtr ReturnSecretToken(int key);
    18. private static string InvokeNative()
    19. {
    20. IntPtr retValue = ReturnSecretToken(123);
    21. return Marshal.PtrToStringUni(retValue);
    22. }
    23. }
    24. }


    Doch der erhoffte string wird nicht ausgegeben.
    Stattdessen bekomme ich ne HRESULT Exception : BadImageFormat?



    Was ist an meinem Aufruf falsch, wieso funktioniert er nicht?
    C# Developer
    Learning C++

    Rikudo schrieb:

    BadImageFormat
    Kompiliere mal mit x86 oder x64, je nach dem.
    Nicht aber mit AnyCPU.
    Zu den Deklarationen gugst Du hier, allerdings weiß ich nicht, wie ein Delphi-String abgebildet wird.
    Fang mal an mit Integer.
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!
    Zeig mal wie du die Delphi-Dll kompiliert hast(warum überhaupt Delphi?)

    Aber vmtl. dürfte x86 richtig sein.
    In welcher Zeile kommt dann die AccessViolation? Vlt. solltest du in Delphi LongInt nehmen, dort wird garantiert, dass die Größe 4-Byte beträgt.
    Und Int32 beim Aufruf, damit es eindeutiger ist(sollte aber keinen unterschied machen - ich finds nur schöner)
    Ich wollte auch mal ne total überflüssige Signatur:
    ---Leer---
    @jvbsl : Ehm, wie, wie kompiliert?
    Ich nutze Delphi 7SE (schon etwas älter) und soweit ich weiß kann dort nur als x86 kompiliert werden.
    Hab auch nochmal den DelphiCode editiert, ohne erfolg:

    C#-Quellcode

    1. ​library Project2;
    2. { Important note about DLL memory management: ShareMem must be the
    3. first unit in your library's USES clause AND your project's (select
    4. Project-View Source) USES clause if your DLL exports any procedures or
    5. functions that pass strings as parameters or function results. This
    6. applies to all strings passed to and from your DLL--even those that
    7. are nested in records and classes. ShareMem is the interface unit to
    8. the BORLNDMM.DLL shared memory manager, which must be deployed along
    9. with your DLL. To avoid using BORLNDMM.DLL, pass string information
    10. using PChar or ShortString parameters. }
    11. uses
    12. SysUtils,
    13. Classes;
    14. {$R *.res}
    15. Function ReturnSecretToken(key : Integer ) : string; stdcall;
    16. Begin
    17. Result := 'MyToken_' + IntToStr(key);
    18. End;
    19. exports
    20. ReturnSecretToken;
    21. end.



    Der fehler in C# wird hier ausgelöst:

    C#-Quellcode

    1. ​IntPtr retValue = ReturnSecretToken(123);
    C# Developer
    Learning C++
    Strings in Delphi müssen mit Hilfe des ShareMem-Speichermanagers umhergetauscht werden. Der riesige Kommentar steht nicht nur zum Spaß und zum Herumnerven da ;)

    Das Problem ist, das C# mit dem Sharemem-Speichermanager überhaupt nix anzufangen weiß. Somit sind Delphi-Strings als Rückgabewert nach .NET (und in nahezu jede andere Sprache außer Delphi) ausgeschlossen. Benutze BSTR, OLESTR oder, noch besser, PChar. Achte darauf, das du auch einen Unicode-String in Form von PWideChars zurückgibst, das erspart dir weitere Kopfschmerzen: In C# ist jeder String automatisch Unicode.

    Hinweis: Delphi beherrscht erst seit D2009 Unicode. Erst ab dieser Version sind Delphi-Strings automatisch Unicode.
    Hey,

    Nice, das wirft auf jeden fall mal keine Exception mehr.
    Allerdings bekomme ich keinen gescheiten String zurück, nur Fragezeichen ;D ? Wieso passiert das?

    C#-Quellcode

    1. using System;
    2. using System.Collections.Generic;
    3. using System.Linq;
    4. using System.Text;
    5. using System.Runtime.InteropServices;
    6. namespace CallNativeDll
    7. {
    8. class Program
    9. {
    10. static void Main(string[] args)
    11. {
    12. Console.WriteLine("Invoking native dll...");
    13. Console.WriteLine(InvokeNative());
    14. Console.ReadKey();
    15. }
    16. [DllImport("Project2.dll", CharSet = CharSet.Unicode)]
    17. public static extern IntPtr ReturnSecretToken(int key);
    18. [return: MarshalAs(UnmanagedType.LPWStr)]
    19. private static string InvokeNative()
    20. {
    21. IntPtr retValue = ReturnSecretToken(1234);
    22. return Marshal.PtrToStringUni(retValue);
    23. }
    24. }
    25. }





    C#-Quellcode

    1. library Project2;
    2. { Important note about DLL memory management: ShareMem must be the
    3. first unit in your library's USES clause AND your project's (select
    4. Project-View Source) USES clause if your DLL exports any procedures or
    5. functions that pass strings as parameters or function results. This
    6. applies to all strings passed to and from your DLL--even those that
    7. are nested in records and classes. ShareMem is the interface unit to
    8. the BORLNDMM.DLL shared memory manager, which must be deployed along
    9. with your DLL. To avoid using BORLNDMM.DLL, pass string information
    10. using PChar or ShortString parameters. }
    11. uses
    12. SysUtils,
    13. Classes;
    14. {$R *.res}
    15. // Function ReturnSecretToken(key : Integer ) :
    16. Function ReturnSecretToken(key : Integer ) : PChar ;export; stdcall;
    17. Begin
    18. Result := PAnsiChar(AnsiString('MyToken_' + IntToStr(key)));
    19. End;
    20. exports
    21. ReturnSecretToken;
    22. end.





    Habs!!
    PtrToStringAnsi statt PtrToStringUni musste ich nehmen ;)
    C# Developer
    Learning C++

    Dieser Beitrag wurde bereits 2 mal editiert, zuletzt von „Rikudo“ ()

    Jo, das habe ich dann auch selbst noch rausbekommen;P
    Meine Frage: Wenn ich die C# App als x86 kompilere funzt alles. Bei AnyCpu und x64 nicht
    Wie kann ich meine Delphi dll so kompilieren das es bei allen dreien funktioniert?
    C# Developer
    Learning C++

    Rikudo schrieb:

    so kompilieren
    Welche Optionen bietet Delphi diesbezüglich?
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!

    Rikudo schrieb:

    Keine
    Und wie wird es kompiliert?
    Mit .NET kannst Du eine DLL in x64 und das rufende Programm in AnyCPU machen, wenn das auf einem 64 Bit PC läuft.
    Normalerweise sollten beide (exe und dll) dieselbe Einstellung (je nach PC) haben.
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!
    Zunächst einmal kompiliert Delphi 7 ausschließlich 32Bit. 64Bit gabs erst ab D2009, also 8 Versionen später (nur als Hinweis auf das Alter von D7), IIRC.

    Zweitens: 64Bit-Programme erfordern zwingend 64-Bit-DLLs. Es ist nicht möglich, denen eine 32Bit-DLL unterzujubeln.

    Ein .Net-Programm, kompiliert als AnyCPU, läuft auf einem 32-Bit-Windows als 32-Bit-Anwendung; auf einem 64-Bit-Windows als 64Bit-Anwendung. Ausnahme: Es ist unter VS2012+ mit "32Bit-Preferred" kompiliert, dann ist es immer 32Bit ;)
    Das heisst im Klartext:

    1) Prüfen ob das target als 32,64 oder Anycpu kompiliert wurde.

    Bei 32 Bit -> verwende 32Bit DLL.
    Bei 64 Bit -> verwende 64Bit DLL.
    Bei AnyCpu ->

    A) Check ob mit 32Bit kompiliert -> verwende 32Bit DLL.

    B) OS Check ob 64 oder 32 bit und verwende entsprechende DLL.



    So richtig?
    C# Developer
    Learning C++

    Rikudo schrieb:

    AnyCpu
    kannst Du nur bei .NET-Assemblies ohne nativen Code verwenden.
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!
    Im Falle AnyCPU wäre es als erstes angebracht herauszufinden, ob ich nun als 64er oder 32er laufe (keine Ahnung, ob das machbar ist). Erst dann ist sicher, welche DLL zu laden ist. Wie schon gesagt, mit "32Bit preferred"ist es nicht mehr vom OS abhänging, ob das AnyCPU-Programm 32 oder 64 Bit breit ist.