★★★★★大家对开发专家有兴趣我这里还有一点资料给大家共享一下★★★
在Delphi中,要撰寫專家最主要的著眼點在於「如何撰寫專家」及「與IDE環境互動」,在Hidden Paths of Delphi 3.0的寫法前者是由TIExpert繼承下來並
且覆寫所有的方法,使我們對Delphi的整合環境有了第一次接觸,只要由TIExpert中呼叫我們寫的程式,也就會讓Delphi在設計時期呼叫我們的程式;但是若
您要和Delphi的整合開發環境有親密接觸,那你得接觸TIToolServices類別才行,其中的函式可以讓你把玩Delphi整合環境,比如說你可以開啟或關閉一個專
案,或是寫出許多可以實際產生程式的專家。
當我拿到產生BizSnap程式碼的專家時,因為最近在看Hidden Paths of Delphi 3.0這本書,學Open Tools API如何用,所以也好奇的把其中的原始碼調出來
瞧瞧,赫然發覺其中的程式我完全看不懂,怎麼Hidden Paths Of Delphi中的寫法完全沒看到,讓我大失所望;又調出Delphi的Demos中的Open Tools API範
例,又是類似的寫法,都沒有自TIExpert繼承,也看不到TIToolsServices,反倒是implement一大堆Interface。
對於這些奇怪的寫法,我找不到文件有說明,只好自行分析Open Tools API的原始碼,看來這次尋幽訪勝得靠自己了;)
在經過一夜的Trace後,我想我大致上可以掌握了在Delphi 6所附的Open Tools API範例的寫法,在ToolsAPI單元中宣告的這些介面,不再像以往的
TIInterface是繼承TObject來模擬Interface,而是用Complier原生支援的Interface,所有的Interface繼承至IUnknown;其實你在Delphi 5、6中還是可以照
Hidden Paths of Delphi 3.0中的寫法,不過用Delphi所附的範例寫法會比較好,底下是我想到的優點:
你的專家會比較有彈性:
使用傳統的方法你必需要覆寫所有的專家方法,但是其實有些方法是不需要覆寫的,比如說若你的專家是要放在物件寶庫中那麼其實你不用覆寫TIExpert以下
的這個方法「function GetMenuText: string; virtual; stdcall; abstract;」這個函式是用來回傳假如你寫得是一個標準專家或是附加專家那麼在選單上
要出現的文字是什麼;藉由選擇性的implement你的專家Interface,可以只要改寫真正需要的方法就可以了。
你的專家會有更多的功能:
藉由IBorlandIDEServices這個介面,你可以存取到比用IToolServices更多的功能,在ToolsAPI這個單元當中只要是結尾為Services的Interface,那麼就可
以被IBorlandIDEServices所轉型後使用,也就是說IBorlandIDEServices替代了IToolServices,Delphi的整合環境會自動產生一個物件,此物件實作了
IBorlandIDEServices,而此物件會被自動指定到BorlandIDEServices這個IBorlandIDEServices介面變數,使得藉由BorlandIDEServices可以和Delphi的整合
環境深度對話,下面是摘自ToolsAPI可被BorlandIDEServices轉型的Interface及簡短的說明,我第一次接觸還真的蠻驚訝的,竟然可以轉這麼多型別。
Interface名稱 摘要說明
IBorlandIDEServices 與整合環境互動的始祖,可轉型至結尾為Services的Interface,以真正提供服務。
INTAServices 可以換掉整合環境的圖示
IOTAActionServices 可以用來開啟檔案、關閉檔案、存檔案以及開啟專案。
IOTADebuggerServices 用來得知除錯資訊,比如說設定了多少個中斷點,這些中斷點又在哪裡?
IOTAEditorServices 用來和編輯器互動,比如說可以得知目前在編輯器中游標的位置或是在編輯器中加碼
IOTAKeyBindingServices 得知鍵盤訊息,不是很清楚
IOTAKeyboardServices 得知鍵盤訊息,不是很清楚
IOTAMessageServices 通常在程式執行時程式編輯器下面會有一個視窗出現,用來顯示Complier的訊息,這個Interface就是和那個視窗互動的
IOTAServices 用來得知目前的編輯環境是VCL還是CLX
IOTAModuleServices 提供產生模組、獲得目前整合環境的模組資訊
IOTAPackageServices 用來顯示Packages資訊
IOTAToDoServices 提供對To-Do List的功能,比如說加入一些To-Do項目
IOTAWizardServices 用來在設計時期動態的加入或移除專家,提供了以往只能用註冊的方式
您可以看到這些豐富的介面,真是多啊!這每一個介面又牽扯到至少兩三個相關的介面,看來要詳細介紹完可得要完全改寫了Hidden Paths of Delphi了,我
可沒那個本事,能讓你入門就不錯了;看來Code Site、Code Rush這類軟體就是大量用到了這些介面。
有兩種介面我還不是很清楚,就是IOTAKeyBindingServices及IOTAKeyboardServices,我試圖開啟Editor Keybinding這個範例,裡面有對於這兩個介面的程
式碼,可是無法正確的Install成功,若您知道解法的話請告訴我。
底下我們以Delphi中所附的Open Tools API範例PackageDemo來說明專家的寫法及如何與整合環境互動,明白的講,就是要告訴你兩件事:
1不要再繼承至TIExpert了啦!用繼承多個ToolsAPI單元中的介面這樣的方式會比較好。
2不要再用ToolsServices這個類別了啦!用BorlandIDEServices會比較好。
先來看看在PackageDemo中的WizMain單元中對於一個專家的宣告:
TMenuIOTATest = class(TNotifierObject, IOTAWIzard, IOTAMenuWizard)
public
function GetMenuText: string;
function GetIDString: string;
function GetName: string;
function GetState: TWizardState;
procedure Execute;
end;
看起來是不是比TIExpert的宣告清爽了許多呢?有關TIExpert的宣告你可在ExptIntf這個單元中找到,我就不列出來了。其中TnotifierObject是基本和整合
環境互動的類別,當整合環境發生事件時會通知您,不過TnotifierObject暗中把這些方法做掉了,ToolsAPI單元中的註解中有提到,通常我們都不會需要這
些方法,好吧!也不再追究詳細的用法了。
而實作IOTAWIzard這個介面是一個重要的關鍵,代表要這個類別要成為一個專家,底下是其介面宣告:
IOTAWizard = interface(IOTANotifier)
['{B75C0CE0-EEA6-11D1-9504-00608CCBF153}']
{ Expert UI strings }
function GetIDString: string; //傳回唯一代表這個專家的字串
function GetName: string; //傳回這個專家的名稱
function GetState: TWizardState; //傳回這個專家的狀況,是要致能或是選取
{ Launch the AddIn }
procedure Execute; //專家實際執行時會呼叫的程式碼
end;
眼尖的朋友一定會發現,原本在TIExpert中有的GetStyle方法已不復見,這個函式的傳回值決定了專家的種類,是為附加專家、標準專家、專案專家或是表格
專家;底下是在ToolsAPI中結尾為Wizard的介面摘要說明,我們可以從其中的關係很容易推出若我們要寫哪一種類的專家就要實作哪個介面,是的,專家種類
的選擇由函式傳回值改為實作某個介面了!
介面名稱 父介面 專家類型
IOTAWizard IOTANotifier 最基本的專家介面
IOTAMenuWizard IOTAWizard 標準專家
IOTARepositoryWizard IOTAWizard 物件寶庫中的專家介面
IOTARepositoryWizard60 IOTARepositoryWizard 可以判斷是CLX還是VCL
IOTAProjectWizard IOTARepositoryWizard 專案專家
IOTAFormWizard IOTARepositoryWizard 表格專家
若要寫個表格專家就實作IOAFormaWizard介面,一塊實作它的父介面IOTARepositoryWizard及IOTAWizard;若要寫個標準專家就實作IOTAMenuWizard介面,一
塊實作它的父介面IOTAWizard,這樣的原則很容易掌握吧。在下表中更可清楚看出TIExpert與IOTAXXXWizard介面之間的差異及實作方法的說明:
IOTAXXWizard 函式 說明
TIExpert IOTAWizard function GetIDString: string; 傳回識別專家的唯一字串
function GetName: string; 傳回專家的名稱
function GetState: WizardState; 在選單項目中的狀態
procedure Execute; 實際實行專家程式碼的地方
IOTAMenuWizard function GetMenuText: string; 傳回出現在選單的名稱
IOTARepositoryWizard function GetAuthor: string; 傳回作者名稱
function GetComment: string; 傳回專家簡要說明
function GetPage: string; 要在物件寶庫中哪一頁安裝
function GetGlyph: Cardinal; 傳回代表專家的圖示
IOTAProjectWizard 取代TIExpert中的GetStyle方法 只是單純的宣告為專案專家
IOTAFormWizard 只是單純的宣告為表格專家
IOTARepositoryWizard60 function GetDesigner: string;
property Designer: string read GetDesigner; 獲得目前的編輯環境是在VCL下還是CLX
容我再說明一次,在用IOTAxxWizard介面來寫專家你只要改寫需要的方法就可以了,不用再覆載所有TIExpert的方法了;IOTAProjectWizard及
IOTAFormWizard只是單純的標記此專家為專案專或是表格專家,取代了以往的GetStyle函式,在其中沒有任何的方法宣告,但是Delphi整合環境會用
QueryInterface的方式來查出這些介面來判斷正確的專家類型,而安裝在正確的地方。
還有一個值得一題的介面IOTARepositoryWizard60,此函式並沒有出現在 Delphi 6.0的TIExpert宣告中,實作此函式可以得知目前的設計環境是在VCL下還是
CLX下,從種種的跡象看來,似乎TIExpert和TIToolsServices都和BDE的命運一樣,進入了維護階段。
以下我列出PackagesDemo中的WizMain單元程式碼,對於容易理解而且上面有提過的介面及方法就用註解的方式來標記,其他的才特別加以說明。
unit WizMain;
interface
uses
Windows, ToolsAPI, Forms, Dialogs, FrmMain, SysUtils, Graphics;
//以前至少要使用兩個Open Tools API的單元ExptIntf及ToolInf,前者是專家的//宣告,後者是TIToolServices的宣告,現在看到ToolsAPI這個單元全包了
。
type
TMenuIOTATest = class(TNotifierObject, IOTAWIzard, IOTAMenuWizard)
public
function GetMenuText: string;
function GetIDString: string;
function GetName: string;
function GetState: TWizardState;
procedure Execute;
end;
procedure Register;
var
//宣告介面變數,可取得Package資訊的介面
PackageTest: IOTAPackageServices;
PackageForm: TForm2;
implementation
procedure Register;
begin
RegisterPackageWizard(TMenuIOTATest.create as IOTAWizard);//註冊專家
end;
//出始化專家,獲得IOTAPackageServices介面
procedure InitExpert;
begin
//將BorlandIDEServices轉型為IOTAPackageServices以取得Package相關資料
PackageTest := (BorlandIDEServices as IOTAPackageServices);
end;
//結束專家要做的事情
procedure DoneExpert;
begin
{ stubbed out }
end;
{ TMenuIOTATest }
procedure TMenuIOTATest.Execute;
var
i: Integer;
begin
if BorlandIDEServices <> nil then
begin
PackageForm := TForm2.create(Nil);
with PackageForm do
{Package information}
//取得在整合環境中的Package數目
for i := 0 to PackageTest.GetPackageCount-1 do
//根據索引來取得Package的名稱
ListBox1.Items.Add(PackageTest.GetPackageName(i));
PackageForm.ShowModal;
PackageForm.Free;
end;
end;
//傳回唯一識別專家的字串
function TMenuIOTATest.GetIDString: string;
begin
Result := 'PackageInfoMenuId';
end;
//傳回了在Help選單項目上出現的文字,為「Package Demo」
function TMenuIOTATest.GetMenuText: string;
begin
Result := 'Packa&ge Info';
end;
//傳回專家的名稱
function TMenuIOTATest.GetName: string;
begin
Result := 'PackageInfoMenu';
end;
//傳回專家在選單項目上的狀態,此為Enabled
function TMenuIOTATest.GetState: TWizardState;
begin
Result := [wsEnabled];
end;
initialization
InitExpert;
finalization
DoneExpert;
end.
在以上的程式碼中您會發覺幾個重要的改變:
1註冊專家的方式:
procedure Register;
begin
RegisterPackageWizard(TMenuIOTATest.create as IOTAWizard);//註冊專家
end;
您必須先將您的「專家類別」轉型成為「專家介面」再用RegisterPackageWizard這個函式來進行註冊,取代了註冊TIExpert專家的RegisterLibraryExpert函
式。
2如何與整合環境互動:
procedure InitExpert;
begin
PackageTest := (BorlandIDEServices as IOTAPackageServices);
end;
以往,一拖拉庫的函式都放在TIToolServices中,這次,放在IBorlandIDEServices介面,你要若取得相關的資訊,得轉型至相關的介面才可以使用,這樣分
類看起來的確是清爽許多,詳細的BorlandIDEServices可轉型至何介面,請看本文第一張附表。
3如何使用IOTAxxxServices類別:
底下列出這個範例中的IOTAPackageServices宣告,
//繼承至IUnknown
IOTAPackageServices = interface(IUnknown)
['{26EB0E4D-F97B-11D1-AB27-00C04FB16FB3}']
//取得目前整合環境中已安裝的Package數目
function GetPackageCount: Integer;
//根據索引值取得Package的名稱
function GetPackageName(Index: Integer): string;
//根據Package的索引值找出在這個Package中有幾個元件
function GetComponentCount(PkgIndex: Integer): Integer;
//根據Package及Component的索引值找出對應的元件名稱
function GetComponentName(PkgIndex, CompIndex: Integer): string;
//下面的屬性只是上面的函式再包裝而已
property PackageCount: Integer read GetPackageCount;
property PackageNames[Index: Integer]: string read GetPackageName;
property ComponentCount[PkgIndex: Integer]: Integer read GetComponentCount;
property ComponentNames[PkgIndex, CompIndex: Integer]: string read GetComponentName;
end;
行文至此,我想這個範例對你而言已經沒有什麼秘密了吧!剩下的就是一些UI的程式碼而已,大部份放在FrmMain這個單元中,不難,你應該可以輕易瞭解。
ToolsAPI還有許多強大的介面等待我們去發掘,這篇文章只是個起點而已,下次來看看Borland所釋出的BizSnap專家是如何寫得吧!
最後,我們來討論一下,你覺得Borland設計這些與整合環境互動的概念與技巧可否應用於你的專案上呢?你寫的物件別人只能經由介面來存取,完全看不到
你實作程式碼,若要使用者提供一些資訊的話,就讓他實作某個介面,你的內部類別再去呼叫這些程式碼以真正提供服務。
假如你因為這小段話而獲得啟發的話,可別忘了把你的成品發表出來看看哦!