有沒(méi)有使用過(guò)Adobe Photoshop如果用過(guò),你就會(huì)對(duì)插件的概念比較熟悉。對(duì)外行人來(lái)說(shuō),插件僅僅是從外部提供給應(yīng)用程序的代碼塊而已(舉個(gè)例子來(lái)說(shuō),在一個(gè)DLL中)。一個(gè)插件和一個(gè)普通DLL之間的差異在于插件具有擴(kuò)展父應(yīng)用程序功能的能力。例如,Photoshop本身并不具備進(jìn)行大量的圖像處理功能。插件的加入使其獲得了產(chǎn)生諸如模糊、斑點(diǎn),以及其他所有風(fēng)格的奇怪效果,而其中任何一項(xiàng)功能都不是父應(yīng)用程序自身所具有的。
對(duì)于圖像處理程序來(lái)說(shuō)這很不錯(cuò),可是為什么要花偌大的力氣去完成支持插件的商業(yè)應(yīng)用程序呢?假設(shè),我們舉個(gè)例子,你的應(yīng)用程序要產(chǎn)生一些報(bào)表。你的客戶肯定會(huì)一直要求更新或者增加新的報(bào)表。你可以使用一個(gè)諸如Report Smith的外部報(bào)表生成器,這是個(gè)不怎么樣的解決方案,需要發(fā)布附加的文件,要對(duì)用戶進(jìn)行額外的培訓(xùn),等等。你也可以使用QuickReport,不過(guò)這會(huì)使你身處版本控制的噩夢(mèng)之中——如果每改變字體你就要Rebuild你的應(yīng)用程序的話。
然而,只要你把報(bào)表做到插件中,你就可以使用它。需要一個(gè)新的報(bào)表嗎?沒(méi)問(wèn)題,只要安裝一個(gè)DLL,下次應(yīng)用程序啟動(dòng)時(shí)就會(huì)看見(jiàn)它了。另外一個(gè)例子是處理來(lái)自外部設(shè)備(比如條形碼掃描器)的數(shù)據(jù)的應(yīng)用程序,為了給用戶更多的選擇,你不得不支持半打的各種設(shè)備。通過(guò)將每種設(shè)備接口處理例程寫成插件,不用對(duì)父應(yīng)用程序作任何變動(dòng)就可以獲得大程度的可伸縮性。
入門
在開始寫代碼之前重要的事情就是搞清楚你的應(yīng)用程序到底需要擴(kuò)展哪些功能。這是因?yàn)椴寮峭ㄟ^(guò)一個(gè)特定的接口與父應(yīng)用程序交互的,而這個(gè)接口將根據(jù)你的需要來(lái)定義。在本文中,我們將建立3個(gè)插件,以便展示插件與父應(yīng)用程序相交互的幾種方式。
我們將把插件制作成DLL。不過(guò),在做這項(xiàng)工作之前,我們得先制作一個(gè)外殼程序來(lái)載入和測(cè)試它們。第一個(gè)插件沒(méi)有完成什么大不了的功能,實(shí)際上,它所做的只是返回一個(gè)描述自己的字符串。不過(guò),它證明了很重要的一點(diǎn)——不管有沒(méi)有插件應(yīng)用程序都可以正常運(yùn)行。如果沒(méi)有插件,它就不會(huì)出現(xiàn)在已安裝的插件列表中,但是應(yīng)用程序仍然可以正常的行使功能。
我們的插件外殼程序與普通應(yīng)用程序之間的不同就在于工程源文件中出現(xiàn)在uses子句中的Sharemem單元和加載插件文件的代碼。任何在自身與子DLL之間傳遞字符串參數(shù)的應(yīng)用? 都需要Sharemem單元,它是DelphiMM.dll(Delphi提供該文件)的接口。要測(cè)試這個(gè)外殼,需要將DelphiMM.dll文件從Delphi\Bin目錄復(fù)制到path環(huán)境變量所包含的路徑或者應(yīng)用程序所在目錄中。發(fā)布終版本時(shí)也需要同時(shí)分發(fā)夢(mèng)募插件通過(guò)LoadPlugins過(guò)程載入到這個(gè)測(cè)試外殼中,這個(gè)過(guò)程在主窗口的FormCreate事件中調(diào)用。該過(guò)程使用FindFirst和FindNext函數(shù)在應(yīng)用程序所在目錄中查找插件文件。找到一個(gè)文件以后,就使用LoadPlugins過(guò)程將其載入。
{ 在應(yīng)用程序目錄下查找插件文件 } procedure TfrmMain.LoadPlugins;
var
sr: TSearchRec;
path: string;
Found: Integer;
begin
path := ExtractFilePath(Application.Exename);
try
Found := FindFirst(path + cPLUGIN_MASK, 0, sr);
while Found = 0 do begin
LoadPlugin(sr);
Found := FindNext(sr);
end;
finally
FindClose(sr);
end;
end;
{ 加載指定的插件 DLL. } procedure TfrmMain.LoadPlugin(sr: TSearchRec);
var
Description: string;
LibHandle: Integer;
DescribeProc: TPluginDescribe;
begin
LibHandle := LoadLibrary(Pchar(sr.Name));
if LibHandle $#@60;$#@62; 0 then
begin
DescribeProc := GetProcAddress(LibHandle, cPLUGIN_DESCRIBE);
if Assigned(DescribeProc) then
begin
DescribeProc(Description);
memPlugins.Lines.Add(Description);
end
else
begin
MessageDlg(’File "’ + sr.Name + ’" is not a valid plug-in.’,
mtInformation, [mbOK], 0);
end;
end
else
MessageDlg(’An error occurred loading the plug-in "’ +
sr.Name + ’".’, mtError, [mbOK], 0);
end;
對(duì)于圖像處理程序來(lái)說(shuō)這很不錯(cuò),可是為什么要花偌大的力氣去完成支持插件的商業(yè)應(yīng)用程序呢?假設(shè),我們舉個(gè)例子,你的應(yīng)用程序要產(chǎn)生一些報(bào)表。你的客戶肯定會(huì)一直要求更新或者增加新的報(bào)表。你可以使用一個(gè)諸如Report Smith的外部報(bào)表生成器,這是個(gè)不怎么樣的解決方案,需要發(fā)布附加的文件,要對(duì)用戶進(jìn)行額外的培訓(xùn),等等。你也可以使用QuickReport,不過(guò)這會(huì)使你身處版本控制的噩夢(mèng)之中——如果每改變字體你就要Rebuild你的應(yīng)用程序的話。
然而,只要你把報(bào)表做到插件中,你就可以使用它。需要一個(gè)新的報(bào)表嗎?沒(méi)問(wèn)題,只要安裝一個(gè)DLL,下次應(yīng)用程序啟動(dòng)時(shí)就會(huì)看見(jiàn)它了。另外一個(gè)例子是處理來(lái)自外部設(shè)備(比如條形碼掃描器)的數(shù)據(jù)的應(yīng)用程序,為了給用戶更多的選擇,你不得不支持半打的各種設(shè)備。通過(guò)將每種設(shè)備接口處理例程寫成插件,不用對(duì)父應(yīng)用程序作任何變動(dòng)就可以獲得大程度的可伸縮性。
入門
在開始寫代碼之前重要的事情就是搞清楚你的應(yīng)用程序到底需要擴(kuò)展哪些功能。這是因?yàn)椴寮峭ㄟ^(guò)一個(gè)特定的接口與父應(yīng)用程序交互的,而這個(gè)接口將根據(jù)你的需要來(lái)定義。在本文中,我們將建立3個(gè)插件,以便展示插件與父應(yīng)用程序相交互的幾種方式。
我們將把插件制作成DLL。不過(guò),在做這項(xiàng)工作之前,我們得先制作一個(gè)外殼程序來(lái)載入和測(cè)試它們。第一個(gè)插件沒(méi)有完成什么大不了的功能,實(shí)際上,它所做的只是返回一個(gè)描述自己的字符串。不過(guò),它證明了很重要的一點(diǎn)——不管有沒(méi)有插件應(yīng)用程序都可以正常運(yùn)行。如果沒(méi)有插件,它就不會(huì)出現(xiàn)在已安裝的插件列表中,但是應(yīng)用程序仍然可以正常的行使功能。
我們的插件外殼程序與普通應(yīng)用程序之間的不同就在于工程源文件中出現(xiàn)在uses子句中的Sharemem單元和加載插件文件的代碼。任何在自身與子DLL之間傳遞字符串參數(shù)的應(yīng)用? 都需要Sharemem單元,它是DelphiMM.dll(Delphi提供該文件)的接口。要測(cè)試這個(gè)外殼,需要將DelphiMM.dll文件從Delphi\Bin目錄復(fù)制到path環(huán)境變量所包含的路徑或者應(yīng)用程序所在目錄中。發(fā)布終版本時(shí)也需要同時(shí)分發(fā)夢(mèng)募插件通過(guò)LoadPlugins過(guò)程載入到這個(gè)測(cè)試外殼中,這個(gè)過(guò)程在主窗口的FormCreate事件中調(diào)用。該過(guò)程使用FindFirst和FindNext函數(shù)在應(yīng)用程序所在目錄中查找插件文件。找到一個(gè)文件以后,就使用LoadPlugins過(guò)程將其載入。
{ 在應(yīng)用程序目錄下查找插件文件 } procedure TfrmMain.LoadPlugins;
var
sr: TSearchRec;
path: string;
Found: Integer;
begin
path := ExtractFilePath(Application.Exename);
try
Found := FindFirst(path + cPLUGIN_MASK, 0, sr);
while Found = 0 do begin
LoadPlugin(sr);
Found := FindNext(sr);
end;
finally
FindClose(sr);
end;
end;
{ 加載指定的插件 DLL. } procedure TfrmMain.LoadPlugin(sr: TSearchRec);
var
Description: string;
LibHandle: Integer;
DescribeProc: TPluginDescribe;
begin
LibHandle := LoadLibrary(Pchar(sr.Name));
if LibHandle $#@60;$#@62; 0 then
begin
DescribeProc := GetProcAddress(LibHandle, cPLUGIN_DESCRIBE);
if Assigned(DescribeProc) then
begin
DescribeProc(Description);
memPlugins.Lines.Add(Description);
end
else
begin
MessageDlg(’File "’ + sr.Name + ’" is not a valid plug-in.’,
mtInformation, [mbOK], 0);
end;
end
else
MessageDlg(’An error occurred loading the plug-in "’ +
sr.Name + ’".’, mtError, [mbOK], 0);
end;

