当前位置:  开发笔记 > 编程语言 > 正文

调用Delphi DLL的意外线程行为

如何解决《调用DelphiDLL的意外线程行为》经验,为你挑选了1个好方法。

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

我用IStream输入/输出编写了DLL .DLL使用IXMLDocument(起初我认为与后续问题有关)测试它,它在主UI中运行良好.当我从工作线程调用DLL时出现问题.

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;

在运行某些测试时,我有时会遇到Access Violations,即使是异常块也无法捕获.该程序只是与Runtime error 216 at xxxxxxx或崩溃Invalid pointer operation.

我已经尝试过静态和动态DLL链接(想象一下,动态链接可能在LoadLibrary/FreeLibrary中有竞争条件).

首先我认为IXMLDocument是主要问题:

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

这有时随机失败,没有明显的理由:

在文档的顶级无效.

要么:

名称以无效字符开头.

我想也许它使用了一些共享资源.但即使省略这些行也会导致AV!

所以DLL实际上没什么特别的.我也没有看到任何可以感染的特殊物品DLLMain.

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


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

以及一些提示IsMultiThread: IsMultiThread Variable



1> David Heffer..:

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

在创建一个Delphi线程模块,框架设置IsMultiThread,以True创建一个线程时.在你的程序的线程由主机创建的,所以没有在图书馆设置IsMultiThreadTrue.所以你必须在DLL中明确地这样做.在库.dpr文件的主要部分写这个:

begin
  IsMultiThread := True;
end.

推荐阅读
ar_wen2402851455
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有