调用Delphi DLL的意外线程行为 [英] Unexpected Thread behaviour calling 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 xxxxxxx
或Invalid 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屋!