Delphi Open Tools Api实例研究(一)
先行知识:Delphi/接口/VCL组件包/COM(了解)
来自:http://dev.csdn.net/develop/article/21/21725.shtm
难度:★★☆☆☆

在这篇文章正式开始以前,首先向大家道歉。因为这个月的专栏文章本该很早就发布,但由于一些事情所以一直推迟到现在,并且这个月也只发布了这么一篇。另外,关于这篇文章我觉得我应该感谢csdn上的几位朋友,他们是chechy、FrameSniper、pankun,特别感谢chechy,让我认识到Delphi Open Tools Api(以下简称OTA)的有趣,并决定在其上面投入一些精力。并向我介绍了相当不错的资料。另外还要说明的是虽然题目叫xxxxx(一),但接下来的文章可能不是xxxxx(二)因为这个系列文章的每一篇都会是一个独立的内容,之所以叫xxx(一)是因为我会在以后的文章中不连续的些一写关于OTA的东西。

呵呵,说了那么多的废话,现在开始!但在开头还要再罗嗦一下,大概的介绍一下OTA  OTA是delphi的各个版本中都有提供的一套有趣的接口,运用它你可以任意的扩展delphi的IDE,使之符合自己的需要。例如你可以扩展IDE的菜单、代码编辑器、窗口设计器、属性编辑器和控件编辑器(这个已经在前面的一系列关于VCL开发的文章中说明过)等等,几乎你想得到的所有地方,甚至是code inside功能你都能够扩展!这个激动人心的特性在delphi5以后得到了更好的发展,变的更易于使用。使开发者可以用很少的、很基础的代码完成这些有趣的扩展实现强大的功能。通过OTA也使你能够领略到delphi IDE完美的设计,建立在COM技术基础上使得delphi IDE能够轻易被客户扩展而无须重新编译IDE。

在进行这次的例子前应该指出想要学习OTA的最好资料是位于delphi安装目录下的SourceàToolsAPI里的ToolsAPI.pas文件,它列出了所有OTA的接口并有比较详细的注释说明。另外关于OTA的站点,你可以去http://delphi.about.com/library/weekly/aa033099.htm和http://www.tempest-sw.com/opentools/看看,还可以去borland的新闻组borland.public.delphi.opentoolsapi参与讨论。当然,《delphi5开发者指南》中的26章也介绍了一些OTA的知识,并演示了如何自己实现一个delphi中的向导(本文就不讲述这个了),你们也可以去看看。

由于delphi OTA的版本差异性比较大,这个文章中的内容都以使用delphi7为前提。当delphi IDE处于运行中的时候有一个我们应该清楚的一个重要的实例(Instance)是BorlandIDEServices,它实现了众多OTA接口,换句话说我们可以从BorlandIDEServices得到很多接口,并且这些接口在delphi运行时已经被实现,我们只用通过接口调用接口方法就可以轻松的得到IDE的很多东西,比如菜单、窗口等等,有了这些,扩展delphi IDE便成为了现实。为了能够扩展delphi IDE我们必须要在delphi处于运行时进入,这意味着我们可以有两种方法来实现我们的delphi扩展(也可以叫插件)并向外发布,一种办法是将插件做成设计时VCL组件包(本文采取这种形式,关于VCL组件包请参看我在之前发表的文章),让客户在delphi运行时安装。另一种办法是将插件基于一个DLL并在注册表中的H K E Y _ C U R R E N T_ U S E R / S o f t w a r e / B o r l a n d /D e l p h i / 7 . 0 / E x p e r t s注册,并在DLL中以一个特殊的导出函数作为入口点,delphi IDE在重新启动以后便会加载你的插件(这个方法将在以后的文章中说明)。后者为建立简单化的插件安装程序提供了可能,前者需要用户在delphi运行中如同安装组件一样的进行安装。我们的例子将?

package Package1;

//…省略编译器指示,注意将这个组件设计为设计时(Design Time)的

requires

rtl,

vcl,

designide;

contains

NTAMenu in 'NTAMenu.pas';

end.

可以看到我们将在NTAMenu.pas中实现我们的插件,在这个文件中我们主要用到了以下的接口:IOTAServices,被BorlandIDEServices直接实现的接口,是OTA的一个基础接口,我们用它的GetParentHandle方法来取得delphi IDE的句柄;INTAServices,在delphi运行时被实现的接口,可以用它的方法直接得到delphi IDE的MainMenu、ImageLis、ActionList、ToolBar这样我们就可以直接做很多操作了;IOTAModuleServices、IOTAModule、IOTAEditor、IOTASourceEditor、IOTAEditView,在代码中可以看到我们用这些接口来一步一步得到代码编辑器并最后得到一个可以在代码中当前的光标位置处理数据的IOTAEditPosition接口,我们就用它来向当前光标处插入一句代码,插入大量的代码段还可以使用IOTAEditWriter接口。关于在下面的代码中使用到的接口方法我们会在注释中做说明,没有使用到的接口和其它方法别忘了查看ToolsApi.pas文件。另外可以注意到下面的代码在很多地方进行检测以保证代码在运行后尽量不要出问题以及在出现异常时能够合理释放资源。别忘了,我们的目的是扩展delphi,而并不是要把delphi IDE弄的面目全非。

unit NTAMenu;

interface

uses 
  SysUtils, Classes,Menus,ToolsApi,Controls,ImgList,Graphics,Forms,ComCtrls,windows; 
type

TNTATest = class

private

FMainMenu:TMainMenu;//用来存贮delphi IDE的主菜单

NewMenu:TMenuItem;//我们将要插入的菜单

FImageList:TCustomImageList;//用来存贮delphi IDE主菜单和工具栏的ImageList

ImageIndex1:integer;//检测量,请参看后面的代码

IDEHandle:HWND;//存贮IDE的handle

protected

procedure AddMenu;//加入我们的菜单

procedure ReMoveMenu;//卸载我们的菜单

procedure ReCodeEditer(sender:TObject);//菜单项一的事件

procedure AboutForm(sender:TObject);//菜单项二的事件

public

constructor Create;

destructor Destroy;override;

end;

procedure Register; 
var 
 MyNTATest:TNTATest;

implementation

procedure Register;

begin

MyNTATest.AddMenu;

//和传统组件的同名方法不同,这里没有在组件面板上安装图标

//而是直接调用AddMenu方法添加我们的菜单

end;

{ TNTATest }

constructor TNTATest.Create;

begin

IDEHandle:=(BorlandIDEServices as IOTAServices).GetParentHandle;

//我们用IOTAServices接口的GetParentHandle方法取得了ide的handle

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:='hk.barton';

ImageIndex1:=-1;//没有载入图标

//下面的代码使用for和case来添加两个菜单项有点小题大作,但

//我们展示了一种更通用的方法使你能够添加更多的菜单项,而不必简单的复制代码。

for i:=0 to 2 do

begin

MenuItem[i]:=TMenuItem.Create(NewMenu);//创建子菜单项

case i of

0:

begin

MenuItem[i].Caption:='InsertText';

Icon1:=TIcon.Create;

try

Icon1.LoadFromFile('D:/MyWorks/MyComponent/OTATest/NewForm.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:=ReCodeEditer;//添加事件处理程序

end;

1:MenuItem[i].Caption:='-';//当然还有一个分割符号,其实是3个菜单项

2:

begin

MenuItem[i].Caption:='About';

MenuItem[i].OnClick:=AboutForm;

end;

end;

NewMenu.Add(MenuItem[i]);//添加菜单项

end;

FMainMenu.Items.Add(NewMenu);//最后添加我们的菜单到IDE主菜单

end;

procedure TNTATest.ReCodeEditer(sender:TObject);

var

Module:IOTAModuleServices;

CurentMoudle: IOTAModule;

IntfEditor:IOTAEditor;

Editor:IOTASourceEditor;

EditView:IOTAEditView;

EditWriterPos:IOTAEditPosition;

i:integer;

begin

Module:=BorlandIDEServices as IOTAModuleServices;

CurentMoudle:=Module.CurrentModule;

//使用IOTAModuleServices的CurrentModule方法得到当前打开的工程模块

if CurentMoudle=nil then

begin

messagebox(IDEHandle,'当前没有打开项目文件','hkTest',MB_ICONINFORMATION);

exit;

end;

//遍历已打开工程中所有的文件

for i:=0 to CurentMoudle.ModuleFileCount-1 do

begin

IntfEditor:=CurentMoudle.ModuleFileEditors[i];

//IOTAModule的ModuleFileEditors[]属性得到一个IOTAEditor

if IntfEditor.QueryInterface(IOTASourceEditor,Editor)=S_OK then

//查看遍历到的文件是否是代码文件并已开始在代码编辑器中编辑。

//如果是便通过一个out参数Editor得到一个实现IOTASourceEditor的实例

break;

end;

if Editor=nil then

begin

messagebox(IDEHandle,'当前没有代码编辑窗口','hkTest',MB_ICONINFORMATION);

exit;

end;

EditView:=Editor.EditViews[i];

//使用IOTASourceEditor的 EditViews[]属性得到一个IOTAEditView

EditWriterPos:=EditView.Position;

//使用IOTAEditView的Position属性最终得到一个IOTAEditPosition

EditWriterPos.InsertText('{///  This is add by the OTATest of hk.barton,enjoy days!  ///}');

//IOTAEditPosition的InsertText方法向当前光标位置插入一行代码,这里是一行注释。

end;

procedure TNTATest.AboutForm(sender: TObject);

//一个简单的关于对话框,注意参数中的IDEHandle

begin 
   messagebox(IDEHandle,'This is a test of OTA write by hk.barton','hkTest',MB_ICONINFORMATION);
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);

inherited;

end;

initialization

//在组件第一次被安装时创建了TNTATest

MyNTATest:=TNTATest.Create;

finalization

//在组件被卸载时释放了MyNTATest

MyNTATest.Free;

end.

请注意上面代码中的注释。单就这个例子可能并没有多少用处,然而只要你稍微扩展就可以让这个例子有一点实际用处,你可以加入很多菜单项,每一个项对应一些用户常用的但烦琐的代码,这样就可以免去在开发中输入同样代码的烦琐了,甚至你还可以设置快捷键,也可以做一个设置窗口允许用户自己设置需要的代码和动态的添加菜单项目。当然要完成更复杂的插件你还需要其他的OTA知识,我会在以后的专栏文章中不连续的介绍这些。这次就写到这里,我已经在文章开始的地方推荐了一些学习OTA的资料,另外如果需要这个例子的全部代码,请给我来信索取:hk.barton@sohu.com。

Delphi Open Tools Api实例研究(一)相关推荐

  1. Delphi Open Tools Api实例研究(二)

    Delphi  Open  Tools  Api实例研究(二) 先行知识: Delphi /接口 / DLL / COM(了解) 来自: http://dev.csdn.net/develop/art ...

  2. php版redis插件,SSDB数据库,增强型的Redis管理api实例

    php版redis插件,SSDB数据库,增强型的Redis管理api实例 SSDB是一套基于LevelDB存储引擎的非关系型数据库(NOSQL),可用于取代Redis,更适合海量数据的存储. 另外,r ...

  3. 【刘文彬】以太坊RPC机制与API实例

    原文链接:醒者呆的博客园,https://www.cnblogs.com/Evsward/p/eth-rpc.html 上一篇文章([刘文彬]探路以太坊)介绍了以太坊的基础知识,我们了解了web3.j ...

  4. EMC solutions tools安装实例

    EMC solutions tools安装实例 zhzw02:/var/emc #ls<?xml:namespace prefix = o ns = "urn:schemas-micr ...

  5. TPM零知识学习八 —— tpm组件联调及API实例解析

    一.组件联调准备 1. 运行TPM模拟器 ~/TPM/ibmtpm/ibmtpm$ ./src/tpm_server LIBRARY_COMPATIBILITY_CHECK is ON Startin ...

  6. 《分布式系统:概念与设计》一1.6 实例研究:万维网

    1.6 实例研究:万维网 万维网[www.w3.org I,Berners-Lee 1991]是一个不断发展的系统,用于发布和访问互联网上的资源和服务.通过常用的Web浏览器,用户可以检索和查看多种类 ...

  7. 一个Win32 API实例类(代码收集)

    最近看到别人代码中一个很好的功能类,该类是一个Win32 API实例类,该类功能包括:同一程序禁止启动多次:获取任意窗体:恢复窗体状态:设置窗体焦点等. 该类很实用,与大家分享一下:  1     / ...

  8. MapReduce Java API实例-排序

    场景 MapReduce Java API实例-统计单词出现频率: https://blog.csdn.net/BADAO_LIUMANG_QIZHI/article/details/11941016 ...

  9. MapReduce Java API实例-统计平均成绩

    场景 MapReduce Java API实例-统计单词出现频率: https://blog.csdn.net/BADAO_LIUMANG_QIZHI/article/details/11941016 ...

最新文章

  1. 字符串数组中查找字符串
  2. 【bzoj2245】[SDOI2011]工作安排 费用流
  3. 流量回放开源代码Java_流量回放框架 jvm-sandbox-repeater 的实践
  4. 后怎么恢复_爬山后小腿肌肉酸痛怎么办 ?这样来恢复!|新生活公社
  5. 活动目录实战系列五(更改域名)
  6. SpringMVC 使用注解时控制器传参
  7. 我想在杭州买一套房一百平米左右的房子大概多少钱?
  8. python版贴吧_【和我一起学Python吧】Python3.0与2.X版本的区别
  9. MFC对话框打开一幅图片并在picture显示和图片保存
  10. android中timepicker 常用属性,android – 如何读取TimePicker选择的值?
  11. 阿里云智能语音交互服务-录音文件识别采样率不支持-UNSUPPORTED_SAMPLE_RATE 解决方案
  12. k8s deployment Strategy 更新策略
  13. SOEM 源码解析 ecx_LWR
  14. failed to open mysql_mysql解决fail to open file的方法
  15. 学校计算机统一编号,华中科技大学人员编号编码管理办法
  16. 智慧停车(十一) 前期运营策略-停车人,合伙人,授权人
  17. 什么是远程桌面连接?win11系统如何启用远程桌面连接?
  18. 游戏原来也可以严肃?——严肃游戏及在中国的应用前景
  19. BT656(4:2:2)YCbCr标准规范
  20. CodeForces_29B

热门文章

  1. Html5学习进阶一 视频和音频
  2. Master主动向Slave发送binlog?还是Slave主动向Master要binlog?
  3. iOS开发之UITableView全面解析
  4. Jquery页面跳转
  5. GridView控件与DataList控件DataKeyNames、DataKeysField、DataKeys用法
  6. php数组如何插入,PHP如何在数组指定位置插入单元
  7. Windows的图形设备接口(GDI)入门 上篇
  8. 路由协议之间的经典对比
  9. JavaScript获取java变量值
  10. 2022-04-11 查询PostGIS和pgRouting的版本号