为什么我的 Delphi dll 中需要 Sharemem,它只公开一个带有 WideString 参数的函数?
Why do I need Sharemem in my Delphi dll which only exposes a function with WideString parameters?
我有一个 dll 和一个用 Delphi 编写的测试应用程序。测试应用使用多线程调用dll导出的函数。导出的函数具有简单的线程安全实现。当 运行 测试应用程序时,会发生各种错误(访问冲突、无效指针操作、堆栈溢出等)或应用程序冻结。在某些情况下,应用程序完成时没有错误。
请注意,这些错误仅在使用多线程时发生(表面)。只有从主线程调用函数时一切正常。
我发现将 ShareMem 添加到 dll 和应用程序可以停止所有这些类型的错误。但我不明白为什么。据我所知,只有在 dll 和应用程序之间传递长字符串时才需要 ShareMem。据我所知 WideString 不是一个长字符串。
另外根据这个 post ShareMem 应该不需要:
Why can Delphi DLLs use WideString without using ShareMem?
这里是 dll 的源代码:
library External;
uses
Winapi.Windows;
type
TMyType = class
private
FText: string;
end;
function DoSomething(input: WideString; out output: WideString): Bool; stdcall;
var
x: TObject;
begin
x := TMyType.Create;
try
output := x.ClassName;
finally
x.Free;
end;
Result := True;
end;
exports
DoSomething;
begin
end.
这是测试应用程序:
program ConsoleTest;
{$APPTYPE CONSOLE}
uses
System.SysUtils,
Winapi.Windows,
OtlParallel;
function DoSomething(input: WideString; out output: WideString): Bool; stdcall; external 'External.dll' name 'DoSomething';
var
sResult: WideString;
begin
try
Parallel.&For(0, 500).Execute(procedure(value: Integer)
var
sResult: WideString;
begin
DoSomething('hhh', sResult);
end);
WriteLn('Done');
ReadLn;
except
on E: Exception do
Writeln(E.ClassName, ': ', E.Message);
end;
end.
为什么 ShareMem 可以消除错误,还有其他方法可以修复这些错误吗?
我正在使用 Delphi XE2 和 OmniThread 3.07.5。
更新
- 当 运行 来自 VCL 应用程序按钮的单击事件处理程序时,同样的问题
- 如果 DoSomething 在内部使用临界区,那么 运行 没问题
- 如果从 TMyClass 中删除 FText 字段,则不会报告任何错误,但应用程序会随机冻结
为了让标准内存管理器(FastMM)支持多线程,您需要设置IsMultiThread
标志。
当您使用 RTL 进行线程处理时,会自动设置此标志。正如对问题的评论所揭示的那样,OTL 也使用 RTL 来启动它的线程。所以可执行文件中的内存管理器知道线程,但 dll 中不同的内存管理器会导致错误。当您使用 "sharemem" 时,由于 OTL,只有一个内存管理器知道线程,因此您不会遇到任何错误。
除了使用共享内存管理器之外,另一种解决方案是在 dll 中也为内存管理器设置标志。
我有一个 dll 和一个用 Delphi 编写的测试应用程序。测试应用使用多线程调用dll导出的函数。导出的函数具有简单的线程安全实现。当 运行 测试应用程序时,会发生各种错误(访问冲突、无效指针操作、堆栈溢出等)或应用程序冻结。在某些情况下,应用程序完成时没有错误。
请注意,这些错误仅在使用多线程时发生(表面)。只有从主线程调用函数时一切正常。
我发现将 ShareMem 添加到 dll 和应用程序可以停止所有这些类型的错误。但我不明白为什么。据我所知,只有在 dll 和应用程序之间传递长字符串时才需要 ShareMem。据我所知 WideString 不是一个长字符串。
另外根据这个 post ShareMem 应该不需要: Why can Delphi DLLs use WideString without using ShareMem?
这里是 dll 的源代码:
library External;
uses
Winapi.Windows;
type
TMyType = class
private
FText: string;
end;
function DoSomething(input: WideString; out output: WideString): Bool; stdcall;
var
x: TObject;
begin
x := TMyType.Create;
try
output := x.ClassName;
finally
x.Free;
end;
Result := True;
end;
exports
DoSomething;
begin
end.
这是测试应用程序:
program ConsoleTest;
{$APPTYPE CONSOLE}
uses
System.SysUtils,
Winapi.Windows,
OtlParallel;
function DoSomething(input: WideString; out output: WideString): Bool; stdcall; external 'External.dll' name 'DoSomething';
var
sResult: WideString;
begin
try
Parallel.&For(0, 500).Execute(procedure(value: Integer)
var
sResult: WideString;
begin
DoSomething('hhh', sResult);
end);
WriteLn('Done');
ReadLn;
except
on E: Exception do
Writeln(E.ClassName, ': ', E.Message);
end;
end.
为什么 ShareMem 可以消除错误,还有其他方法可以修复这些错误吗?
我正在使用 Delphi XE2 和 OmniThread 3.07.5。
更新 - 当 运行 来自 VCL 应用程序按钮的单击事件处理程序时,同样的问题 - 如果 DoSomething 在内部使用临界区,那么 运行 没问题 - 如果从 TMyClass 中删除 FText 字段,则不会报告任何错误,但应用程序会随机冻结
为了让标准内存管理器(FastMM)支持多线程,您需要设置IsMultiThread
标志。
当您使用 RTL 进行线程处理时,会自动设置此标志。正如对问题的评论所揭示的那样,OTL 也使用 RTL 来启动它的线程。所以可执行文件中的内存管理器知道线程,但 dll 中不同的内存管理器会导致错误。当您使用 "sharemem" 时,由于 OTL,只有一个内存管理器知道线程,因此您不会遇到任何错误。
除了使用共享内存管理器之外,另一种解决方案是在 dll 中也为内存管理器设置标志。