继续我的另一个问题: 如何从我的应用程序传递和检索内存流到/从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
Delphi中的内存管理器对单线程使用进行了优化.这些是默认启用的.如果您的代码是多线程的,则需要禁用此优化.通过设置IsMultiThread
来做到这一点True
.
在创建一个Delphi线程模块,框架设置IsMultiThread
,以True
创建一个线程时.在你的程序的线程由主机创建的,所以没有在图书馆设置IsMultiThread
到True
.所以你必须在DLL中明确地这样做.在库.dpr文件的主要部分写这个:
begin IsMultiThread := True; end.