未在dll中运行的结束部分



我试图在Delphi XE2中创建一个DLL,它将弹出一个带有TWebBrowser组件的表单。当网络浏览器。当应用程序结束时,不调用单元(或任何单元)的终结部分。如果未调用Navigate2,则结束部分将正常运行。

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

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

有人知道发生了什么事吗?

谢谢。

下面是这个问题的简单再现:

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中的dbeexpress驱动程序)时,我将其从带有导入库的静态链接dll更改为动态加载的dll,一切都开始工作。

在DLL初始化/结束时不要调用CoInitialize()CoUninitialize()。这是一个非常糟糕的地方,此外,调用它们并不是DLL的责任。这是调用DLL函数的线程的责任。如果必须调用它们,那么至少在导出函数中调用它们。

对于导出函数本身,用WebBrowser1.Parent代替WebBrowser1.ParentWindow,用Form1.Free代替Form1.Release,完全去掉Application.ProcessMessages

最后,不要使用手动修饰的名称导出函数。这也不是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.

Delphi没有大量使用普通dll,它的支持是基本的,几乎没有文档

虽然Delphi为EXE文件做得很好,拦截WinMain并将其语义引入Turbo Pascal风格的上下文,但对于DLL,您必须手动完成。

首先阅读DLL-Main Microsoft文档和教程。

然后你可以在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 =};

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

  • 对于手动加载的包(通过LoadPackage(...)调用),所有单元被授予的完成调用
  • 对于手动加载的包(通过Project Options / Packages / Link with Runtime packages列表),对所有单元调用最终化,在主机EXE的"uses"部分中引用。

pp。仅仅为了显示一个页面而启动MSIE——这看起来是不是有点过头了?也许本地HTML支持就足够了,即使有一些限制?它能够从TStream或String加载页面,而不需要对中间临时文件进行修补。(嗯,MSIE也可以,不过,经过一些脚手架)。

您可能会发现,当其中一个单元正在完成时,会引发异常,从而阻止其他单元的完成。

我不确定XE2,但老版本的Delphi往往是非常挑剔的ComObj单元是"高"在使用/初始化,所以它将是最后完成之一。
问题是,如果ComObj完成得太快,它就会过早地进行CoUninitialize——有效地破坏了其他仍然期望初始化COM的代码。

如果XE2版本的SHDocVw在其实现部分仍然使用ComObj,那么ComObj将相对"较晚"初始化。所以这很可能是你的问题。在这种情况下,只要在源代码中显式地添加它就可以了。

最新更新