调用Delphi DLL的意外线程行为 [英] Unexpected Thread behaviour calling Delphi DLL

查看:67
本文介绍了调用Delphi DLL的意外线程行为的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

继续我的另一个问题: 如何通过并从我的应用程序检索到DLL的内存流?

Continue from my other question: How do I pass and retrieve memory stream from my Application to/from DLL?

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

I have wrote the DLL using IStream as input/output. The DLL uses IXMLDocument (which at first I thought was related to the follow problem) Tested it, and it worked well in the main UI. Problems began when I was calling the DLL from a worker thread.

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 xxxxxxxInvalid pointer operation而崩溃.

When running some tests I sometimes get Access Violations which even the exceptions blocks can't catch. And the program simply crashes with Runtime error 216 at xxxxxxx or Invalid pointer operation.

我尝试了静态和动态DLL链接(图也许动态链接在LoadLibrary/FreeLibrary中具有竞争条件).

I have tried both static and dynamic DLL linking (figured maybe the dynamic linking has race condition in the LoadLibrary/FreeLibrary).

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

First I thought IXMLDocument was the main issue:

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

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

This sometimes randomly failed with no apparent reason with:

在顶部的无效 文档.

Invalid at the top level of the document.

或者:

名称以无效字符开头.

A name was started with an invalid character.

我认为也许它使用了一些共享资源.但甚至忽略这些行也会导致AV!

I thought maybe it used some shared resources. but even omitting these lines caused AVs!

因此DLL实际上没有做任何特别的事情. 我也看不到任何可能感染DLLMain的特殊东西.

So the DLL is practically doing nothing special. I also Don't see anything special which could infect DLLMain.

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

I have no Idea what is going on... Can someone suggest how to handle this situation? (Can someone reproduce this behavior?)

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

I just wanted to add a related question (with similar IsMultiThread solution): Delphi DLL - thread safe

以及有关IsMultiThread的一些提示: IsMultiThread变量

And some tips about IsMultiThread: IsMultiThread Variable

推荐答案

Delphi中的内存管理器针对单线程使用进行了优化.这些默认情况下处于启用状态.如果您的代码是多线程的,则需要禁用此优化.通过将IsMultiThread设置为True来执行此操作.

The memory manager in Delphi has optimisations for single threaded use. These are enabled by default. If your code is multi-threaded then this optimisation needs to be disabled. Do that by setting IsMultiThread to True.

在创建Delphi线程的模块中,框架在创建线程时将IsMultiThread设置为True.在您的程序中,线程是由主机创建的,因此库中没有任何内容将IsMultiThread设置为True.因此,您必须在DLL中明确地执行该操作.在库.dpr文件的主要部分中编写以下代码:

In a module that creates a Delphi thread, the framework sets IsMultiThread to True when a thread is created. In your program the threads are created by the host and so nothing in the library sets IsMultiThread to True. So you must do that explicitly in the DLL. In the main section of the library .dpr file write this:

begin
  IsMultiThread := True;
end.

这篇关于调用Delphi DLL的意外线程行为的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆