参考《Delphi Open Tools Api實例研究(一)》http://www.xuebuyuan.com/zh-tw/1275460.html,在此特别表示感谢!!!
//本单元文件,根据compile.inc编译指令文件中的不同编译指令,同时读取工程配置config.ini文件,一次编译输出多个项目工程结果。
compile.inc
{$UNDEF NoFinger}
{$DEFINE Ok}
{$DEFINE release}
{$DEFINE NoFinger}
{$DEFINE Hello}
{$DEFINE debug}
config.ini
[General]
//工程输出目标程序文件配置列表,后面的每个小节必须以此列表命名,子项key为固定值,如需添加,要修改程序
sections=nofinger,finger
//单独编译某段,如果全编译使用 all
activesection=nofinger
[nofinger]
//编译指令集,将输出到工程目录下的compile.inc文件中,作为工程编译时条件指令集
compilationDirective={$DEFINE NoFinger},{$DEFINE Hello},{$DEFINE debug}
//目标程序文件目录
outDir=..\bin_NoFinger
//目标程序文件名称
ExeName=FGDC_NoFinger.exe
//目标程序文件属性中的文件描述信息
FileDescription=无指纹版本
FileVersion=1.0.0.6
[finger]
compilationDirective={$UNDEF NoFinger},{$DEFINE Ok},{$DEFINE release}
outDir=..\bin_Finger
ExeName=FGDC_Finger.exe
FileDescription=有指纹版本
FileVersion=1.0.0.6
//单元文件
unit u_main;
interface
uses
SysUtils, Classes, Menus, ToolsApi, Controls, ImgList, Graphics, Forms,
ComCtrls, windows, TypInfo, variants, auto_object, IdStrings, dialogs, u_inifile_C;
type
TNTATest = class
private
FMainMenu: TMainMenu; //用來存貯delphi IDE的主菜單
NewMenu: TMenuItem; //我們將要插入的菜單
FImageList: TCustomImageList; //用來存貯delphi IDE主菜單和工具欄的ImageList
ImageIndex1: integer; //檢測量,請參看後面的代碼
IDEHandle: HWND;
procedure BuildAllProjects(sender: TObject);
procedure BuildPrj(sOutDir, sExeName, sFileDesc, sFileVersion: string);
procedure BuildPrjBefore(sSection, sprj_path: string;
var sLst_compilefile: TStringList);
procedure BuildProject(sender: TObject);
procedure DynamicAddMenu(sender: TObject);
function GetProjectResource(Project: IOTAProject): IOTAProjectResource;
procedure IncrementVersion(sender: TObject);
protected
procedure AddMenu; //加入我們的菜單
procedure ReMoveMenu; //卸載我們的菜單
procedure ReCodeEditer(sender: TObject); //菜單項一的事件
procedure AboutForm(sender: TObject); //菜單項二的事件
procedure drawFormcustom(Sender: TObject; ACanvas: TCanvas;
ARect: TRect; Selected: Boolean);
public
constructor Create;
destructor Destroy; override;
private
index: integer;
end;
TProjectNotifer = class(TNotifierObject, IOTAIDENotifier)
public
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;
var
MyNTATest: TNTATest;
implementation
uses VersionInfo;
const
FIXED_MENU_COUNT = 4;
function printLogToFile(sfmt: string; ss: array of const; apppath: string): string;
procedure writeLnToFile(s: ansistring);
var
tf: TextFile;
strFileName: ansistring;
begin
if not DirectoryExists(apppath + 'log\') then CreateDir(apppath + 'log\');
strFileName := Format('%s%s.log', [apppath + 'log\', FormatDateTime('yyyymmdd', now)]);
AssignFile(tf, strFileName);
if FileExists(strFileName) then
begin
Append(tf);
end
else
begin
Rewrite(tf);
end;
writeln(tf, s);
Flush(tf);
CloseFile(tf);
end;
var
sStr, sStr1, sStr2: ansistring;
begin
sStr := Format('%s', [FormatDateTime('hh:nn:ss', now)]);
sStr1 := Format(sfmt, ss);
sStr2 := Format('%s %s', [sStr, sstr1]);
writeLnToFile(sStr2);
result := sStr1;
end;
procedure Register;
begin
MyNTATest.AddMenu;
//和傳統組件的同名方法不同,這裡沒有在組件面板上安裝圖標
//而是直接調用AddMenu方法添加我們的菜單
end;
{ TNTATest }
constructor TNTATest.Create;
begin
IDEHandle := (BorlandIDEServices as IOTAServices).GetParentHandle;
//我們用IOTAServices介面的GetParentHandle方法取得了ide的handle
Index := (BorlandIDEServices as IOTAServices).AddNotifier(TProjectNotifer.create);
end;
procedure TNTATest.AddMenu;
//var
//MenuItem: array[0..2] of TMenuItem;
//i: integer;
//Icon1: TIcon; //菜單項一的圖標
begin
FMainMenu := (BorlandIDEServices as INTAServices).MainMenu;
//我們用 INTAServices的MainMenu屬性直接得到了IDE的主菜單
FImageList := (BorlandIDEServices as INTAServices).ImageList;
//我們用 INTAServices的ImageList屬性直接得到了IDE的圖象列表
NewMenu := TMenuItem.Create(FMainMenu);
//創建我們的菜單
NewMenu.Caption := 'wata';
ImageIndex1 := -1; //沒有載入圖標
//下面的代碼使用for和case來添加兩個菜單項有點小題大作,但
//我們展示了一種更通用的方法使你能夠添加更多的菜單項,而不必簡單的複製代碼。
NewMenu.OnClick := DynamicAddMenu; //从本地配置中读取要编译的工程列表动态加载菜单
// NewMenu.OnDrawItem := self.drawFormcustom;
FMainMenu.Items.Add(NewMenu); //最後添加我們的菜單到IDE主菜單
end;
procedure TNTATest.DynamicAddMenu(sender: TObject);
var
MenuItem: array of TMenuItem;
i: integer;
//Icon1: TIcon; //菜單項一的圖標
sLst_section: TStringlist;
sprj_path, sSections: string;
icount, iindx: integer;
sFileVersion: string;
prj: IOTAProject;
begin
prj := GetActiveProject;
if prj = nil then exit;
sprj_path := extractfilepath(prj.FileName);
//showmessage(sprj_path);
//工程使用编译指令文件,固定名称为compile.inc,固定路径为工程当前路径
{ if not fileExists(sprj_path + 'compile.inc') then
begin
Messagebox(IDEHandle, pchar(sprj_path + 'compile.inc文件不存在!'), '提示', mb_iconinformation);
exit;
end;
}
//工程使用目标程序文件输出配置文件,固定名称为config.ini,固定路径为工程当前路径
TAuto_object.New(iniRw, TIniRW_Env.create(sprj_path + 'config.ini'));
sSections := iniRw.readFromIni('General', 'sections', 'bin_1,bin_2');
TAuto_object.New(sLst_section, TStringList.Create);
sLst_section.Delimiter := ',';
sLst_section.DelimitedText := sSections;
icount := sLst_section.Count + FIXED_MENU_COUNT;
// SplitColumns(sDefines, sLst_defines, ',');
FMainMenu := (BorlandIDEServices as INTAServices).MainMenu;
//我們用 INTAServices的MainMenu屬性直接得到了IDE的主菜單
FImageList := (BorlandIDEServices as INTAServices).ImageList;
//我們用 INTAServices的ImageList屬性直接得到了IDE的圖象列表
for i := 0 to FMainMenu.Items.Count - 1 do
begin
//printlogtofile('%d =>%s', [i, FMainMenu.Items[i].Caption], sprj_path);
if FMainMenu.Items[i].Caption = 'wata' then
begin
FMainMenu.Items.Delete(i);
break;
end;
end;
NewMenu := TMenuItem.Create(FMainMenu);
//創建我們的菜單
NewMenu.Caption := 'wata';
NewMenu.Tag := 20;
setlength(MenuItem, icount);
for i := 0 to icount - 1 do
begin
MenuItem[i] := TMenuItem.Create(NewMenu); //創建子菜單項
case i of
0:
begin
MenuItem[i].Caption := 'About';
MenuItem[i].OnClick := AboutForm;
end;
1: MenuItem[i].Caption := '-'; //當然還有一個分割符號,其實是3個菜單項
2:
begin
MenuItem[i].Caption := 'Increment Version';
MenuItem[i].OnClick := IncrementVersion; //添加事件處理程序
end;
3:
begin
MenuItem[i].Caption := 'Build All Projects';
{
Icon1 := TIcon.Create;
try
Icon1.LoadFromFile('MAI2.ICO');
//我從硬碟的文件上載入了一個圖標作為菜單項一的圖標
except
on E: Exception do
begin
raise Exception.Create(E.Message);
exit;
end;
end;
ImageIndex1 := FImageList.AddIcon(Icon1);
//加入那個載入的圖標並返回一個ImageIndex
MenuItem[i].ImageIndex := ImageIndex1;
}
MenuItem[i].OnClick := BuildAllProjects; //添加事件處理程序
end;
else begin
iindx := i - FIXED_MENU_COUNT;
MenuItem[i].Tag := iindx;
sFileVersion := iniRw.readFromIni(sLst_section[iindx], 'FileVersion', '1.0.0.0');
MenuItem[i].Caption := 'Build => ' + sLst_section[iindx] + ' v' + sFileVersion;
MenuItem[i].OnClick := BuildProject;
end;
end;
NewMenu.Add(MenuItem[i]); //添加菜單項
end;
FMainMenu.Items.Add(NewMenu); //最後添加我們的菜單到IDE主菜單
end;
procedure TNTATest.IncrementVersion(sender: TObject);
var
i: integer;
sLst_section, sLst_version: TStringlist;
sprj_path, sSections: string;
iindx: integer;
sFileVersion: string;
iVer: Integer;
prj: IOTAProject;
begin
prj := GetActiveProject;
if prj = nil then exit;
sprj_path := extractfilepath(prj.FileName);
// showmessage(sprj_path);
//工程使用目标程序文件输出配置文件,固定名称为config.ini,固定路径为工程当前路径
TAuto_object.New(iniRw, TIniRW_Env.create(sprj_path + 'config.ini'));
sSections := iniRw.readFromIni('General', 'sections', 'bin_1,bin_2');
TAuto_object.New(sLst_version, TStringList.Create);
sLst_version.Delimiter := '.';
TAuto_object.New(sLst_section, TStringList.Create);
sLst_section.Delimiter := ',';
sLst_section.DelimitedText := sSections;
//icount := sLst_section.Count + FIXED_MENU_COUNT;
// SplitColumns(sDefines, sLst_defines, ',');
FMainMenu := (BorlandIDEServices as INTAServices).MainMenu;
//我們用 INTAServices的MainMenu屬性直接得到了IDE的主菜單
for i := 0 to FMainMenu.Items.Count - 1 do
begin
//printlogtofile('%d =>%s', [i, FMainMenu.Items[i].Caption], sprj_path);
if FMainMenu.Items[i].Caption = 'wata' then
begin
NewMenu := FMainMenu.Items[i];
break;
end;
end;
for i := 0 to NewMenu.Count - 1 do
begin
// showmessage(format('%d =>%s', [i, NewMenu.Items[i].Caption]));
case i of
0, 1, 2, 3:
begin
end;
else begin
iindx := i - FIXED_MENU_COUNT;
sFileVersion := iniRw.readFromIni(sLst_section[iindx], 'FileVersion', '1.0.0.0');
sLst_version.DelimitedText := sFileVersion;
iVer := strtoint(sLst_version[3]);
inc(iVer);
sFileVersion := format('%s.%s.%s.%d', [sLst_version[0], sLst_version[1], sLst_version[2], iVer]);
iniRw.writeToIni(sLst_section[iindx], 'FileVersion', sFileVersion);
NewMenu[i].Caption := 'Build => ' + sLst_section[iindx] + ' v' + sFileVersion;
end;
end;
end;
end;
procedure TNTATest.AboutForm(sender: TObject);
//一個簡單的關於對話框,注意參數中的IDEHandle
begin
// showmessage(inttostr(TMenuItem(sender).tag));
messagebox(IDEHandle, '此菜单实现了根据配置文件按照编译指令输出不同目录、执行文件、版本号等功能!', 'by wata', MB_ICONINFORMATION);
DynamicAddMenu(nil);
end;
procedure TNTATest.BuildPrj(sOutDir, sExeName, sFileDesc, sFileVersion: string);
function getSubstr(var sSTr: string): string;
var
iPos: integer;
begin
ipos := pos('.', sStr);
if ipos = 0 then
begin
result := sStr;
end else begin
result := copy(sStr, 1, ipos - 1);
sSTr := copy(sStr, ipos + 1, length(sStr) - ipos);
end;
end;
var
prjbuild: IOTAProjectBuilder;
sprj_path, sprj_name, sprj_name_t: string;
oarr: TOTAOptionNameArray;
i: integer;
v: variant;
bOk: boolean;
ProjectResource: IOTAProjectResource;
ResourceEntry: IOTAResourceEntry;
vi: TVersionInfo;
Stream: TMemoryStream;
srcFileName, destFileName: string;
ver1, ver2, ver3, ver4: string;
prj: IOTAProject;
begin
prj := GetActiveProject;
if prj = nil then exit;
sprj_path := extractfilepath(prj.FileName);
sprj_name_t := extractfileName(prj.FileName);
sprj_name := copy(sprj_name_t, 1, length(sprj_name_t) - 4);
//showmessage(sprj_path);
oarr := prj.ProjectOptions.GetOptionNames;
{
for i := 0 to high(oarr) do
begin
printlogtofile('%d =>%s,type =>%s,value =>%s',
[i, oarr[i].Name, TypInfo.GetEnumName(TypeInfo(TTypeKind),
Ord(oarr[i].Kind)), prj.ProjectOptions.Values[oarr[i].Name]], sprj_path);
end;
}
v := prj.ProjectOptions.Values['OutputDir'];
bOk := true;
if VarIsNull(v) then
begin
printlogtofile('OutputDir is null', [], sprj_path);
bOk := false;
end;
if VarIsEmpty(v) then
begin
printlogtofile('OutputDir is empty', [], sprj_path);
bOk := false;
end;
if bOK then
printlogtofile('OutputDir =>%s', [vartostr(v)], sprj_path);
//设置文件输出路径
prj.ProjectOptions.Values['OutputDir'] := soutDir;
ForceDirectories(sprj_path + soutDir);
// 设置文件版本号
ver1 := getSubstr(sFileVersion);
ver2 := getSubstr(sFileVersion);
ver3 := getSubstr(sFileVersion);
ver4 := getSubstr(sFileVersion);
// showmessage(ver1 + ',' + ver2 + ',' + ver3 + ',' + ver4);
prj.ProjectOptions.Values['MajorVersion'] := strtointdef(ver1, 0);
prj.ProjectOptions.Values['MinorVersion'] := strtointdef(ver2, 0);
prj.ProjectOptions.Values['Release'] := strtointdef(ver3, 0);
prj.ProjectOptions.Values['Build'] := strtointdef(ver4, 0);
ProjectResource := GetProjectResource(prj);
//showmessage(ProjectResource.FileName);
ResourceEntry := ProjectResource.FindEntry(RT_VERSION, PChar(1));
if Assigned(ResourceEntry) then
begin
TAuto_object.New(VI, TVersionInfo.Create(PChar(ResourceEntry.GetData)));
for i := 0 to VI.KeyCount - 1 do
begin
printlogtofile('%d =>%s,svalue =>%s',
[i, VI.KeyName[i], VI.KeyValue[VI.KeyName[i]]], sprj_path);
end;
//VI.KeyValue['Comments'] := 'hall';
//设置文件描述
VI.KeyValue['FileDescription'] := sFileDesc;
//VI.KeyValue['ProductVersion'] := sFileVersion;
//此处设置文件版本号不起作用,必须在options中设置
//VI.KeyValue['FileVersion'] := sFileVersion;
// showmessage(sFileVersion);
try
TAuto_object.New(Stream, TMemoryStream.Create);
try
VI.SaveToStream(Stream);
ResourceEntry.DataSize := Stream.Size;
Move(Stream.Memory^, ResourceEntry.GetData^, Stream.Size);
finally
end;
finally
end;
end;
prjbuild := prj.GetProjectBuilder();
if prjbuild.BuildProject(cmOTABuild, true) then
begin
srcfilename := sprj_path + soutDir + '\' + sprj_name + '.exe';
destFileName := sprj_path + soutDir + '\' + sExeName;
if not sametext(srcfilename, destFileName) then
begin
copyfile(pchar(srcfilename), pchar(destFileName), false);
deletefile(pchar(srcfilename));
end;
end;
end;
function TNTATest.GetProjectResource(Project: IOTAProject): IOTAProjectResource;
var
i: Integer;
Editor: IOTAEditor;
begin
Result := nil;
for i := 0 to (Project.GetModuleFileCount - 1) do
begin
Editor := Project.GetModuleFileEditor(i);
if Supports(Editor, IOTAProjectResource, Result) then
Break;
end;
end;
procedure TNTATest.BuildPrjBefore(sSection, sprj_path: string; var sLst_compilefile: TStringList);
var
sOutDir, sExeName, scompilationDirective, sFileDesc, sFileVersion: string;
sLst_compiledirective, sLst_compilefile_t: TStringList;
i, k: integer;
begin
TAuto_object.New(sLst_compilefile_t, TStringList.Create);
TAuto_object.New(sLst_compiledirective, TStringList.Create);
sLst_compiledirective.Delimiter := ',';
//本程序支持多编译指令输出目标程序文件
//编译指令集中包含空格,使用DelimitedText解析结果不正确,所以采用如下函数解析
SplitColumns(iniRw.readFromIni(sSection, 'compilationDirective', '{$nodefine}'),
sLst_compiledirective, ',');
sOutDir := iniRw.readFromIni(sSection, 'outDir', '..\noDir');
sExeName := iniRw.readFromIni(sSection, 'ExeName', 'noExeName.exe');
sFileDesc := iniRw.readFromIni(sSection, 'FileDescription', 'noFileDesc');
sFileVersion := iniRw.readFromIni(sSection, 'FileVersion', '1.0.0.0');
for k := 0 to sLst_compiledirective.Count - 1 do
begin
scompilationDirective := sLst_compiledirective[k];
for i := 0 to sLst_compilefile.Count - 1 do
begin
if pos(uppercase(scompilationDirective), uppercase(sLst_compilefile[i])) > 0 then
begin
//如果有重复的,会出现out of index错误,所以使用另一个lst作为过渡
//sLst_compilefile.Delete(i);
//如果有重复的,全删除
//break;
continue;
end;
sLst_compilefile_t.Add(sLst_compilefile[i]);
end;
sLst_compilefile_t.Add(scompilationDirective);
sLst_compilefile.Assign(sLst_compilefile_t);
//修改编译指令文件
sLst_compilefile_t.SaveToFile(sprj_path + 'compile.inc');
sLst_compilefile_t.Clear;
end;
BuildPrj(sOutDir, sExeName, sFileDesc, sFileVersion);
end;
procedure TNTATest.BuildAllProjects(sender: TObject);
var
sLst_section, sLst_compilefile: TStringlist;
sprj_path: string;
j: integer;
sSections, sActiveSection: string;
prj: IOTAProject;
begin
prj := GetActiveProject;
if prj = nil then exit;
sprj_path := extractfilepath(prj.FileName);
//工程使用编译指令文件,固定名称为compile.inc,固定路径为工程当前路径
if not fileExists(sprj_path + 'compile.inc') then
begin
Messagebox(IDEHandle, pchar(sprj_path + 'compile.inc文件不存在!'), '提示', mb_iconinformation);
exit;
end;
//工程使用目标程序文件输出配置文件,固定名称为config.ini,固定路径为工程当前路径
TAuto_object.New(iniRw, TIniRW_Env.create(sprj_path + 'config.ini'));
sSections := iniRw.readFromIni('General', 'sections', 'bin_1,bin_2');
sActiveSection := iniRw.readFromIni('General', 'activesection', 'all');
TAuto_object.New(sLst_section, TStringList.Create);
sLst_section.Delimiter := ',';
sLst_section.DelimitedText := sSections;
// SplitColumns(sDefines, sLst_defines, ',');
TAuto_object.New(sLst_compilefile, TStringList.Create);
sLst_compilefile.LoadFromFile(sprj_path + 'compile.inc');
{
if sActiveSection = 'all' then
begin
}
for j := 0 to sLst_section.Count - 1 do
begin
printlogtofile('%d =>%s', [j, sLst_section[j]], sprj_path);
BuildPrjBefore(sLst_section[j], sprj_path, sLst_compilefile);
end;
{
end else begin
printlogtofile('=>%s', [sActiveSection], sprj_path);
BuildPrjBefore(sActiveSection, sprj_path, sLst_compilefile);
end;
}
end;
procedure TNTATest.BuildProject(sender: TObject);
var
sLst_section, sLst_compilefile: TStringlist;
sprj_path: string;
j: integer;
sSections, sActiveSection: string;
prj: IOTAProject;
begin
prj := GetActiveProject;
if prj = nil then exit;
sprj_path := extractfilepath(prj.FileName);
//工程使用编译指令文件,固定名称为compile.inc,固定路径为工程当前路径
if not fileExists(sprj_path + 'compile.inc') then
begin
Messagebox(IDEHandle, pchar(sprj_path + 'compile.inc文件不存在!'), '提示', mb_iconinformation);
exit;
end;
//工程使用目标程序文件输出配置文件,固定名称为config.ini,固定路径为工程当前路径
TAuto_object.New(iniRw, TIniRW_Env.create(sprj_path + 'config.ini'));
sSections := iniRw.readFromIni('General', 'sections', 'bin_1,bin_2');
sActiveSection := iniRw.readFromIni('General', 'activesection', 'all');
TAuto_object.New(sLst_section, TStringList.Create);
sLst_section.Delimiter := ',';
sLst_section.DelimitedText := sSections;
// SplitColumns(sDefines, sLst_defines, ',');
TAuto_object.New(sLst_compilefile, TStringList.Create);
sLst_compilefile.LoadFromFile(sprj_path + 'compile.inc');
j := TMenuItem(Sender).Tag;
printlogtofile('BuildProject %d =>%s', [j, sLst_section[j]], sprj_path);
BuildPrjBefore(sLst_section[j], sprj_path, sLst_compilefile);
end;
procedure TNTATest.ReMoveMenu;
//卸載菜單
begin
if assigned(NewMenu) then NewMenu.Free;
end;
destructor TNTATest.Destroy;
begin
MyNTATest.ReMoveMenu;
if ImageIndex1 <> -1 then
//如果在前面載入圖標的工作出現異常就不釋放圖標,否則會釋放到delphi本身使用的圖標
MyNTATest.FImageList.Delete(MyNTATest.ImageIndex1);
(BorlandIDEServices as IOTAServices).RemoveNotifier(Index);
inherited;
end;
procedure TNTATest.ReCodeEditer(sender: TObject);
begin
end;
procedure TNTATest.drawFormcustom(Sender: TObject; ACanvas: TCanvas;
ARect: TRect; Selected: Boolean);
begin
// self.DynamicAddMenu(nil);
end;
{ TProjectNotifer }
procedure TProjectNotifer.AfterCompile(Succeeded: Boolean);
begin
end;
procedure TProjectNotifer.BeforeCompile(const Project: IOTAProject;
var Cancel: Boolean);
begin
end;
procedure TProjectNotifer.FileNotification(
NotifyCode: TOTAFileNotification; const FileName: string;
var Cancel: Boolean);
begin
//新工程打开时更新wata菜单
if ofnActiveProjectChanged = NotifyCode then MyNTATest.DynamicAddMenu(nil);
end;
initialization
//在組件第一次被安裝時創建了TNTATest
MyNTATest := TNTATest.Create;
finalization
//在組件被卸載時釋放了MyNTATest
MyNTATest.Free;
end.