出于某些原因,我需要将TIdHTTPServer
的实例放入DLL中。这是这样做的:
接口单元:
unit DLL.Intf;
interface
type
IServer = interface
procedure DoSomethingInterfaced();
end;
implementation
end.
服务器的容器:
unit Server;
interface
uses
DLL.Intf,
IdHTTPServer,
IdContext,
IdCustomHTTPServer;
type
TServer = class(TInterfacedObject, IServer)
private
FHTTP: TIdHTTPServer;
procedure HTTPCommandGet(AContext: TIdContext; ARequestInfo: TIdHTTPRequestInfo;
AResponseInfo: TIdHTTPResponseInfo);
procedure DoSomethingInterfaced();
public
constructor Create();
destructor Destroy(); override;
end;
function GetInstance(): IServer;
implementation
uses
SysUtils;
var
Inst: IServer;
function GetInstance(): IServer;
begin
if not Assigned(Inst) then
Inst := TServer.Create();
Result := Inst;
end;
constructor TServer.Create();
begin
inherited;
FHTTP := TIdHTTPServer.Create(nil);
FHTTP.OnCommandGet := HTTPCommandGet;
FHTTP.Bindings.Add().SetBinding('127.0.0.1', 15340);
FHTTP.Active := True;
end;
destructor TServer.Destroy();
begin
FHTTP.Free();
inherited;
end;
procedure TServer.DoSomethingInterfaced();
begin
end;
procedure TServer.HTTPCommandGet(AContext: TIdContext; ARequestInfo: TIdHTTPRequestInfo;
AResponseInfo: TIdHTTPResponseInfo);
begin
AResponseInfo.ContentText := '<html><h1>HELLO! ' + IntToStr(Random(100)) + '</h1></html>';
end;
end.
DLL导出GetInstance()
函数:
library DLL;
uses
SysUtils,
Classes,
Server in 'Server.pas',
DLL.Intf in 'DLL.Intf.pas';
{$R *.res}
exports
GetInstance;
begin
end.
服务器加载并正常工作,直到我退出主EXE文件。调试器显示主线程挂起FHTTP.Free();
。
我认为我不需要担心线程同步,因为我对EXE和DLL项目使用“Build with runtime packages”选项。
如何修复此问题?
答案 0 :(得分:2)
我的解决方案是在关闭应用程序的主要表单时将Active
的{{1}}属性设置为TIdHTTPServer
。
我想服务器必须在退出消息循环之前停止其所有线程并与主线程同步。
如果它能解释背后的机制,我会检查另一个答案是否正确。
答案 1 :(得分:1)
如果您的代码与TIdHTTPServer
事件中的主要线程同步,例如OnCommandGet
,则只会发生您所描述的内容。但是在你展示的代码中没有这样做,所以不应该阻止TIdHTTPServer
析构函数正常退出。在内部,析构函数将Active
属性设置为False,它会等待任何活动线程完全终止。 TIdHTTPServer
内部没有与主线程同步的内容。在与主线程同步时从主线程中取消TIdHTTPServer
将导致死锁(因此,如果禁用运行时软件包,则会在DLL内部调用TThread.Synchronize()
,您可以说它们不是)。所以你所描述的内容毫无意义。您只需在调试器中单步执行TIdHTTPServer析构函数即可找到实际的死锁。