Delphi TIdHTTPServer在放入DLL时挂起Free()

Delphi TIdHTTPServer在放入DLL时挂起Free(),delphi,delphi-xe,indy,httpserver,idhttp,Delphi,Delphi Xe,Indy,Httpserver,Idhttp,出于某些原因,我需要将TIdHTTPServer的一个实例放入DLL中。是这样做的: 接口单元: unit DLL.Intf; interface type IServer = interface procedure DoSomethingInterfaced(); end; implementation end. unit Server; interface uses DLL.Intf, IdHTTPServer, IdContext, IdCus

出于某些原因,我需要将
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.
服务器的容器:

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.
服务器加载并正常工作,直到我退出主EXE文件。调试器显示主线程挂起在
FHTTP.Free()上

我认为我不需要担心线程同步,因为我对EXE和DLL项目都使用了“使用运行时包构建”选项


如何修复此挂起?

我的解决方案是在关闭应用程序的主窗体时,将
TIdHTTPServer
Active
属性设置为
false

我猜服务器必须停止所有线程,并在退出消息循环之前与主线程同步


我将检查另一个答案是否正确,如果它能够解释背后的机制。

您所描述的应该只发生在您的代码与
TIdHTTPServer
事件(如
OnCommandGet
中的主线程同步时。但是在您展示的代码中没有这样做,因此应该没有任何东西阻止
TIdHTTPServer
析构函数正常退出。在内部,析构函数会将
Active
属性设置为False,这会等待任何活动线程完全终止。
TIdHTTPServer
中没有任何内容与主线程同步。在与主线程同步时从主线程停用
TIdHTTPServer
,会导致死锁(如果运行时包被禁用,通常在DLL中调用
TThread.Synchronize()
,您会说它们没有被禁用)。所以你所描述的毫无意义。您只需在调试器中单步执行TIdHTTPServer析构函数即可找到实际的死锁。

如果您注释掉FHTTP.Free(),会发生什么情况?@MartinJames在不调用Free()时,只会导致内存泄漏,但不会挂起。这里面临同样的问题。有人有办法吗?也许。如果有必要,您可能会认为dtor会将属性本身设置为false。另外,我不认为任何Indy服务器/客户端本身执行任何主线程同步。不过,如果这个答案有效,谁在乎呢:)@MartinJames我希望Remy Lebeau稍后会解释这一点。我用Delphi XE准备了一个测试项目。你能看看这个吗?也许我错过了什么,但它真的挂起了
Free()
。只有在服务器运行时访问任何浏览器后,才会发生这种情况。