Hoe deze Delphi. Dll-functie te bellen vanuit C #?

// delphi-code (Delphi-versie: Turbo Delphi Explorer (het is Delphi 2006))

function GetLoginResult:PChar;
   begin
    result:=PChar(LoginResult);
   end; 

// C# code om bovenstaande delphi-functie te gebruiken (ik gebruik unity3d, binnen, C #)

[DllImport ("ServerTool")]
private static extern string GetLoginResult(); //this does not work (make crash unity editor)

[DllImport ("ServerTool")] 
[MarshalAs(UnmanagedType.LPStr)] private static extern string GetLoginResult();//this also occur errors

Wat is de juiste manier om die functie in C# te gebruiken?

(voor gebruik in ook in delphi, code is als,  if (event = 1) en (tag = 10) then writeln ('Login result:', GetLoginResult); )

4
toegevoegd de auteur Christophe Geers, de bron

1 antwoord

Het geheugen voor de string is eigendom van uw Delphi-code, maar uw p/invoke-code zal ertoe leiden dat de marshaller CoTaskMemFree in dat geheugen aanroept.

Wat je moet doen is de marshaller vertellen dat hij geen verantwoordelijkheid moet nemen voor het vrijmaken van het geheugen.

[DllImport ("ServerTool")] 
private static extern IntPtr GetLoginResult();

Gebruik vervolgens Marshal.PtrToStringAnsi() om de geretourneerde waarde naar een C# -teken te converteren.

IntPtr str = GetLoginResult();
string loginResult = Marshal.PtrToStringAnsi(str);

Je moet er ook voor zorgen dat de aanroepconventies overeenkomen door de Delphi-functie als stdcall te declareren:

function GetLoginResult: PChar; stdcall;

Hoewel het zo is, maakt deze conventie misaanpassing niet uit voor een functie die geen parameters en een retourwaarde voor de aanwijzer heeft.

Om dit allemaal te laten werken, moet de Delphi-tekenreeksvariabele LoginResult een globale variabele zijn zodat de inhoud geldig is nadat GetLoginResult is geretourneerd.

8
toegevoegd
@DavidHeffernan Bedankt voor het antwoord, maar dit gebeurt fouten ... Zie deze foutfoto's,
toegevoegd de auteur creator, de bron
toegevoegd de auteur creator, de bron
@DavidHeffernan Wauw, ja je hebt gelijk, het werkt! Bedankt man. Maar hoe zou je dit allemaal kunnen weten? Door boek- en forumthreads te lezen?
toegevoegd de auteur creator, de bron
Lees het eerste foutbericht. U kunt dit niet doen in een veldinitialisator. Je moet de code in een methode plaatsen.
toegevoegd de auteur David Heffernan, de bron
@StefanGlienke In feite kan een Delphi-functie die een WideString retourneert niet worden aangeroepen vanuit een C# p/invoke met [return: MarshalAs (UnmanagedType.BStr)] . Ik denk dat we elkaar moeten bespreken. Je moet iets anders dan ik doen. Ik heb die andere vraag bijgewerkt om duidelijk te maken dat Delphi het zwarte schaap van het gezin is.
toegevoegd de auteur David Heffernan, de bron
@StefanGlienke Als de aanroepconventies niet overeenkomen, is dat een binaire incompatibiliteit. Oproepconventie is onderdeel van de ABI. En de vreemde eend in dit alles is Delphi. C ++ en C# verwerken BSTR-retourwaarden op dezelfde manier. Delphi behandelt ze anders. Als je echt probeert een Delphi-DLL te schrijven die een WideString retourneert en deze dan met p/invoke aanroept, zie je waar ik het over heb.
toegevoegd de auteur David Heffernan, de bron
@StefanGlienke A BSTR/WideString is niet helemaal hetzelfde. Merk ook op dat we hier te maken hebben met 8 bit char, omdat dat de standaard is voor p/invoke. Zelfs als het een 16-bits teken was, heeft een BSTR extra laadvermogen. En zelfs al dat toe te staan, is het retourneren van een WideString van een Delphi DLL niet binair compatibel met andere compilers. Zie mijn vraag over dat onderwerp: stackoverflow.com/questions/9349530/…
toegevoegd de auteur David Heffernan, de bron
@Stefan Om dat MSDN-voorbeeld te begrijpen, moet je kijken naar de onbeheerde code die is aan de andere kant van de interface . Zoals je ziet, roept dat CoTaskMemAlloc om de string toe te wijzen, precies zoals ik al zei.
toegevoegd de auteur David Heffernan, de bron
@Stefan Wat er gebeurt, is dat de marshaller CoTaskMemFree aanroept op de aanwijzer die u retourneert.
toegevoegd de auteur David Heffernan, de bron
@Petesh in feite niet omdat de functie geen params heeft en de retourwaarde wordt hetzelfde behandeld voor stdcall en registreren. Maar het is beter om Delphi-functie als standaard te declareren. Dank daarvoor.
toegevoegd de auteur David Heffernan, de bron
Zou de conventie ook in dit geval van belang zijn?
toegevoegd de auteur Petesh, de bron
@David Sorry, ik vergat het feit dat je WideString in je Delphi dll in dat geval moet gebruiken zoals vermeld in nog een SO-vraag . Maar voor mij is dat de beste manier om het te doen.
toegevoegd de auteur Stefan Glienke, de bron
Ik ben er vrij zeker van dat als je de retourwaarde van je DllImport-functie als string definieert, de marshaller deze correct verwerkt zoals beschreven in de msdn ( msdn.microsoft.com/en-us/library/e765dyyy.aspx ).
toegevoegd de auteur Stefan Glienke, de bron
@David Eerst zijn ze niet binair onverenigbaar, het lijkt meer een conventieprobleem te zijn. Ten tweede lijkt het probleem dat je beschrijft te zijn wanneer je interop tussen C ++ en Delphi doet. We hebben het over C# en Delphi die de marshaller gebruiken. Ik heb in dat geval nog geen enkel probleem gezien en het is de voorgestelde manier wanneer ik erover lees.
toegevoegd de auteur Stefan Glienke, de bron
@David Ik heb het niet alleen geprobeerd, ik heb het al een tijdje geleden gedaan en het werkte prima
toegevoegd de auteur Stefan Glienke, de bron