调用 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 的意外线程行为 的相关文章

  • 为什么 PathFileExists() 不起作用?

    我想验证文件是否存在 经过一番搜索后 我认为 PathFileExists 可能适合这项工作 但是 下面的代码总是显示该文件不存在 为了确保该文件确实存在 我选择cmd exe的完整路径作为测试文件路径 我使用的是 Windows 7 x6
  • Win32 函数获取 C:\ProgramData 的路径

    我的应用程序需要安装一些可以由应用程序在运行时编辑的文件 Installshield提供了一个别名 CommonAppDataFolder 它将在Vista和Windows 7上解析为c programData 并且也适用于Windows
  • 在没有“跨线程”的情况下,我可以从BackgroundWorker访问什么?

    我意识到我无法从BackgroundWorker 的DoWork 事件处理程序访问表单控件 如果我尝试这样做 我会得到一个异常 正如预期的那样 但是 我是否可以访问表单上存在的其他 自定义 对象 例如 我创建了一个 设置 类并在我的表单中实
  • Android CountDownTimer 类滞后主线程

    我正在尝试使用 android os CountDownTimer 通过文本视图字面显示倒计时器 以达到健身目的 我遇到的问题是计时器似乎在主线程上运行时遇到问题 即倒计时将跳 2 4 秒并且明显 滞后 计时器应处于无限循环状态 直到停止按
  • 加载 Jpg/Gif/Bitmap 并转换为 Bitmap

    我必须从 XML 文件加载图像 XML 文件中没有关于图像是否为 JPG GIF BMP 的信息 加载图像后 我需要将其转换为位图 有谁知道如何在不知道实际文件格式的情况下将图像转换为位图 我正在使用 Delphi 2007 2009 谢谢
  • 为什么我们不能在当前队列上使用dispatch_sync?

    我遇到了一个场景 我有一个委托回调 它可能发生在主线程或另一个线程上 并且直到运行时我才知道是哪个 使用StoreKit framework 我还需要在该回调中更新 UI 代码 该回调需要在函数执行之前发生 所以我最初的想法是拥有一个如下所
  • Delphi TImageList 位图更改

    我正在使用 Delphi XE2 Update 3 Update 4 与我们的一些第 3 方组件不兼容 因此我们尚未更新 我在我的应用程序中使用 TImageList 我注意到很多时候当它从源视图切换到表单视图 F12 时 突然之前未修改的
  • 无法设置指定的 COM 单元状态

    看来我真的不擅长多线程应用程序 我正在尝试打开一个FolderBrowserDialog 但我收到一个异常告诉我 Current thread must be set to single thread apartment STA mode
  • 在 C# 中查看非托管 dll 上的导出表

    我目前正在尝试创建一个 C 应用程序 该应用程序将允许我查看非托管 DLL 中的导出表 我的问题是 一旦我获得了所需的所有指针 我不知道如何循环访问 API 为我提供的信息 这是我现在所拥有的 using System using Syst
  • delphi 变量值在循环中的线程中发生变化

    我的代码正在运行一个 for 循环来处理一些数据 如下所示 procedure printValue Value Integer begin TThread Synchronize TThread Current procedure beg
  • 在非 UI 线程上运行 RIA 服务

    我正在尝试从非 UI 线程进行 RIA 服务调用 我通过打开新线程和后台工作人员进行了调用 但对于这两种情况 回调都在 UI 线程上运行 是否可以在调用者线程而不是 UI 上执行回调 Thanks tl dr Use WCF 自行将结果编组
  • 如何避免使用 WinApi.Windows 的 Delphi 应用程序中的 dll 劫持

    Delphi 最新版本使用各种系统 dll 的静态链接 例如 WinApi Windows 单元中的 version dll 这会导致在单元初始化之前加载 version dll 这会打开一个安全漏洞 可以通过将受感染的 version d
  • 使用 xerces 链接 DLL 会给出未定义的符号

    我正在使用 cygwin 创建一个共享库 DLL 它使用 Xerces 当我从主应用程序调用 xercesc 函数时 一切都很好 但是当我尝试将一些代码放入库中时 我会得到 xerxesc 定义的所有静态内容的未定义符号 例如 std st
  • 如何找到锁的所有者(监视器)?

    有没有办法发现哪个线程当前拥有锁 具体来说 我正在寻找一些代码来打印出阻止锁定的线程 我想尝试锁定给定的超时时间 然后报告哪个线程正在阻止锁定 不需要 只需编写代码 private int lockOwner private object
  • 使用 pythonw.exe 时 Python subprocess.call() 失败

    我有一些 Python 代码 当我使用 python exe 运行时可以正常工作 但如果我使用 pythonw exe 则失败 def runStuff commandLine outputFileName somefile txt out
  • 如何在 Delphi REST 中发布内容类型为“multipart/form-data”的数据?

    我正在尝试使用 REST API 发送请求multipart form data作为内容类型 我总是收到 HTTP 1 1 500 Internal Error 作为响应 我尝试向需要的方法发送请求application x www for
  • 什么样的应用程序需要多线程?

    什么是一些具体的例子需要或不需要多线程的应用程序 但这样会更好吗 如果答案以每个帖子一个申请的形式最好 这样最适用的就会浮到顶部 没有硬性且快速的答案 但大多数时候 您不会看到工作流程 计算是连续的系统有任何优势 然而 如果问题可以分解为可
  • 从 PDF 转换为 HTML [关闭]

    就目前情况而言 这个问题不太适合我们的问答形式 我们希望答案得到事实 参考资料或专业知识的支持 但这个问题可能会引发辩论 争论 民意调查或扩展讨论 如果您觉得这个问题可以改进并可能重新开放 访问帮助中心 help reopen questi
  • 为什么 Windows 只允许一个应用程序访问网络摄像头? [关闭]

    Closed 这个问题是无关 help closed questions 目前不接受答案 我一直在尝试用 C 制作一个示例网络摄像头应用程序 我发现该应用程序无法同时运行 Skype 或 Oovoo 或任何其他应用程序运行 反之亦然 为什么
  • 转储 Windows DLL 版本的命令行工具?

    我需要一个命令行工具来转储标准 Windows DLL 版本信息 以便我可以通过 bash 脚本 Cygwin 对其进行处理 作为一名 Java 开发人员 我不太习惯 Microsoft 开发工具 尽管我对 Microsoft Visual

随机推荐