Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Access violation when calling Delphi DLL from C# in a multi-threaded environment

I am calling a DLL function written in Delphi XE2 from C# using P/Invoke. It appears to be working when calls are made sequentially from a single thread. However, when multiple threads are calling the function, the C# host application throws System.AccessViolationException seemingly at random.

Why does the code below trigger an access violation and how do I fix this?

Minimum Delphi library code for reproducing the problem:

library pinvokeproblem;

{$R *.res}

uses Windows, SysUtils;

procedure Test(const testByte: byte); stdcall;
begin
  OutputDebugString(PWideChar(IntToStr(testByte)));
end;

exports Test;

end.

Minimum C# host application code to reproduce the problem:

[DllImport(
    "pinvokeproblem.dll",
    CallingConvention = CallingConvention.StdCall,
    EntryPoint = "Test")]
private static extern void Test(byte testByte);

public static void Main(string[] args)
{
    for (int i = 1; i <= 1000; i++) // more iterations = better chance to fail
    {
        int threadCount = 10;
        Parallel.For(1, threadCount, new ParallelOptions { MaxDegreeOfParallelism = threadCount }, test =>
        {
            byte byteArgument = 42;
            Test(byteArgument);
            Console.WriteLine(String.Format("Iteration {0}: {1}", test, byteArgument));
        });
     }
}

Additional information:

  • Platform is x64 Windows 7. C# host application built for x86 in .NET 4.0, Delphi DLL compiled for 32-bit.

  • The library appears to be working OK when used in a multi-threaded Delphi host application.

  • An MSVC version of the DLL with the function signature extern __declspec(dllexport) void __stdcall Test(char testByte) works fine with the C# host (which suggests this is somehow specific to Delphi).

  • The code will not fail if the library function has no return value (void) and arguments.

  • Changing the calling convention in both code to cdecl did not help.

Any ideas would be much appreciated.

like image 384
PeterK Avatar asked May 12 '15 11:05

PeterK


1 Answers

All you have to do is set IsMultiThread to True (as the first line in your DLL's main begin..end block) to switch the memory manager to a thread-safe mode:

IsMultiThread := True;
like image 83
Ondrej Kelle Avatar answered Nov 05 '22 05:11

Ondrej Kelle