Delphi:有条件地进行单元初始化吗?

时间:2014-05-17 08:32:08

标签: delphi module initialization gdi+

我的应用程序可以MasterWorker启动,它由启动参数控制。

program MyApp;
uses
  SysUtils, Forms, WorkerForm, MasterForm;

begin
  Application.Initialize;
  //if ParamStr(1) = 'WorkerProcess' then
  if FindCmdLineSwitch('--WorkerProcess') then
    Application.CreateForm(TWorkerForm, WorkerForm)
  else
    Application.CreateForm(TMasterForm, MasterForm);
  Application.Run;
end;

主进程可以派生工作进程。如果无法在3秒内启动工作进程,则主进程将显示错误。最近我将GDIPOBJ包含在应用程序中,因此无法按时分叉工作进程。

我通过延长等待时间来解决问题。但是由于工作进程没有UI,所以它不需要Gdi +的东西。我非常好奇地问所有亲爱的德尔菲专家,如果可以有条件地装载一个单元或有条件地初始化一个单元。

我当前的解决方案是将单元复制到我的项目目录并将代码移动到方法InitGidplusStartup。当我创建MasterProcess时,我必须显式调用该方法。

procedure InitGdiplusStartup;
begin
  if not IsLibrary then
  begin
    // Initialize StartupInput structure
    StartupInput.DebugEventCallback := nil;
    StartupInput.SuppressBackgroundThread := False;
    StartupInput.SuppressExternalCodecs   := False;
    StartupInput.GdiplusVersion := 1;

    GdiplusStartup(gdiplusToken, @StartupInput, nil);
  end;
end;

initialization
(*
  if not IsLibrary then
  begin
    // Initialize StartupInput structure
    StartupInput.DebugEventCallback := nil;
    StartupInput.SuppressBackgroundThread := False;
    StartupInput.SuppressExternalCodecs   := False;
    StartupInput.GdiplusVersion := 1;

    GdiplusStartup(gdiplusToken, @StartupInput, nil);
  end;
*)

program MyApp;
uses
  GDIPOBJ{INCLUDE MODIFIED VERSION FIRST AND I KNOW THE RISK}, 
  SysUtils, Forms, WorkerForm, MasterForm;

begin
  Application.Initialize;
  if FindCmdLineSwitch('--WorkerProcess') then
    Application.CreateForm(TWorkerForm, WorkerForm)
  else
  begin
    InitGdiplusStartup;
    Application.CreateForm(TMasterForm, MasterForm);
  end;
  Application.Run;
end;

我知道这不是一种标准方法。我只是想了解单元初始化及其局限性。请不要怪我。

3 个答案:

答案 0 :(得分:2)

单元未在运行时加载,它们在编译期间链接。如果要区分命令行参数,则无论是否使用,都必须存在该单元。编译器无法知道运行时参数,因此无论如何都必须链接该单元。

如果您可以分离初始化代码,则可以使用它。请注意,当您采用这种方式时,在初始化序列期间执行代码的时间可能会有所不同。

另一种可能很麻烦的方法是将违规单位放入一个只按需加载的包裹中。

答案 1 :(得分:2)

无条件执行单元初始化部分。因此,我可以看到以下可用于解决问题的选项:

  1. 将程序更改为对执行gdipobj初始化的工作进程具有弹性。
  2. 创建程序的两个版本,一个用于,另一个没有gdipobj。
  3. 将使用gdipobj的代码放在一个仅有条件加载的单独模块中。
  4. 将gdipobj单元替换为仅有条件地执行初始化部分的版本。
  5. 后一种选择很容易做到。获取gdipobj单元的副本,并将其包含在项目的源代码树中。确保在.dpr文件中引用了此单元。修改此单元副本的initialization部分,以根据您的条件跳过初始化。

答案 2 :(得分:1)

使用DDetours library https://code.google.com/p/delphi-detours-library/wiki/DDetours 将呼叫修补到GDIplusStartup

首先在项目中添加此单元。

Unit PatchGDI;

Interface

uses
  WinApi.GDIPAPI, DDetours;

Implementation

var
  TrampolineGdiplusStartup : function(out token: ULONG; input: PGdiplusStartupInput;
   output: PGdiplusStartupOutput): Status; stdcall = nil;

function InterceptGdiplusStartup(out token: ULONG; input: PGdiplusStartupInput;
   output: PGdiplusStartupOutput): Status; stdcall;
begin
  Result := GdiplusNotInitialized;
end;

Initialization

  if ParamStr(1) = 'WorkerProcess' then
    @TrampolineGdiplusStartup := InterceptCreate(@GdiplusStartup, @InterceptGdiplusStartup);

end.

免责声明,未经测试,但您明白了。

这是一个示例项目。在调试模式下运行它,您可以看到GDIplusStartup被截获,GDIPOBJ中的初始化代码正在调用截获的过程:

program Project124;

{$APPTYPE CONSOLE}

uses
  System.SysUtils,
  PatchGDI in 'PatchGDI.pas',
  GDIPOBJ;

begin
end.