Delphi 如何处理.dpr使用部分中的ifdef

Delphi 如何处理.dpr使用部分中的ifdef,delphi,Delphi,无论何时向项目添加新单元,Delphi都会重建.dpr文件,uses部分中的所有ifdef都会消失 为了解决这个问题,我通常使用记事本创建新的.pas文件,并手动将其添加到.dpr中。如果我需要一个表单,我使用File->New->form,然后将.dpr文件恢复到以前的版本。如果你问我的话,我觉得不太好;-) 你是怎么处理的?有没有办法在IDE中添加一个单元,同时保留IFDEF?您可以从IDE中手动添加它。(在项目上使用“查看源”选项) 通常,dpr是“隐藏的”。你不需要改变那里的任何东西。

无论何时向项目添加新单元,Delphi都会重建.dpr文件,uses部分中的所有ifdef都会消失

为了解决这个问题,我通常使用记事本创建新的.pas文件,并手动将其添加到.dpr中。如果我需要一个表单,我使用File->New->form,然后将.dpr文件恢复到以前的版本。如果你问我的话,我觉得不太好;-)


你是怎么处理的?有没有办法在IDE中添加一个单元,同时保留IFDEF?

您可以从IDE中手动添加它。(在项目上使用“查看源”选项)

通常,dpr是“隐藏的”。你不需要改变那里的任何东西。
如果您这样做,您最好确保所有更改都是手动的,否则您会丢失一些信息。

有时我会专门创建一个单元,用于放置所有IFDEF和其他IDE在dpr中可能会弄糟的东西。该单元通常位于dpr的uses子句的顶部。这个技巧并不能适应所有的场景,但有时它可以节省很多繁琐的工作。

我花了很长时间试图解决这个问题

我最终得到了每个构建类型的项目文件(.dpr), 使用项目|项目选项|目录/条件中的条件 只有我想要的单位加入到项目中

这样做的缺点是,如果在.dpr中有自定义代码,则在其更改时必须手动将其复制到其他项目文件中

正如Rob Kennedy所指出的,这可以通过将自定义代码放入自己的单元来处理,该单元由单个过程调用。从而将要进行的.dpr代码大小/更改降至最低


此外,您还可以获得另一个好处,即如果您将所有.dpr文件添加到项目组中,则只需单击/cmd行即可生成所有不同的版本。我不会将任何ifdef放入dpr文件中。如果我想在一个项目中使用不同的单位/形式,根据某些条件,我会将项目一分为二。

(Delphi 7)

我也试过了。 请看第一个代码版本和我的以下评论:

program Project1;

{$IFDEF TESTIFDEF}
uses
  Forms,
  Unit1 in 'Unit1.pas' {Form1},
  Unit2 in 'Unit2.pas' {Form2};

{$ELSE}
uses
  Forms,
  Unit1 in 'Unit1.pas' {Form1};
{$ENDIF TESTIFDEF}

{$R *.res}

begin
  Application.Initialize;
  Application.CreateForm(TForm1, Form1);
  Application.CreateForm(TForm2, Form2);
  Application.Run;
end.
此时,我刚刚插入了第二个表单,并注意到相应的单元(Unit2.pas)被插入到IFDEF的第一部分中,即标记为part的“TESTIFDEF”中,因此没有覆盖第二个块(在{$ELSE}之后)

因此,您的解决方案应该是:

  • 定义像“{$IFDEF DELPHIBASISCONFIGURATION}”这样的IFDEF语句来代替我的“{$IFDEF TESTIFDEF}”,所有表单都将添加到该语句中
  • 为要使用的不同配置定义尽可能多的备选标签
  • 每次将表单添加到项目中时,根据需要将第一个块的插入行复制到下面相应的块中
  • 使用define语句或选项对话框激活所需配置
  • 永远不要定义“DELPHIBASISCONFIGURATION”;) 因此,它应该是这样的:

    program Project1;
    
    {$DEFINE MYCONFIG1} // THIS ONE IS NOW ACTIVE
    
    
    {$IFDEF DELPHIBASISCONFIGURATION}
    uses
      Forms,
      Unit1 in 'Unit1.pas' {Form1},
      Unit2 in 'Unit2.pas' {Form2},
      Unit3 in 'Unit3.pas' {Form3};
    
    {$ELSE}
         // THIS IS A "COMMON TO ALL CONFIG" PART
         uses
           Forms,
    
           // FIRST CONFIGURATION
           {$IFDEF MYCONFIG1}
           Unit1 in 'Unit1.pas' {Form1},
           Unit3 in 'Unit3.pas' {Form3}
           {$ENDIF MYCONFIG1}
    
           // SECOND CONFIGURATION    
           {$IFDEF MYCONFIG2}
           Unit1 in 'Unit1.pas' {Form1},
           Unit2 in 'Unit2.pas' {Form2}
           {$ENDIF MYCONFIG2}
    
         // THIS IS THE "COMMON TO ALL CONFIG" END :)
         ;
    
    {$ENDIF TESTIFDEF}
    
    {$R *.res}
    
    begin
      Application.Initialize;
      Application.CreateForm(TForm1, Form1);
      //Application.CreateForm(TForm3, Form3);
      //Application.CreateForm(TForm2, Form2);
      Application.Run;
    end.
    
    如您所见,我已经放弃了对Form2和Form3的Application.CreateForm(…)的调用


    依我看,通常最好在您真正需要的时候动态创建补充表单,即程序开始时不是所有表单…

    对于表单、数据模块和其他包含单个类的单元,功能将被替换,解决方案相当简单。只是不要将自定义单位直接添加到产品中,而是将它们保存在搜索路径中的某个位置(或者修改项目搜索路径以包含它们的位置)

    1) 创建一个新单元,它包含所有其他类的父类,或者它们都将实现的接口(我通常更喜欢后者,因为它允许更容易的定制)[例如,这被称为uSpecialParent.pas]

    2) 添加一个类变量,在需要创建新功能时将引用该类变量。例如,如果您只想显示一组表单,因此不关心任何其他方法,那么您可以有一个如下所示的变量:

    TYPE
      TMySpecialFormClass : class of TForm;
    
    VAR
      TMySpecialForm : TMySpecialFormClass;
    
    Unit uRegisterSpecialForms;
    
    interface
    
    uses
    {$IFDF SPECIAL1}
      uSpecial1,
    {$ENDIF}
    {$IFDEF SPECIAL2}
      uSpecial2,
    {$ENDIF}
      uSpecialParent;
    
    implementation
    
    // no code needed.
    
    initialization
    
    {$IFDEF SPECIAL1}
      TMySpecialForm := uSpecial1.TSpecialForm1;
    {$ENDIF}
    {$IFDEF SPECIAL2}
      TMySpecialForm := uSpecial2.TSPecialForm2;
    {$ENDIF}
    
    end.
    
    var
      frm : TForm;
    begin
      frm := TMySpecialForm.Create(nil);
      try
        frm.showmodal;
      finally
        frm.free;
      end;
    end;
    
    3) 创建另一个包含所有IFDEF的单元。它可能看起来如下所示:

    TYPE
      TMySpecialFormClass : class of TForm;
    
    VAR
      TMySpecialForm : TMySpecialFormClass;
    
    Unit uRegisterSpecialForms;
    
    interface
    
    uses
    {$IFDF SPECIAL1}
      uSpecial1,
    {$ENDIF}
    {$IFDEF SPECIAL2}
      uSpecial2,
    {$ENDIF}
      uSpecialParent;
    
    implementation
    
    // no code needed.
    
    initialization
    
    {$IFDEF SPECIAL1}
      TMySpecialForm := uSpecial1.TSpecialForm1;
    {$ENDIF}
    {$IFDEF SPECIAL2}
      TMySpecialForm := uSpecial2.TSPecialForm2;
    {$ENDIF}
    
    end.
    
    var
      frm : TForm;
    begin
      frm := TMySpecialForm.Create(nil);
      try
        frm.showmodal;
      finally
        frm.free;
      end;
    end;
    
    4) 要在代码中引用此项,您只需将USPECIALPART添加到将请求特殊表单的单元中,然后动态创建它,例如,为了显示此模式,您可以调用以下命令:

    TYPE
      TMySpecialFormClass : class of TForm;
    
    VAR
      TMySpecialForm : TMySpecialFormClass;
    
    Unit uRegisterSpecialForms;
    
    interface
    
    uses
    {$IFDF SPECIAL1}
      uSpecial1,
    {$ENDIF}
    {$IFDEF SPECIAL2}
      uSpecial2,
    {$ENDIF}
      uSpecialParent;
    
    implementation
    
    // no code needed.
    
    initialization
    
    {$IFDEF SPECIAL1}
      TMySpecialForm := uSpecial1.TSpecialForm1;
    {$ENDIF}
    {$IFDEF SPECIAL2}
      TMySpecialForm := uSpecial2.TSPecialForm2;
    {$ENDIF}
    
    end.
    
    var
      frm : TForm;
    begin
      frm := TMySpecialForm.Create(nil);
      try
        frm.showmodal;
      finally
        frm.free;
      end;
    end;
    

    为了完整性起见,这里是lo-tech方法:

    IDE再次打乱uses子句后:

  • 关闭项目
  • 转到您选择的版本控制工具,将DPR与最新签入的版本进行区分 使用支持合并的差异工具(如WinMerge)的版本
  • 还原IDE更改
  • 拯救朝鲜
  • 继续干吧

  • +1.我不记得曾经使用过单位的条件包含。为什么要这样做?@mghie:跨编译器和RTL版本的可移植性。跨编译器(Delphi vs C++ Builder)的可移植性,避免了依赖于构建配置的单元(例如,在发布版本中,您可能不希望堆栈转储支持,因为没有调试符号)。首先,这不会导致有条件包含的单元成为项目的一部分(因此可以通过项目管理器访问)@mghie:Memory managers必须是链接器看到的第一个单元,这只能通过.dpr文件实现。这就是我所指的用例(我也在使用FastMM,除了在一些应用程序中遇到bug,并且必须在.dpr文件中维护ifdef以测试各种版本)是的,我这样做是因为我在dpr中加入了IFDEF,德尔福对此感到困惑。你能解释一下为什么有时候你只想让一个单位成为你项目的一员吗?我不明白它的目的。在我们的项目中,我们使用Delphi附带的FastMM进行发布构建,但是ext