调用 Delphi DLL 的意外线程行为

2023-12-02

继续我的另一个问题:如何将内存流从我的应用程序传递到 DLL 或从 DLL 检索内存流?

我已经使用 DLL 编写了IStream作为输入/输出。该DLL使用IXMLDocument(起初我认为这与以下问题有关) 测试了一下,在主 UI 中运行良好。当我从工作线程调用 DLL 时,问题就开始了。

The DLL:

library MyDLL;

uses
  Windows,
  Variants,
  SysUtils,
  Classes,
  AxCtrls,
  ActiveX,
  XMLDoc,
  XMLIntf;

{$R *.res}    

procedure Debug(V: Variant);
begin
  OutputDebugString(PChar(VarToStr(V)));
end;

procedure DoProcess(InStream, OutStream: TStream);
var
  Doc: IXMLDocument;
begin
  InStream.Position := 0;
  Doc := TXMLDocument.Create(nil);
  Doc.LoadFromStream(InStream);
  // plans to do some real work...
  OutStream.Position := 0;
  Debug('MyDLL DoProcess OK');
end;

function Process(AInStream, AOutStream: IStream): Integer; stdcall;
var
  InStream, OutStream: TStream;
begin
  try
    InStream := TOleStream.Create(AInStream);
    try
      OutStream := TOleStream.Create(AOutStream);
      try
        DoProcess(InStream, OutStream);
        Result := 0;
      finally
        OutStream.Free;
      end;
    finally
      InStream.Free;
    end;
  except
    on E: Exception do
    begin
      Result := -1;
      Debug('MyDLL Error: ' + E.Message);
    end;
  end;
end;

exports
  Process;

begin
end.

我的来电应用程序:

implementation

uses
  ActiveX,ComObj;

{$R *.dfm}

procedure Debug(V: Variant);
begin
  OutputDebugString(PChar(VarToStr(V)));
end;

const
  MyDLL = 'MyDLL.dll';

{$DEFINE STATIC_DLL}
{$IFDEF STATIC_DLL}
function Process(AInStream, AOutStream: IStream): Integer; stdcall; external MyDLL;
{$ENDIF}

type
  // Dynamic
  TDLLProcessProc = function(AInStream, AOutStream: IStream): Integer; stdcall;

function DLLProcess(AInStream, AOutStream: TStream): Integer;
var
  InStream, OutStream: IStream;
  Module: HMODULE;
  DLLProc: TDLLProcessProc;
begin
  InStream := TStreamAdapter.Create(AInStream, soReference);
  OutStream := TStreamAdapter.Create(AOutStream, soReference);
{$IFDEF STATIC_DLL}
  Result := Process(InStream, OutStream); // Static
  Exit;
{$ENDIF}
  // Dynamic load DLL ...
  Module := LoadLibrary(MyDLL);
  if Module = 0 then RaiseLastOSError;
  try
    DLLProc := GetProcAddress(Module, 'Process');
    if @DLLProc = nil then RaiseLastOSError;
    Result := DLLProc(InStream, OutStream);
  finally
    FreeLibrary(Module);
  end;
end;

type
  TDLLThread = class(TThread)
  private
    FFileName: string;
  public
    constructor Create(CreateSuspended: Boolean; AFileName: string);
    procedure Execute(); override;
  end;

constructor TDLLThread.Create(CreateSuspended: Boolean; AFileName: string);
begin
  FreeOnTerminate := True;
  FFileName := AFileName;
  inherited Create(CreateSuspended);
end;

procedure TDLLThread.Execute;
var
  InStream, OutStream: TMemoryStream;
  RetValue: Integer;
begin
  try
    //CoInitializeEx(nil, COINIT_APARTMENTTHREADED);
    CoInitialize(nil);
    try
      InStream := TMemoryStream.Create;
      try
        InStream.LoadFromFile(FFileName);
        OutStream := TMemoryStream.Create;
        try
          RetValue := DLLProcess(InStream, OutStream);
          Sleep(0);
          Debug('TDLLThread Result=> ' + IntToStr(RetValue));
          if RetValue = 0 then
          begin
            Debug('TDLLThread OK');
          end;
        finally
          OutStream.Free;
        end;
      finally
        InStream.Free;
      end;
    finally
      CoUninitialize;
    end;
  except
    on E: Exception do
    begin
      Debug('TDLLThread Error: ' + E.Message);
    end;
  end;
end;

procedure TForm1.Button1Click(Sender: TObject); // Test
var
  I: Integer;
begin
  for I := 1 to 5 do
    TDLLThread.Create(False, '1.xml');
end;

当运行一些测试时我有时获取即使异常块也无法捕获的访问冲突。程序就崩溃了Runtime error 216 at xxxxxxx or Invalid pointer operation.

我尝试过静态和动态 DLL 链接(图maybe动态链接在 LoadLibrary/FreeLibrary 中存在竞争条件)。

首先我想到IXMLDocument主要问题是:

Doc := TXMLDocument.Create(nil);
Doc.LoadFromStream(InStream);

有时,这会随机失败,没有明显的原因:

在顶层无效 文档。

Or:

名称以无效字符开头。

我想也许它使用了一些共享资源。但即使省略这些行引发AV!

所以 DLL 实际上没有做任何特别的事情。 我也没有看到任何可能感染的特殊情况DLLMain.

我不知道发生了什么事...有人可以建议如何处理这种情况吗? (有人可以重现这种行为吗?)


编辑:我只是想添加一个相关的问题(类似IsMultiThread解决方案):Delphi DLL - 线程安全

以及一些关于IsMultiThread: Is多线程变量


Delphi 中的内存管理器针对单线程使用进行了优化。这些是默认启用的。如果您的代码是多线程的,那么需要禁用此优化。通过设置来做到这一点IsMultiThread to True.

在创建 Delphi 线程的模块中,框架设置IsMultiThread to True当创建一个线程时。在您的程序中,线程是由主机创建的,因此库中没有设置任何内容IsMultiThread to True。因此您必须在 DLL 中显式地执行此操作。在库 .dpr 文件的主要部分中写入以下内容:

begin
  IsMultiThread := True;
end.
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

调用 Delphi DLL 的意外线程行为 的相关文章

  • 原始类型是易失性的还是同步的?

    在 Java 中 如果变量的大小小于或等于 32 位 则赋值是原子的 但如果变量的大小大于 32 位 则赋值不是原子的 在双重或长分配的情况下 使用什么 易失性 同步 会更有效 Like volatile double x y 同步不适用于
  • 如何在 Delphi DBLookupComboBox 中选择正确的项目

    我有一个数据库查找组合框连接到数据库查询 那部分工作正常 当我运行程序时数据库查找组合框填充有查询的结果 我想看看数据库查找组合框填充第一项 请选择 当 的时候程序第一次运行或者当一个新项目行动已启动 见下图 另外 如果我正在加载以前保存的
  • 以编程方式重新启动 Delphi 应用程序

    应该不可能运行我的应用程序的多个实例 因此项目源码包含 CreateMutex nil False PChar ID if GetLastError ERROR ALREADY EXISTS then Halt 现在我想以编程方式重新启动我
  • 带等待/通知的同步块与不带等待/通知的同步块之间的区别?

    如果我只是使用synchronized 不是wait notify方法 它仍然是线程安全的吗 有什么不同 Using synchronized使方法 块一次只能由一个线程访问 所以 是的 它是线程安全的 这两个概念是结合在一起的 而不是相互
  • 为什么绿色线程不能在多核上工作

    在维基百科上 绿色线程 http en wikipedia org wiki Green threads被描述为通常无法在多核上运行 而没有解释原因 在多核处理器上 本机线程实现可以 自动将工作分配给多个处理器 而绿色线程 实现通常不能 我
  • 修改代码以从 Windows 中的 PE 可执行文件检索双重签名信息?

    我已经挣扎了一段时间想要修改这段代码示例 https support microsoft com en us help 323809 how to get information from authenticode signed execu
  • std::list 线程push_back、front、pop_front

    std list 线程安全吗 我假设不是这样 所以我添加了自己的同步机制 我认为我有正确的术语 但我仍然遇到问题 每个函数都由单独的线程调用 Thread1 不能等待 它必须尽可能快 std list
  • 在 Delphi 中使用 XML(将特定数据返回到变量)

    过去几天我一直在尝试使用 Delphi 2010 和 MSXML 我是一个极端的新手 需要一点指导 var MemoryStream TMemoryStream XMLPath String sName String XMLDoc vari
  • 我们可以使用 x86_64 CPU 原子在 PCI Express 上生成复合原子操作吗?

    如您所知 从2 0版本开始 PCI Express支持复合原子操作 FetchAdd Swap CAS https pcisig com sites default files specification documents ECN Ato
  • delphi中如何实现多重继承?

    我正在对一个旧库进行完全重写 我不确定如何处理这种情况 为了便于理解 大家都欢呼自行车类比 我有以下课程 TBike 自行车本身 TBikeWheel 自行车的一个轮子 TBikeWheelFront and TBikeWheelBack
  • 使用来自多个 kafka 主题的消息的最佳实践是什么?

    我需要消费来自不同卡夫卡主题的消息 我是否应该为每个主题创建不同的消费者实例 然后根据分区数量启动一个新的处理线程 或者 我应该从单个消费者实例订阅所有主题 并且应该启动不同的处理线程 感谢和问候 梅加 唯一的规则是 您必须考虑 Kafka
  • 防止重入并确保某些操作获取锁的正确方法是什么?

    我正在设计一个基类 当继承该基类时 它将针对多线程环境中的上下文提供业务功能 每个实例可能都有长时间运行的初始化操作 所以我想让这些对象可重用 为此 我需要能够 为这些对象之一分配上下文以允许其完成工作 防止对象在已有上下文的情况下被分配新
  • 使用 Thread.Sleep 等待的替代方法

    首先我不是在问同样的问题C Thread Sleep 的替代方案 https stackoverflow com questions 5450353 c sharp alternative to thread sleep or C 中 Th
  • Final字段的线程安全

    假设我有一个 JavaBeanUser这是从另一个线程更新的 如下所示 public class A private final User user public A User user this user user public void
  • 我如何才能等待多个事情

    我正在使用 C 11 和 stl 线程编写一个线程安全队列 WaitAndPop 方法当前如下所示 我希望能够将一些内容传递给 WaitAndPop 来指示调用线程是否已被要求停止 如果 WaitAndPop 等待并返回队列的元素 则应返回
  • 临时表是线程安全的吗?

    我正在使用 SQL Server 2000 它的许多存储过程广泛使用临时表 数据库的流量很大 我担心创建和删除临时表的线程安全性 假设我有一个存储过程 它创建了一些临时表 它甚至可以将临时表连接到其他临时表等 并且还可以说两个用户同时执行存
  • 如何在运行时(Delphi/Windows)程序中添加代码?

    我正在Windows XP Delphi 7上工作 我需要在正在运行的程序中添加一些过程 或函数 并且我不想在完成后再次重新编译它 我只有一个具有 5 个功能的主机应用程序来发送不同类型的警报 但是还有其他新的警报类型 所以我必须执行新的功
  • asp.net core / kestrel中的线程管理

    我正在解决我们已迁移到 asp net core 2 0 的 asp net 应用程序的性能 可扩展性问题 我们的应用程序作为应用程序服务托管在 azure 上 并且在任何中等流量的情况下都很容易崩溃 让我困惑的一件事是如何处理多个并发请求
  • Delphi 5 中的 Oracle 数据库连接

    我正在使用 Delphi 5 版本 我想连接到 Oracle 数据库 我有 TDatabase 组件 我不知道如何通过 Delphi 连接到数据库 请提供连接数据库的步骤 谢谢 The TDatabase http docwiki emba
  • Java执行器服务线程池[关闭]

    很难说出这里问的是什么 这个问题是含糊的 模糊的 不完整的 过于宽泛的或修辞性的 无法以目前的形式得到合理的回答 如需帮助澄清此问题以便重新打开 访问帮助中心 help reopen questions 如果我使用 Executor 框架在

随机推荐