【问题标题】:Access violation when calling Delphi DLL from C# in a multi-threaded environment在多线程环境中从 C# 调用 Delphi DLL 时访问冲突
【发布时间】:2015-05-12 11:29:49
【问题描述】:

我正在使用 P/Invoke 从 C# 调用用 Delphi XE2 编写的 DLL 函数。当从单个线程按顺序进行调用时,它似乎正在工作。但是,当多个线程调用该函数时,C# 宿主应用程序会看似随机地抛出System.AccessViolationException

为什么下面的代码会触发访问冲突,我该如何解决?

重现问题的最少 Delphi 库代码:

library pinvokeproblem;

{$R *.res}

uses Windows, SysUtils;

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

exports Test;

end.

重现问题的最少 C# 宿主应用程序代码:

[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));
        });
     }
}

补充资料:

  • 平台是 x64 Windows 7。在 .NET 4.0 中为 x86 构建的 C# 主机应用程序,为 32 位编译的 Delphi DLL。

  • 在多线程 Delphi 主机应用程序中使用时,该库似乎工作正常。

  • 具有函数签名extern __declspec(dllexport) void __stdcall Test(char testByte) 的 DLL 的 MSVC 版本可以在 C# 主机上正常工作(这表明这在某种程度上特定于 Delphi)。

  • 如果库函数没有返回值 (void) 和参数,代码不会失败。

  • 将两个代码中的调用约定更改为 cdecl 没有帮助。

任何想法都将不胜感激。

【问题讨论】:

  • 看起来问题可能出在 OutputDebugString(PWideChar(IntToStr(testByte)));在您的德尔福代码中。我不确定您是否可以将单个字节转换为 WideChar。我不是 Delphi 专家,但我觉得这句话有些不对劲。
  • @mageos 这个说法很好
  • @mageos:谢谢。该代码是有效的,我的 Delphi 代码中似乎根本没有出现这个问题。当我将 Delphi 代码包装到 try..catch 块中时,我从未设法捕获异常。

标签: c# .net multithreading delphi pinvoke


【解决方案1】:

您所要做的就是将IsMultiThread 设置为True(作为您的DLL 主begin..end 块中的第一行)将内存管理器切换到线程安全模式:

IsMultiThread := True;

【讨论】:

  • 天啊,我怎么忘记了!谢谢!
  • 欢迎。比较容易忘记。不久前我遇到了一个非常相似的问题,巧合的是 C# 互操作。
  • @TOndrej:谢谢,我从来没有想过要手动设置它(在我的辩护中,通常我使用 COM 互操作,其中类工厂会自动设置它,否则我会启动自己的线程来执行一样)。
  • 没错,通常你不需要这样做,因为相关的单元和类会在它们的初始化过程中为你做这件事。
  • 我的代码库在始终使用的单元的初始化部分中有IsMultiThread := True。坦率地说,今天 IsMultiThread 有点过时了。
猜你喜欢
  • 1970-01-01
  • 2012-09-12
  • 1970-01-01
  • 2010-11-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-03-19
  • 1970-01-01
相关资源
最近更新 更多