📄 插件实现.txt
字号:
话题726154的标题是: 文摘:插件结构的实现之编程篇 (0分)
分类:编程手记 吴下阿蒙 (2001-11-13 00:03:00)
http://www.ccidnet.com/html/tech/guide/2001/09/29/58_3373.html
(文:Binh Ly 安富国编译 2001年09月29日 15:29)
经过前文所述的注册过程后,下面让我们以Delphi为例,开始具体的编程工作。
文件系统插件的实现
在Delphi里,为了创建一个in-process (DLL) COM服务器工程,我们从菜单File|New的对话框的ActiveX页选择ActiveX Library,取名“FileSystemPlugin”,Delphi将会生成一个FileSystemPlugin.dll文件做为我们的插件服务器。
然后要建立我们的插件coclass。选择File|New ,从对话框ActiveX页上选COM Object,在新对话框里,为它取名“FileSystemExplorable”,新的模块以FileSystemExplorable.pas的名字保存。
用Delphi 4/5生成,要确保取消“Include Type Library复选框的选中状态。这是因为我们只是简单地建立一个轻量级的COM对象,而不需要任何其它东西进入类型库。
经过前面的步骤,Delphi产生了一个轻量级的COM对象:
type
TFileSystemExplorable = class (TComObject)
end;
const
Class_FileSystemExplorable: TGUID = '{8B9A0689-7434-11D3-A802-0000B4552A26}';
因为我们正在建立一个插件,我们希望FileSystemExplorable实现IExplorable插件接口。为此,我们为TFileSystemExplorable 手工加上IExplorable:
type
TFileSystemExplorable = class(TComObject, IExplorable)
protected
//IExplorable 方法
function SetExplorer(const Explorer: IExplorer): HResult; stdcall;
function GetDescription(out Description: WideString): HResult; stdcall;
function GetSubItems(const Path: WideString; out SubItems: ISubItems): HResult; stdcall;
function GetMenuActions(const Path: WideString; out Actions: OleVariant): HResult; stdcall;
function DoMenuAction(const Path: WideString; ActionId: Integer): HResult; stdcall;
function GetProperties(const Path: WideString; out Properties: OleVariant): HResult; stdcall;
protected
FExplorer : IExplorer;
end;
接下来是实现它们。以SetExplorer and GetDescription为例:
function TFileSystemExplorable.SetExplorer (const Explorer: IExplorer): HResult;
begin
FExplorer := Explorer; //FExplorer是TFileSystemExplorable的成员
Result := S_OK;
end;
function TFileSystemExplorable.GetDescription (out Description: WideString): HResult;
begin
Description := 'File System (Delphi version)';
Result := S_OK;
end;
一点也不复杂,是不是?下面看看GetSubItems。
记住,GetSubItems将由万能浏览器调用以取得任意结点的子项(由ISubItems接口枚举)。这意味着我们要用函数FindFirst和FindNext扫描文件夹(由node/path指定)。我们把这些操作封装进TSubItems类:
type
TSubItems = class (TInterfacedObject)
protected
FSubItems : TStringList;
procedure LoadSubItems (const Path : string);
procedure LoadDrives;
procedure LoadFiles (Path : string);
public
constructor Create (const Path : string);
end;
constructor TSubItems.Create(const Path: string);
begin
inherited Create;
FSubItems := TStringList.Create;
LoadSubItems (Path); // 把所有Path下的文件和文件夹放入FSubItems
end;
... 以下部分略 ...
//根据给定的路径Path装入文件系统的一个分枝
procedure TSubItems.LoadSubItems(const Path: string);
begin
//reset list
FSubItems.Clear;
//如果Path是根,那么装入所有驱动器否则把Path做为文件夹装入
if (Path = '') then
LoadDrives
else
LoadFiles (Path);
end;
//把系统驱动器装入FSubItems列表
procedure TSubItems.LoadDrives;
begin
//以下是伪码实现
Find all drives;
For each drive found
Add drive name into FSubItems list;
end;
//把文件夹中的文件及子文件夹装入FSubItems列表
procedure TSubItems.LoadFiles(Path: string);
begin
//伪码实现
Find all files (and folders) under Path (using FindFirst/FindNext)
For each file (and folder) found
Add file name into FSubItems list;
end;
这里最重要的方法是LoadSubItems,如果Path为空,那么我们装入您系统中的所有驱动器,否则只需使用FindFirst与FindNext找齐指定路径下的所有文件。
由于FileSystemExplorable必须把ISubItems 交给万能浏览器,我们简单地TSubItems里实现ISubItems:
type
TSubItems = class (TInterfacedObject, ISubItems)
protected
//ISubItems方法
function GetCount(out Count: Integer): HResult; stdcall;
function GetItem(Index: Integer; out Item: WideString): HResult; stdcall;
protected
FSubItems : TStringList;
...
end;
//返回子项个数
function TSubItems.GetCount(out Count: Integer): HResult;
begin
Count := FSubItems.Count;
Result := S_OK;
end;
//返回对应Index的一个子项
function TSubItems.GetItem(Index: Integer; out Item: WideString): HResult;
begin
Item := FSubItems [Index];
Result := S_OK;
end;
最后,我们回到TFileSystemExplorable并实现GetSubItems:
function TFileSystemExplorable.GetSubItems(const Path: WideString;
out SubItems: ISubItems): HResult;
begin
//要求TSubItems返回一个指定path的子项列表
SubItems := TSubItems.Create (Path);
Result := S_OK;
end;
下面实现GetProperties。如果您还记得,万能浏览器调用GetProperties来得到任意结点的以名/值对表达的结点信息。在COM中,我们只需使用可变数组即可实现:
function TFileSystemExplorable.GetProperties(const Path: WideString;
out Properties: OleVariant): HResult;
begin
Result := S_OK;
//Properties 是一个二维数组:
//
// | Property Name 1 | Property Value 1 |
// | Property Name 2 | Property Value 2 |
//
Properties := VarArrayCreate ([
0, 4, //行范围
0, 1 //列范围
],
varOleStr //数组成员是字符串
);
//类型(Type): 文件还是文件夹?
Properties [0, 0] := 'Type';
if IsFolder (Path) then
Properties [0, 1] := 'Folder' // 文件夹
else
Properties [0, 1] := 'File'; // 文件
//文件名
Properties [1, 0] := 'Name';
Properties [1, 1] := NameOfFile (Path);
//文件大小
Properties [2, 0] := 'Size';
Properties [2, 1] := IntToStr (SizeOfFile (Path));
//文件日期
Properties [3, 0] := 'Date/Time';
Properties [3, 1] := DateTimeToStr (DateTimeOfFile (Path));
//文件属性
Properties [4, 0] := 'Attributes';
Properties [4, 1] := AttributesOfFile (Path);
end;
我们做的只是简单地返回一个二维可变数组(从0到4共有5行)包含下面的文件属性:文件类型、文件名、文件大小、文件日期及文件属性。具体细节请见源代码。
最后还应实现的是GetMenuActions和DoMenuAction。(由于篇幅所限,这里不再介绍,详见源码。)
这两个方法可以在您的层次结构中的任意结点上实现上下文相关的自定义的操作。GetMenuActions返回给万能浏览器一个包含“动作-ID”值的数组,DoMenuAction执行用户选定的动作(Action)。
我们这里仅仅实现一个动作-文件改名,为安全起见当文件除“存档位”之外的其它属性位有值时不允许文件改名操作:
const ACTION_RENAME = 1;
function TFileSystemExplorable.GetMenuActions(const Path: WideString;
out Actions: OleVariant): HResult;
var
ActionCount : integer;
begin
Result := S_OK;
//ActionCount展示了怎样根据上下文动态生成菜单项
ActionCount := 0;
//菜单动作(Actions)是一个二维数组
//
// | 动作名1 | 动作ID1 |
// | 动作名2 | 动作ID2 |
//
Actions := VarArrayCreate ([
0, 0, //行范围
0, 1 //列范围
],
varVariant //可变的元素
);
//改名
//文件只有存档位被置位时才允许改名
if (FileOnlyHasArchiveAttributeSet (Path)) then
begin
//增加改名动作(Rename Action)
Actions [ActionCount, 0] := 'Rename';
Actions [ActionCount, 1] := ACTION_RENAME; //这是一个整型常量,值为1
//one action in!
inc (ActionCount);
end;
//这里您可以自行定义更多内容...
//如果Actions中没有内容,清除它
if (ActionCount <= 0) then Actions := Unassigned;
end;
我们简单地建立了一个二维数组保存“动作-ID”值。由于我们只实现了一个改名动作,所以数组中只包含了一行内容(行范围是0到0)。
每当万能浏览器得到它的动作列表,它会产生相应的上下文相关的弹出菜单。随后,如果用户选择了“改名”动作,万能浏览器会通过给插件传递一个ACTION_RENAME常量来调用插件中的DoMenuAction动作。下面即是插件中实现DoMenuAction动作完成改名的部分:
function TFileSystemExplorable.DoMenuAction(const Path: WideString; ActionId: Integer): HResult;
var
NewName : string;
begin
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -