0

I'm trying to import a method from a .NET DLL to my DelphiScript:

function GetAnswer() : string; external 'PixelPatternCaptcha.dll';

begin
Print(GetAnswer)
end.

The statement Print(GetAnswer) seems to trigger an error:

Module: "", Line: 3, Message: Access violation at address 00000000. Read of address 00000000

I'm not sure how to specify the path for the method. I have the DLL in the same folder as the script that imports it.

It looks like the DLL actually gets invoked. When I try to delete the DLL, the operating system tells me that my .exe file has it opened.

Currently working on it:

procedure GetAnswer(out codeResult: WideString); stdcall; external 'PixelPatternCaptcha.dll';

var r : WideString;

begin
print(GetAnswer(r));
end.
Yves Gurcan
  • 1,096
  • 1
  • 10
  • 25
bwk4u
  • 25
  • 7
  • If the DLL is in the same folder as your executable, it should find it by its self. Notice I said "executable" because you mention "script". Delphi is not a scripting language, so I assume you meant to say "code". This may not be the correct location, depending on your version of Delphi. Older versions, the EXE was created in the same directory as the project by default, but later versions save in a subdirectory `.\$(Platform)\$(Config)` so, for example, `\Win32\Release`. – Jerry Dodge Mar 27 '17 at 15:37
  • 2
    Unless the DLL is compiled to export a C-style flat function, this will never work. Interop with .NET libraries is usually done using COM interfaces instead. But even if the DLL does export a C-style flat function, Delphi's `String` type is not compatible with .NET and thus is the wrong data type to use. Without more detailed information about the .NET library, how it was compiled, and how the function is declared on the .NET side, this question cannot be answered as-is. – Remy Lebeau Mar 27 '17 at 15:39
  • The DLL was compiled with the help of [link](https://www.nuget.org/packages/UnmanagedExports). All I had to do was add the atribute to my method, like so: [DllExport] public static string GetAnswer() @JerryDodge, tried with the DLL in the same path as the .EXE that invokes it, same error. Thanks for answering! – bwk4u Mar 27 '17 at 15:51
  • Again, Delphi's `string` type is not compatible with .NET`s `string` type. You need to use Interop-safe data types. – Remy Lebeau Mar 27 '17 at 19:15

1 Answers1

1

The DLL is located using the DLL Search Order. In your case, since the program is executing, the DLL is found and loaded. And indeed the function is found and imported.

The runtime error therefore, is due to a defect in your code. The problem is the use of string as a return value type. That is never valid for imported DLLs, apart from when both DLLs are built with the same version of Delphi, and share the same memory manager. That is not the case here, you are importing a .net DLL using Robert Giesecke's UnamagedExports tool.

You need to change both the C# and the Delphi code to use a valid type for interop. The most convenient is the COM BSTR type, aka WideString in Delphi.

On the C# side that looks like this:

[DllExport] 
public void GetAnswer(
    [MarshalAs(UnmanagedType.BStr)] out string result
)
{
    result = "foo";
}

On the Delphi side you import that like this:

procedure GetAnswer(out result: WideString); stdcall; external 'PixelPatternCaptcha.dll';

The reason I use an out parameter here, rather than a return value is explained here:

Why can a WideString not be used as a function return value for interop?

Community
  • 1
  • 1
David Heffernan
  • 601,492
  • 42
  • 1,072
  • 1,490
  • Indeed, my code had a defect that is corrected now. In the other side, tried your solution, but when I attach Visual Studio to my exe for debugging it, the method doesn't get the call (breakpoint not hit). With my solution (same as posted), it debugs ok but when I return the string it crashes (because of what you've explained). How should i call the procedure in Delphi for doing a Print() ? – bwk4u Mar 27 '17 at 16:50
  • I copied another of your mistakes. Calling convention. Should be stdcall. Update to answer fixes it. – David Heffernan Mar 27 '17 at 16:53
  • Thanks for helping. With the updated answer, line print(GetAnswer); returns "" (null). I forced my method in dll to return "ok" for testing purposes, so i guess i'm not calling correctly the procedure. – bwk4u Mar 27 '17 at 16:58
  • Indeed. You need to declare a variable and pass it as an out parameter. Did you do that? – David Heffernan Mar 27 '17 at 17:01
  • Yes, see EDIT2 of main post. Now it debugs correctly, i can see how the method returns "ok" but in my delphi print i get null. – bwk4u Mar 27 '17 at 17:03
  • You've got to print the variable, r – David Heffernan Mar 27 '17 at 17:09
  • Wow, how the f*** I missed that... Thanks a lot my friend, that worked like a charm. – bwk4u Mar 27 '17 at 17:11