为什么 DOF 会删除线条?
Why does the DOF remove lines?
我正在尝试为 Delphi5 编写一个插件,它将在 DOF 中存储我们的测试 exe 的路径,以便项目和该项目的测试之间存在直接关联。当我将自己的模块添加到 DOF 文件时,类似于
[DUint Plugin]
IntegrationTestExe=Somepath
UnitTestExeList=Some;Path;
每当我手动或通过代码添加它时,当我保存项目时,我添加的行都会被删除。我把这归结为 IDE 只是不允许在 DOF 中使用自定义模块。
但是,我们使用名为 EurekaLog 的第三方插件。 EurekaLog 将自己的变量注入到 DOF 中,当您保存时,这些变量不会被删除。我复制了很多代码,这样我就可以测试 EurekaLog 代码是否能正常工作(通过一些魔法),但他们的代码只是将他们的模块写入 DOF,并没有做任何其他特别的事情。
有谁知道这在 EurekaLog 中是如何实现的?我是否需要在某个地方注册我的模块,以便 IDE 知道不要删除它?
更新经过一些试验,似乎将设置保存到 DOF 实际上比将它们保存到 DSK 文件更可靠。
向窗体添加另一个 TEdit 并创建 LoadDOFSettings 和 SaveDOFSettings
类似于现有的 LoadSettings 和 SaveSettings 并在收到时调用它们
DesktopLoad 和 DesktoSave 通知。 SaveDOFSettings 不需要通过 Timer1 事件调用,因为重命名似乎没有发生在 DOF 上。
原回答
我建议在阅读这个答案之前,你做一个 File |关闭 IDE 中的所有内容,
创建一个新包,将下面的单元添加到其中并安装在 IDE.
该包的目的有两个,首先展示如何在 DSK 文件中保存自定义设置,其次让您了解项目的事件信息
您可以通过 ToolsAPI 单元中的服务从 IDE 获取文件..
安装包后,请注意它的形式,它会在您打开、处理和关闭项目时在上方的备忘录中显示文件通知。有
有几点需要注意:
当您打开一个项目时,您收到的最后一条通知是关于它的 DSK 文件已被打开。
并非每种文件类型都是通知的主题。特别是,您不会收到任何专门关于 DOF 文件的通知,因此如果您想写入并稍后从中读取,您必须假设何时安全(或不安全)这样做,这是可能是为什么 运行 你问的问题。
当您对项目执行“全部关闭”时,您收到的最后一次文件更改通知是正在写入的 DSK。问题在于,它最初被写入同名但扩展名为 .$$$ 的文件。不久之后,但 asaics 您无法确切知道什么时候,这个 .$$$ 文件被重命名为 .DSK。
由以下代码创建的表单有一个编辑框,DSK 文件的 edMyValue' which can be used to set a value in a section of the DSK file called
MySettingsand which is reloaded the next time the project is opened. The writing of the
MySettings` 部分由 TTimer 触发,延迟 2 秒以提供 IDE 是时候按照我的描述编写和重命名 DSK 文件了。这显然为竞争条件提供了机会。
你可能想参考
http://www.gexperts.org/open-tools-api-faq/#dsk
(GExperts 是 IDE 插件工具,从 Delphi 的早期就已经存在)
文章的部分是在谈论当前项目的.DSK文件。与 DOF 一样,这是 INI 文件格式,包含
等部分
[Closed Files]
[Modules]
[EditWindow0]
[View0]
如你所见
check for the ofnProjectDesktopLoad and ofnProjectDesktopSave NotifyCode values. When you see one of those, you can save/load values from the file indicated by the FileName parameter using a class such as TIniFile.
由于重命名业务,可能比文章建议的更棘手。
玩得开心!
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.
我正在尝试为 Delphi5 编写一个插件,它将在 DOF 中存储我们的测试 exe 的路径,以便项目和该项目的测试之间存在直接关联。当我将自己的模块添加到 DOF 文件时,类似于
[DUint Plugin]
IntegrationTestExe=Somepath
UnitTestExeList=Some;Path;
每当我手动或通过代码添加它时,当我保存项目时,我添加的行都会被删除。我把这归结为 IDE 只是不允许在 DOF 中使用自定义模块。
但是,我们使用名为 EurekaLog 的第三方插件。 EurekaLog 将自己的变量注入到 DOF 中,当您保存时,这些变量不会被删除。我复制了很多代码,这样我就可以测试 EurekaLog 代码是否能正常工作(通过一些魔法),但他们的代码只是将他们的模块写入 DOF,并没有做任何其他特别的事情。
有谁知道这在 EurekaLog 中是如何实现的?我是否需要在某个地方注册我的模块,以便 IDE 知道不要删除它?
更新经过一些试验,似乎将设置保存到 DOF 实际上比将它们保存到 DSK 文件更可靠。
向窗体添加另一个 TEdit 并创建 LoadDOFSettings 和 SaveDOFSettings 类似于现有的 LoadSettings 和 SaveSettings 并在收到时调用它们 DesktopLoad 和 DesktoSave 通知。 SaveDOFSettings 不需要通过 Timer1 事件调用,因为重命名似乎没有发生在 DOF 上。
原回答 我建议在阅读这个答案之前,你做一个 File |关闭 IDE 中的所有内容, 创建一个新包,将下面的单元添加到其中并安装在 IDE.
该包的目的有两个,首先展示如何在 DSK 文件中保存自定义设置,其次让您了解项目的事件信息 您可以通过 ToolsAPI 单元中的服务从 IDE 获取文件..
安装包后,请注意它的形式,它会在您打开、处理和关闭项目时在上方的备忘录中显示文件通知。有 有几点需要注意:
当您打开一个项目时,您收到的最后一条通知是关于它的 DSK 文件已被打开。
并非每种文件类型都是通知的主题。特别是,您不会收到任何专门关于 DOF 文件的通知,因此如果您想写入并稍后从中读取,您必须假设何时安全(或不安全)这样做,这是可能是为什么 运行 你问的问题。
当您对项目执行“全部关闭”时,您收到的最后一次文件更改通知是正在写入的 DSK。问题在于,它最初被写入同名但扩展名为 .$$$ 的文件。不久之后,但 asaics 您无法确切知道什么时候,这个 .$$$ 文件被重命名为 .DSK。
由以下代码创建的表单有一个编辑框,DSK 文件的 edMyValue' which can be used to set a value in a section of the DSK file called
MySettingsand which is reloaded the next time the project is opened. The writing of the
MySettings` 部分由 TTimer 触发,延迟 2 秒以提供 IDE 是时候按照我的描述编写和重命名 DSK 文件了。这显然为竞争条件提供了机会。
你可能想参考
http://www.gexperts.org/open-tools-api-faq/#dsk
(GExperts 是 IDE 插件工具,从 Delphi 的早期就已经存在)
文章的部分是在谈论当前项目的.DSK文件。与 DOF 一样,这是 INI 文件格式,包含
等部分[Closed Files]
[Modules]
[EditWindow0]
[View0]
如你所见
check for the ofnProjectDesktopLoad and ofnProjectDesktopSave NotifyCode values. When you see one of those, you can save/load values from the file indicated by the FileName parameter using a class such as TIniFile.
由于重命名业务,可能比文章建议的更棘手。
玩得开心!
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.