【问题标题】:Why does the DOF remove lines?为什么 DOF 会删除线条?
【发布时间】:2023-03-09 07:03:01
【问题描述】:

我正在尝试为 Delphi 5 编写一个插件,它将在 DOF 中存储我们的测试 exe 的路径,以便项目和该项目的测试之间存在直接关联。当我将自己的模块添加到 DOF 文件时,类似于

[DUint Plugin]
IntegrationTestExe=Somepath
UnitTestExeList=Some;Path;

每当我手动或通过代码添加它时,当我保存项目时,我添加的行都会被删除。我将此归结为 IDE 可能只是不允许 DOF 中的自定义模块。

但是,我们使用名为 EurekaLog 的第三方插件。 EurekaLog 将自己的变量注入 DOF,当你保存时,这些变量不会被删除。我复制了大部分代码,所以我可以测试 EurekaLog 代码是否可以正常工作(通过一些魔法),但他们的代码只是将他们的模块写入 DOF 并没有做任何其他特别的事情。

有谁知道这在 EurekaLog 中是如何实现的?我是否需要在某处注册我的模块,以便 IDE 知道不要删除它?

【问题讨论】:

    标签: delphi delphi-5


    【解决方案1】:

    更新经过一番试验,似乎将设置保存到 DOF 实际上明显比将它们保存到 DSK 文件更可靠。

    向窗体添加另一个 TEdit 并创建 LoadDOFSettings 和 SaveDOFSettings 类似于现有的 LoadSettings 和 SaveSettings 并在收到时调用它们 DesktopLoad 和 DesktoSave 通知。 SaveDOFSettings 不需要通过 Timer1 事件调用,因为重命名似乎不会发生在 DOF 上。

    原答案 我建议在阅读这个答案之前,你先做一个 File |在 IDE 中全部关闭, 创建一个新包,将下面的单元添加到其中并安装在 IDE 中。

    打包的目的有两个,第一是展示如何在 DSK 文件中保存自定义设置,第二是让您了解有关项目的哪些事件信息 您可以通过 ToolsAPI 单元中的服务从 IDE 获取文件。

    安装软件包后,请注意其表单,当您打开、处理和关闭项目时,该表单会在上方的备忘录中显示文件通知。有 需要注意的几点:

    • 当您打开项目时,您收到的最后一条通知是关于其 DSK 文件已打开。

    • 并非每种文件类型都是通知的主题。特别是,您不会收到任何专门关于 DOF 文件的通知,因此如果您想写入它并稍后读取它,您必须假设何时安全(或不安全)这样做,这是可能是您遇到问题的原因。

    • 当您对项目执行全部关闭操作时,您收到的最后一个文件更改通知是正在写入的 DSK。问题是它最初被写入同名但扩展名为 .$$$ 的文件。不久之后,但你无法确切知道何时,这个 .$$$ 文件被重命名为 .DSK。

    由以下代码创建的表单有一个编辑框,DSK 文件的edMyValue' which can be used to set a value in a section of the DSK file calledMySettingsand which is reloaded the next time the project is opened. The writing of theMySettings` 部分由一个延迟 2 秒的 TTimer 触发,以给 IDE 时间来写入和重命名如我所描述的 DSK 文件。这显然为竞争条件提供了机会。

    您不妨参考

    http://www.gexperts.org/open-tools-api-faq/#dsk

    (GExperts 是从 Delphi 早期就存在的 IDE 插件工具)

    文章的部分讨论的是当前项目的 .DSK 文件。与 DOF 一样,它是 INI 文件格式,带有类似

    的部分
    [Closed Files]
    [Modules]
    [EditWindow0]
    [View0]
    

    如你所见

    检查 ofnProjectDesktopLoad 和 ofnProjectDesktopSave NotifyCode 值。当您看到其中之一时,您可以使用诸如 TIniFile 之类的类从 FileName 参数指示的文件中保存/加载值。

    也许它比文章建议的要复杂一些,因为重命名业务。

    玩得开心!

    unit IDEEventsu;
    interface
    uses
      Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
      Dialogs, StdCtrls, ExtCtrls, ToolsAPI, Grids, IniFiles;
    
    type
      TFileEventsForm = class(TForm)
        Panel1: TPanel;
        Memo1: TMemo;
        edMyValue: TEdit;
        btnClear: TButton;
        Timer1: TTimer;
        Memo2: TMemo;
        procedure btnClearClick(Sender: TObject);
        procedure Timer1Timer(Sender: TObject);
      private
        function GetCurrentProject: IOTAProject;
      public
        //  The following are using interfaces accessible via the ToolsAPI
        Services: IOTAServices;
        ProjectGroup : IOTAProjectGroup;
        Project: IOTAProject;
        Options : IOTAProjectOptions;
        ModServices: IOTAModuleServices;
        Module: IOTAModule;
    
        NotifierIndex: Integer;  // This is used to disconnect our notifier from the IDE
        IsSetUp : Boolean;
        SetUpCount : Integer;
        DskFileName : String;
        procedure SetUp;
        procedure SaveSettings;
        procedure LoadSettings;
      end;
    
    var
      FileEventsForm: TFileEventsForm;
    
    procedure Register;
    
    [...]
    uses
     typinfo;
    
    type
      TIdeNotifier = class(TNotifierObject, IOTANotifier, IOTAIDENotifier)
      //  This is the class we use to receive file notication events from the IDE via the
      //  interfaces in ToolsAPI.Pas
      //
      //  It needs to implement the IOTANotifier and IOTAIDENotifier interfaces and,
      //  once registered with the IDE, the IDE calls its methods as a kind of call-back
      //  mechanism so that it gets notified of file events
      //
      //  Note that this file also provides a form for displaying the received event
      //  notifications and that the IOTANotifier and IOTAIDENotifier interfaces could
      //  just as easily be implemented by the form itself
      protected
        procedure AfterCompile(Succeeded: Boolean);
        procedure BeforeCompile(const Project: IOTAProject; var Cancel: Boolean);
        procedure FileNotification(NotifyCode: TOTAFileNotification;
          const FileName: string; var Cancel: Boolean);
      end;
    
    procedure Register;
    //  This is necessary to register the package in the IDE
    var
      Notifier : TIdeNotifier;
    begin
      FileEventsForm:= TFileEventsForm.Create(Nil);
      FileEventsForm.Services := BorlandIDEServices as IOTAServices;
      Notifier := TIdeNotifier.Create;
      Notifier.Form := FileEventsForm;
      FileEventsForm.NotifierIndex := FileEventsForm.Services.AddNotifier(TIdeNotifier.Create);
    end;
    
    procedure CloseDown;
    begin
      FileEventsForm.Services.RemoveNotifier(FileEventsForm.NotifierIndex);
      FileEventsForm.Close;
      FileEventsForm.Free;
    end;
    
    function NotifyCodeString(NotifyCode : TOTAFileNotification) : String;
    begin
      Result := Copy(GetEnumName(TypeInfo(TOTAFileNotification), Ord(NotifyCode)), 4, MaxInt);
    end;
    
    procedure TIdeNotifier.AfterCompile(Succeeded: Boolean);
    begin
    end;
    
    procedure TIdeNotifier.BeforeCompile(const Project: IOTAProject; var Cancel: Boolean);
    begin
    end;
    
    procedure TIdeNotifier.FileNotification(NotifyCode: TOTAFileNotification;
      const FileName: string; var Cancel: Boolean);
    begin
      if True {NotifyCode in [ofnProjectDesktopLoad, ofnActiveProjectChanged]}  then begin
        FileEventsForm.Show;
        FileEventsForm.Memo1.Lines.Add(Format('%s file: %s', [NotifyCodeString(NotifyCode), FileName]));
        case NotifyCode of
          ofnProjectDesktopLoad,
          ofnDefaultDesktopLoad : begin
            FileEventsForm.DskFileName := FileName;
            FileEventsForm.LoadSettings;
          end;
          ofnProjectDesktopSave,
          ofnDefaultDesktopSave : begin
            if True{CompareText(ExtractFileExt(FileName), '.DSK') = 0} then begin
              FileEventsForm.Caption := FileName;
              FileEventsForm.Timer1.Enabled := True;  //  causes DSK file to be updated after Timer1.Interval (=2000ms)
            end;
          end;
        end; { case }
      end;
    end;
    
    procedure TFileEventsForm.btnClearClick(Sender: TObject);
    begin
      Memo1.Lines.Clear;
    end;
    
    function TFileEventsForm.GetCurrentProject: IOTAProject;
    var
      i: Integer;
    begin
      Result := nil;
      ModServices := BorlandIDEServices as IOTAModuleServices;
      for i := 0 to ModServices.ModuleCount - 1 do
      begin
        Module := ModServices.Modules[i];
        if Supports(Module, IOTAProjectGroup, ProjectGroup) then begin
          Result := ProjectGroup.ActiveProject;
          Options := Result.ProjectOptions;
          Exit;
        end
        else if Supports(Module, IOTAProject, Project) then
        begin // In the case of unbound packages, return the 1st
          if Result = nil then begin
            Result := Project;
            Options := Result.ProjectOptions;
          end;
        end;
      end;
    end;
    
    procedure TFileEventsForm.SetUp;
    begin
      Project := GetCurrentProject;
      Inc(SetUpCount);
      Caption := 'Setup done ' + IntToStr(SetUpCount);
      IsSetUp := True;
    end;
    
    procedure TFileEventsForm.LoadSettings;
    var
      Ini : TMemIniFile;
      S : String;
    begin
      Ini := TMemIniFile.Create(DSKFileName);
      try
        S := Ini.ReadString('MySettings', 'Name', 'no value');
        edMyValue.Text := S;
      finally
        Ini.Free;
      end;
    end;
    
    procedure TFileEventsForm.SaveSettings;
    var
      Ini : TMemIniFile;
      S : String;
    begin
      S := DSKFileName;
      Caption := 'Saving: ' + S;
      Ini := TMemIniFile.Create(S);
      try
        Ini.WriteString('MySettings', 'Name', edMyValue.Text);
        Ini.UpdateFile;
        Ini.ReadSections(Memo2.Lines);
        Memo2.Lines.Add('This file : ' + DSKFileName);
        edMyValue.Text := '?';
      finally
        Ini.Free;
      end;
    end;
    
    procedure TFileEventsForm.Timer1Timer(Sender: TObject);
    begin
      Timer1.Enabled := False;
      SaveSettings;
    end;
    
    initialization
    
    finalization
      CloseDown;
    end.
    

    【讨论】:

    • 我非常感谢这个答案。这非常简洁明了,解决了我的问题。谢谢。
    • 谢谢,很高兴知道。等我有空的十分钟,我会把答案整理一下。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-04-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多