终结部分没有在dll中运行

时间:2013-03-18 19:00:52

标签: delphi twebbrowser

我正在尝试在Delphi XE2中创建一个DLL,它将弹出一个带有TWebBrowser组件的表单。调用WebBrowser.Navigate2方法时,应用程序结束时不会调用单元(或任何单元)的终结部分。如果未调用Navigate2,则完成部分就可以了。

目前正在从C ++(VS 2010 MFC控制台)调用dll,并通过导入库进行链接。

还有其他方法可以做到这一点,但我想重用我们已编写的代码。

有谁知道发生了什么事?

感谢。

这是一个简单的问题重现:

library DisplayPatientAlertsIntf;
exports DisplayPatientAlertsA name 'DisplayPatientAlertsA@4';

begin
end.

unit uAlertWindow;

interface

uses
  Winapi.ActiveX,
  Forms,
  SHDocVw,
  Graphics, Controls;

function DisplayPatientAlertsA(PatientID : PAnsiChar): Integer; export; stdcall;

implementation

var ts : TStringList;

function DisplayPatientAlertsA(PatientID : PAnsiChar): Integer; export; stdcall;
  var Form1 : TForm;
      WebBrowser1 : TWebBrowser;
      DidCoInit : Boolean;
begin
  DidCoInit := Succeeded(CoInitialize(nil));
  try
    Form1 := TForm.Create(nil);
    try
      WebBrowser1 := TWebBrowser.Create(nil);
      try
        WebBrowser1.ParentWindow := Form1.Handle;
        WebBrowser1.Align := alClient;
        WebBrowser1.Navigate2('file://c:\temp.html');
        Form1.ShowModal;
      finally
        WebBrowser1.Free;
      end;
    finally
      Form1.Free;
    end;
  finally
    if DidCoInit then
      CoUninitialize;
  end;
  Result := 0;
end;

initialization
  ts := TStringList.Create;

finalization
  ts.Free;

end.

更新2013.03.19 在解决另一个问题(dll中的dbExpress驱动程序)时,我将它从带有导入库的静态链接dll更改为动态加载的dll,一切都开始工作。

3 个答案:

答案 0 :(得分:6)

在DLL的初始化/完成期间,不要调用CoInitialize()CoUninitialize()。这是一个非常糟糕的地方,而且,无论如何调用它们都不是DLL的责任。调用DLL函数的线程负责。如果你必须调用它们,那么至少在你导出的函数内部这样做。

对于导出的函数本身,请使用WebBrowser1.Parent而不是WebBrowser1.ParentWindow,使用Form1.Free代替Form1.Release,并完全删除Application.ProcessMessages。< / p>

最后,不要使用手动装饰的名称导出该功能。这也不是DLL的责任。让编译器处理装饰。如果在导入函数时存在命名不匹配,则需要在调用应用程序中解决,而不是DLL本身。

您滥用COM和VCL(特别是因为只有在调用导出的DLL函数时才会出现问题)可能会导致死锁,从而阻止DLL正确地从内存中卸载,因此它的最终部分都不是调用因为无法调用其DLL入口点。 COM在初始化/清理时非常敏感,因此您必须确保在正确的上下文中正确执行。

试试这个:

library DisplayPatientAlertsIntf;

uses
  uAlertWindow;

exports
  DisplayPatientAlertsA;

begin
end.

unit uAlertWindow;

interface

uses
  Winapi.ActiveX,
  Forms,
  SHDocVw,
  Graphics, Controls;

function DisplayPatientAlertsA(PatientID : PAnsiChar): Integer; stdcall;

implementation

function DisplayPatientAlertsA(PatientID : PAnsiChar): Integer; stdcall;
var
  Form1 : TForm;
  WebBrowser1 : TWebBrowser;
  DidCoInit: Boolean;
begin
  Result := 0;
  try
    DidCoInit = Succeeded(CoInitialize(nil));
    try    
      Form1 := TForm.Create(nil);
      try
        WebBrowser1 := TWebBrowser.Create(Form1);
        WebBrowser1.Parent := Form1;
        WebBrowser1.Align := alClient;
        WebBrowser1.Navigate2('file://c:\temp.html'); //This contains 'ASDF'
        Form1.ShowModal;
      finally
        Form1.Free;
      end;
    finally
      if DidCoInit then
        CoUninitialize;
    end;
  except
    Result := -1;
  end;
end;

end.

答案 1 :(得分:0)

Delphi没有大量使用普通的DLL,它的支持是基本的,几乎没有记录

虽然Delphi可以很好地处理EXE文件,拦截WinMain并将其语义带到Turbo Pascal样式上下文,但对于DLL,你必须手动完成。

首先阅读DLL-Main Microsoft documentation and tutorials

然后您可以添加到DLL.dpr类似

的内容中
begin
  DLLProc := @DLLMain;
  DLLMain(DLL_PROCESS_ATTACH);
end.

然后在DLL的某个单元中,您可以像这样实现它:

procedure DLLMain(dwReason: DWORD);
begin
  case dwReason of
  DLL_PROCESS_ATTACH:
    begin
      Application.HelpFile := HelpFileName;
      dmSelVars := TdmSelVars.Create(nil);
    end {= DLL_PROCESS_ATTACH =};

  DLL_PROCESS_DETACH:
    begin
      Application.Handle := 0;
      FreeAndNil(dmSelVars);
      g_pSvRec := nil;
    end {= DLL_PROCESS_DETACH =};
  end {= case =};
end {= DLLMain =};

PS。为什么使用DLL,当你可以使用Delphi-native(自1997年)BPL代替? 它解决了许多问题,并提供了更好的终结支持:

  • 用于手动加载的包(通过LoadPackage(...)调用)终止调用已授予所有单元
  • 用于手动加载的包(通过Project Options / Packages / Link with Runtime packages列表)为所有单元调用终结,在主机EXE的“使用”部分中引用。

PPS。仅仅显示一个页面启动MSIE - 它看起来不是太过分了吗? 即使有些限制,也许native HTML support就足够了?并且它能够加载页面from TStreamfrom String,而无需修改中间临时文件。 (不过,在一些脚手架之后,MSIE也有能力。)

答案 2 :(得分:0)

您可能会发现正在最终确定其中一个单位时会引发异常,从而阻止其他单位被最终确定。

我不确定XE2,但是旧版本的Delphi对于ComObj单元在使用/初始化中处于“高位”状态往往非常挑剔,所以它将是最后一个完成的。 问题是,如果ComObj太快完成,那么CoUninitialize会很快 - 有效地从其他仍然期望COM被初始化的代码中剥离出来。

如果SHDocVw的XE2版本仍在其实现部分中使用ComObj,则ComObj将相对“晚”初始化。所以这很可能是你的问题。在这种情况下,只需在源代码中明确地添加它就可以完成。