📄 插件实现.txt
字号:
Result := S_OK;
case ActionID of
ACTION_RENAME :
//文件改名
if (TfrmRename.Rename (Path, NewName)) then
//如果改名成功,给万能浏览器主程序发送一个通知
FExplorer.RenamePath (Path, NewName);
//如果还有其它动作,在这里实现...
end;
end;
这里,我们首先检查ActionID参数并对ACTION——RENAME动作进行响应。对于ACTION_RENAME,我们只是简单地打开一个窗体(TfrmRename),允许用户为文件输入新名字。 如果改名成功的话,就调用FExplorer.RenamePath告诉万能浏览器主程序有一个结点已经改名,主程序会及时地更新它的用户界面以反映这种改变。
这是一个插件如何和它的主程序进行回调的示例。通常,这么做是必需的,因为主程序和插件需要同步更新它们共同拥有的数据。
最后一件事就是以某种方式为我们的插件服务器注册及注销组件目录。最容易的办法是每当注册了我们的插件时,我们也一同注册目录信息,注销插件的同时注销目录信息。对于DLL服务器来说,相应的注册和注销入口是下面的两个公布的函数:DLLRegisterServer(注册)与DLLUnregisterServer(注销)。
先看一下注册过程:
In FileSystemPlugin.dpr
library FileSystemPlugin;
//overidden to include categories registration
function DllRegisterServer: HResult; stdcall;
begin
//调用缺省例程
Result := ComServ.DllRegisterServer;
//注册为浏览插件
RegisterAsExplorableClass (Class_FileSystemExplorable, True); //True即注册
end;
exports
DllGetClassObject,
DllCanUnloadNow,
DllRegisterServer,
DllUnregisterServer;
...
我们对DLLRegisterServer使用了“overriding”技术保证了同时执行目录注册过程RegisterAsExplorableClass。(RegisterAsExplorableClass的具体实现略)
//注册一个指定的类作为一个explorable服务器
procedure RegisterAsExplorableClass (const CLSID : TCLSID; Register : boolean);
var
CatReg : ICatRegister;
begin
CatReg := StdComponentCategoryMgr as ICatRegister;
if (Register) then
begin
//先注册CATID_Explorable目录
//ExplorableCategoryInfo中放的内容在前面已经讨论过
OleCheck (CatReg.RegisterCategories (1, @ExplorableCategoryInfo));
//然后注册支持这个目录的CLSID
OleCheck (CatReg.RegisterClassImplCategories (CLSID, 1, @ExplorableCategoryInfo));
end
else begin
//这里是注销部分
end;
end;
//返回标准组件目录管理器
function StdComponentCategoryMgr : IUnknown;
begin
Result := CreateComObject (CLSID_StdComponentCategoryMgr);
end;
我们在这里做的只不过是注册“Explorable Plugins”目录,及注册实现这一目录的FileSystemExplorable的coclass。该讨论的前面已经讲过了。
注册过程同样照此办理:
In FileSystemPlugin.dpr
library FileSystemPlugin;
//重载以实现目录的注销
function DllUnregisterServer: HResult; stdcall;
begin
//注销万能浏览器的插件
RegisterAsExplorableClass (Class_FileSystemExplorable, False); //False表示注销
//返回缺省的注销句柄
Result := ComServ.DllUnregisterServer;
end;
exports
DllGetClassObject,
DllCanUnloadNow,
DllRegisterServer,
DllUnregisterServer;
...
相应的,RegisterAsExplorableClass的注销部分定义如下:
//注册一个指定的类作为一个explorable服务器
procedure RegisterAsExplorableClass (const CLSID : TCLSID; Register : boolean);
var
CatReg : ICatRegister;
begin
CatReg := StdComponentCategoryMgr as ICatRegister;
if (Register) then
begin
//这里是注册部分
end
else begin
//注意:我们不能注销CATID_Explorable,因为其它服务器可能仍在用它!
//注销支持这个目录的CLSID
OleCheck (CatReg.UnregisterClassImplCategories (CLSID, 1, @ExplorableCategoryInfo));
DeleteRegKey ('CLSID\' + GuidToString (CLSID) + '\' + 'Implemented Categories');
end;
end;
//返回标准组件目录管理器
function StdComponentCategoryMgr : IUnknown;
begin
Result := CreateComObject (CLSID_StdComponentCategoryMgr);
end;
注意我们额外地调用了DeleteRegKey删除“Implemented Categories”子键,从而完全地从注册表中删除我们的coclass。
另一个注册目录信息的方法是建立自己的定制的类代理(class factory),它继承自TComObjectFactory 。TComObjectFactory有一个虚拟方法UpdateRegistry,可以通过重载实现自定义的注册和注销动作。
万能浏览器主程序的实现
随着插件的完工(一定不要忘记注册您的插件),现在到了主程序部分。如前所述,我们的万能浏览器主程序是一个MDI程序,每个MDI子窗口掌管着一个浏览插件。作为实现,当万能浏览器加载时,它寻找所有注册过的浏览插件并把它们放到一个菜单列表里。用户从菜单中选择任何一个插件项目,我们将打开一个MDI子窗体(TfrmExplorerHost)并把选中的插件纳入窗体。
图:“Explorable Plugins”菜单包括了已注册的插件的列表
我们简单地使用前面提到的COM组件目录设备来取得可用的插件,具体说来,是用到了ICatInformation。实现方法略。
procedure TfrmMain.LoadExplorableClasses;
var
Count, i : integer;
Explorable : IExplorable;
Description : widestring;
MenuItem : TMenuItem;
begin
//取得explorable服务器列表
//FExplorableClasses是一个CLSID数组
Count := GetExplorableClasses (FExplorableClasses);
if (Count > 0) then
begin
//取得每一个公布的插件描述并放入子菜单
for i := 1 to Count do
begin
//创建Explorable插件
Explorable := CreateComObject (FExplorableClasses [i]) as IExplorable;
//取得插件描述
OleCheck (Explorable.GetDescription (Description));
//创建一个新菜单项
MenuItem := TMenuItem.Create (Self);
MenuItem.Caption := Description;
//把FExplorableClasses数组的索引放入Tag属性
MenuItem.Tag := i;
//放入菜单中
miExplore.Add (MenuItem);
end;
end;
end;
这段代码取得所有可用coclass的CLSID放入FExplorableClasses数组,然后取得数组中每个插件的描述放入一个菜单列表。
以下是一个真正的GetExplorableClasses:
type
TExplorableClasses = array [1..50] of TCLSID; //50足够大了
//返回Explorable 服务器的CLSIDs
function GetExplorableClasses (var ExplorableClasses : TExplorableClasses) : integer;
var
CatInfo : ICatInformation;
Enum : IEnumGuid;
Fetched : UINT;
begin
Result := 0;
//得到标准目录信息管理器
CatInfo := StdComponentCategoryMgr as ICatInformation;
//取得所有已注册的Explorable类
//ExplorableCategoryInfo中的内容前面已经讨论过
OleCheck (CatInfo.EnumClassesOfCategories (1, @ExplorableCategoryInfo, 0, NIL, Enum));
//把Explorable类放入ExplorableClasses数组
if (Enum <> NIL) then
begin
OleCheck (Enum.Reset);
//填充ExplorableClasses数组
//注意如果Fetched >= High (ExplorableClasses),那么可能还有!
//但对于我们的目的来说,这么简单的代码就够了
OleCheck (Enum.Next (High (ExplorableClasses), ExplorableClasses [1], Fetched));
Result := Fetched;
end;
end;
前面讲过,ICatInformation.EnumClassesOfCategories用来取得所有实现一个指定目录的coclass,然后重复产生与所有匹配CLSID相应的数组。
到此为止,我们已经取得了所有可用的插件并把它们放进菜单。当用户选择了其中的一个时,我们取得选中的插件的coclass,然后把插件装入万能浏览器的主窗体。实现部分略。
//用户从主菜单中选择了一个插件时调用
procedure TfrmMain.miExplorableItemClick(Sender: TObject);
var
ExplorableClass : TCLSID;
Explorable : IExplorable;
begin
//取得选中的Explorable类
//Tag属性包含着FExplorableClasses数组的序号
ExplorableClass := FExplorableClasses [(Sender as TMenuItem).Tag];
//创建Explorable插件
Explorable := CreateComObject (ExplorableClass) as IExplorable;
//往一个新的explorer主窗体中调入Explorable服务器
TfrmExplorerHost.Load (Explorable);
end;
这段程序取得选取的菜单项对应的插件coclass。然后我们把新插件加载到万能浏览器的主窗体。
万能浏览器的主窗体分为两个部分:左边是TreeView目录树(tvwExplorer),右边是ListView列表项(lvwProperties)。目录树显示插件的层次结构,当选中一个树结点时列表项显示它的属性。对FileSystemPlugin来说,目录树显示了您的文件系统的结构图而列表项显示文件属性(文件名、文件大小等)。如图:
小虫~ (2001-11-13 00:22:00)
好文
吴下阿蒙 (2001-11-13 00:30:00)
插件结构的实现之原理篇
http://www.ccidnet.com/tech/guide/2001/09/29/58_3374.html
(文:Binh Ly 安富国编译 2001年09月29日 15:41)
插件结构有助于编写有良好的扩充和定制功能的应用程序。例如,您可能想做一个有三个不同版本的软件(标准版、专业版和企业版),您不必写三套不同的代码,只需建立一个单独的主程序(host application),通过挂接插件实现三个不同的版本。这就是说,标准版=主程序+标准版插件;专业版=主程序+标准版插件+专业版插件;企业版=主程序+标准版插件+专业版插件+企业版插件。
另一个使用插件的好处是可以编制特定的功能模块挂接到您的主程序上面。这是近年来十分常见的一种技术,许多软件甚至操作系统或其外壳程序都有使用。例如,Win32的外壳程序资源管理器提供了大量的API和COM接口允许您编写自己的外壳扩展程序或者说外壳插件。
说到COM,它实际上是一个实现插件的极好的体系结构。您可以用COM建立一个包含有主程序和插件的框架,它们可能是用不同的编程语言写成(VB、Delphi、C++等等),但由于建立在COM之上,它们之间完全能够无缝地结合在一起。
下面,我们将会一步步地制作一个建立在COM之上的插件框架。
万能浏览器
言归正传,一个插件框架包括两个部分:主程序(host)和插件(plug-in)。主程序即是“包含”插件的程序。主程序公布一个标准接口,IHost,作为插件和主程序通信时用;同样,插件也公布一个标准接口 IPlugin,由主程序在与插件通信时调用。在这里,我们将要建立一个简单的程序叫做万能浏览器(universal Explorer)。这个浏览器可以用来查看任何有着层次结构的信息,如文件系统、数据库的主/从关系、组织结构图、家庭成员关系……等等。浏览器公布的宿主接口叫做IExplorer。
浏览器的插件模块知道如何在层次信息中浏览、导航。例如,我们可以建立一个文件系统插件来浏览我们电脑上的文件系统。每个万能浏览器的插件都要公布一个插件接口叫做IExplorable。
接口设计
我们希望每个插件都要能描述它自己并且还要有一个指向它的宿主程序的指针:
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -