命名空间扩展工具包






4.78/5 (9投票s)
本文将向您展示如何为自定义数据构建您自己的 Windows 资源管理器界面。
引言
Galaxy 文件系统工具包(GFT)允许开发人员创建用户级别的文件系统扩展,这些扩展可以通过 Windows 资源管理器查看。虽然工具包本身是使用 C++ COM/ATL 代码构建的,但扩展开发人员可以使用 C# 和 Java 等高级强类型、垃圾回收语言进行编程。因此,该工具包简化了 Windows 平台上用户级别文件系统的开发。GFT 已与 Java 文件系统扩展一起使用,以创建 NFS 版本 2 文件服务器的 Windows 资源管理器界面。其他可能的扩展示例包括:
- Google Gmail 的 Windows 资源管理器界面,功能类似于 Gmail 驱动器。Gmail 文件夹将被建模为文件系统文件夹,邮件消息将映射到文件。或者,文件中的块可以映射到单个消息,以允许更大的文件存储。
- SQL 数据库的 Windows 资源管理器界面。表可以建模为文件夹,行可以建模为文件夹中的文件。
- PlanetLab 的 Windows 资源管理器界面。文件夹将包含机器站点,文件将包含单个机器的属性,例如负载平均值。
- 使用 Windows 资源管理器的类似 /proc 的界面。"/proc" 文件夹中的文件将映射到进程,文件内容将显示进程属性。
1.1 这是什么?
该工具包是一个 Windows Shell 命名空间扩展 (NSE),它通过代理和存根对与您的自定义代码(扩展)进行通信。有关此视图,请参见图 1。该工具包管理 Shell 命名空间的一部分,例如 Windows 路径“C:\Custom”,并将此命名空间上的所有 Windows 资源管理器操作转换为对文件系统扩展的适当方法调用。例如,当文件拖放到文件夹“C:\Custom”时,工具包将首先调用 CreateFile
,然后对您的扩展进行一系列 Write 调用。
本文档的其余部分将介绍安装工具包和创建简单扩展所需的步骤。第 2 节介绍了安装工具包所需的步骤,而文档的大部分内容(第 3 节)将指导您创建 C# 中的示例扩展。第 4 节列出了已知问题和限制,第 5 节给出了致谢。
2 安装
首先,从网站下载工具包源代码或二进制安装程序包。C# 程序员将从源代码安装,因为源代码 zip 文件包含必要的接口和示例代码。Java 开发人员通常会使用二进制安装程序安装 GFT,因为此过程不需要 Visual Studio。
2.1 从源代码安装
GFT 可以通过 Visual Studio 解决方案文件和源代码安装,步骤如下:
- 下载:从 CodeProject 网站下载最新的 GalaxySource.zip 文件。
- 解压:解压源代码,并打开 Galaxy.sln Visual Studio 解决方案文件。
- 构建并启动:导航到 Visual Studio 中的“构建”菜单,然后选择“构建解决方案”。然后,导航到 Visual Studio 中的“调试”菜单,然后选择“启动”。这将启动 C# Mirror 文件系统(您应该会看到一个空白的命令提示符窗口)。现在,转到第 2.3 节。
2.2 从二进制文件安装
从二进制文件安装涉及以下步骤:
- 下载:从网站下载最新的 GalaxyBinary.zip 二进制安装程序。这是一个可以解压到临时位置的 Zip 文件。
- 运行 setup.exe 程序。打开 setup.exe 或 GalaxyToolkit.msi 安装文件。这将安装 C++ 命名空间扩展 (NSE) 和 C# 镜像文件系统。
- 启动镜像文件系统。导航到“开始”、“程序”、“Galaxy”,然后单击“启动 C# 镜像文件系统”链接。此步骤在较旧的 Windows 版本(例如 Win98)或未安装 .NET 可再发行组件的任何操作系统上将不起作用。从 Microsoft 网站获取 .NET 1.1 可再发行组件。
2.3 探索文件系统
此时,您应该已经安装了 Galaxy 命名空间扩展 (NSE),并且正在运行一个示例 C# 镜像文件系统扩展。
要探索此镜像文件系统,请启动 Windows 资源管理器进程(Windows 键 + E)并导航到“我的电脑”。您应该会注意到一个名为“Galaxy”的新系统文件夹。浏览到此文件夹应该会显示两个文件:COPYING.txt 和 Subfolder。您可以打开文本文件(GNU GPL),也可以打开 Subfolder 文件夹,以及打开 Subfolder 文件夹中包含的 JPG 文件。(这些文件是 Test 文件夹中文件的镜像,该文件夹是随源代码分发或随二进制文件安装的,具体取决于您的安装方法。)
您可以尝试在 Galaxy 文件系统之间复制文件,但请注意尚不支持多选。(换句话说,您可以复制整个文件夹,但不能同时高亮显示和复制两个或多个文件。)其他注意事项列在第 4 节中。
3 教程
要创建您自己的 C# 文件系统扩展,您需要执行两个步骤:
- 实现一个抽象类 CSharpFileSystem.FileSystem;
- 更改 Driver.cs 文件的第 50 行以指定您的扩展类名。
在本教程中,我们将创建一个名为“RandomFileSystem”的虚拟只读文件系统扩展,它将显示每个都填充有随机文本的随机文本文件。
首先,启动 Visual Studio(或您的个人 IDE)并打开 CSharpFileSystem 解决方案文件。现在,在 CSharpFileSystem
命名空间中创建一个新类,该类继承自 CSharpFileSystem.FileSystem
。我们将这个新类命名为“RandomFileSystem
”。为默认构造函数提供一个整数参数,用于指定每个文件夹要显示的文件数。
您的代码应如下所示:
using System;
using System.Diagnostics;
using System.IO;
namespace CSharpFileSystem f /// <summary>
/// Summary description for RandomFileSystem.
/// </summary>
public class RandomFileSystem : FileSystem {
int myNumFiles;
public RandomFileSystem(int i) {
myNumFiles=i;
}
}
}
现在,我们需要实现 FileSystem
抽象类的九个方法。在以下每个部分中,我们将实现一个方法并讨论实现细节。在我们不提供实现的方法中,我们仍然返回成功代码 true
;Galaxy 的错误处理尚不完善,因此返回 false
值很可能导致断言失败。
3.1 命令
此命令用于可扩展性,它允许命名空间扩展将任意命令字符串传递给您的文件系统。目前,唯一传递的命令是“trace”,当用户从上下文菜单(通过右键单击)选择“Trace”时,它会传递。我们可以安全地忽略它,因此我们给出一个虚拟实现:
public override bool Command(string command string) {
if (command string.ToLower().Equals("trace")) {
Debug.WriteLine("We should log our trace files to a log file here.");
return true;
} else {
return false;
}
3.2 CopyFile
此方法用于快速路径复制,以便 Galaxy 命名空间扩展在想要将文件复制到您的文件系统时可以传递文件名(而不是文件内容)。由于这是一个优化,我们将忽略此方法并提供以下调试实现(注意:您必须完全限定 FileInfo
类,因为 Galaxy 也定义了一个 FileInfo
类):
public override bool CopyFile(string windows src, string galaxy dst) {
System.IO.FileInfo fi = new System.IO.FileInfo(windows src);
Debug.WriteLine("The Galaxy NSE wants to copy a file of length"+
fi.Length+" to our filesystem");
return true;
}
3.3 CreateDirectory
由于我们正在实现一个随机只读文件系统,因此在我们的实现中,CreateDirectory
调用是一个空操作。
使用以下代码来实现此方法:
public override bool CreateDirectory(string path)
{
Debug.WriteLine("We would normally create a directory with the path"
+ path + " at this point");
return true;
}
3.4 CreateFile
创建文件命令也是如此,因此在这里使用以下代码:
public override bool CreateFile(string path)
{
Debug.WriteLine("We should create a 0¡length file with the name "
+ path + " here");
return true;
}
3.5 删除
同样,由于我们的 RandomFileSystem
是只读的,请使用以下代码进行删除:
public override bool Delete(string path) {
Debug.WriteLine("We are being asked to delete the file "+path);
return true;
}
3.6 ListFiles
在这里,我们需要创建一组随机文件并返回它们。我们将利用 myNumFiles
字段来帮助我们选择要返回的文件数量。我们选择每个奇数文件作为文本文件,而每个偶数文件作为文件夹(即目录)。每个文本文件都给定一个从 0 到 1023 字节的随机 FileSize
。此大小将显示在 Windows 资源管理器中,并由 Galaxy 使用:每当文件打开或复制时,只会读取前 FileSize
字节。
文件夹的大小为 0,每当在 Windows 资源管理器中打开文件夹时,都会在该路径名上调用此 ListFiles
方法。(注意:虽然我们填充了所有字段,但目前 Galaxy 只使用了 FileName
、FileSize
和 FolderFlag
。)
public override FileInfo[] ListFiles(string path) {
Random rnd = new Random();
int num files to return = rnd.Next(myNumFiles);
FileInfo[] ret = new FileInfo[num files to return];
for (int i=0;i<num files to return;i++) f ret[i] = new FileInfo();
ret[i].CreateTime = DateTime.Now; // not currently used
ret[i].LastAccessTime = DateTime.Now; // not currently used
ret[i].LastModifiedTime = DateTime.Now; // not currently used
ret[i].FilePath = "Empty path field"; // not currently used
ret[i].FolderFlag = (i%2 == 0); // even numbered files are folders
if (ret[i].FolderFlag) f ret[i].FileName = ""+i;
ret[i].Size = 0;
} else {
ret[i].Size = rnd.Next(1024); // files can be up to 1k in size
ret[i].FileName = i+".txt"; // non¡folders are text files
}
}
return ret;
}
3.7 读取
每当 Galaxy 中的文件复制到 Windows 或打开 Galaxy 文件时,都会调用 Read
方法。由于我们正在实现一个简单的只读随机文件系统,因此我们将为读取调用返回一个随机字节字符串(注意只返回指定数量的字节)。请注意,我们不会在调用之间保持文件大小一致,而是在此调用中简单地创建一个新的文件大小。此外,我们确保不返回超过 Windows 资源管理器请求的字节数 (count
),并且只有当文件中的偏移量等于 0 时才返回数据(即,通常是第一次读取调用)。
public override byte[] Read(string path, int offset, int count) {
Random rnd = new Random(); // files can be up to 1k in size
int max file size;
if (offset > 0) { // only return data the first time we are asked
max file size = 0;
} else { max file size = rnd.Next(1024);
}// only return up to ’count’ bytes
int file size = Math.Min(max file size,count);
byte[] ret = new byte[file size];
for (int i=0;i<ret.Length;i++) {
ret[i] = (byte)(’a’+ rnd.Next(26));
}
return (ret);
}
3.8 状态
每当文件从 Galaxy 复制到 Windows 时,Galaxy 都会调用 Stat
方法以获取文件信息。我们将解析路径名以确定是否正在请求包含字符串“.txt”的文件。如果包含,那么我们知道该路径指的是文件。否则,该路径指的是文件夹。(由于我们控制所有文件名,因此我们可以确定文件夹不包含字符串“.txt”)。
请注意,我们没有特别注意保持文件大小一致:此方法返回的文件大小是随机的,并且可能与 ListFiles
方法返回的文件大小不同。
public override FileInfo Stat(string path)
{
FileInfo fi = new FileInfo();
fi.CreateTime = DateTime.Now; // not currently used
fi.LastAccessTime = DateTime.Now; // not currently used
fi.LastModifiedTime = DateTime.Now; // not currently used
Random rnd = new Random();
// parse the path into file and directory parts
int index = path.LastIndexOf("/");
string filename = path.Substring(index+1);
string filepath = path.Substring(0,index);
if (filename.IndexOf(".txt")!=(¡1))
{
// this must be a file
fi.FolderFlag = false;
}
else
{
// this must be a folder
fi.FolderFlag = true;
}
fi.FileName = filename; // keep the same filename
fi.FilePath = filepath; // not currently used
if (fi.FolderFlag)
{
fi.Size = 0;
}
else
{
fi.Size = rnd.Next(1024); // files can be up to 1k in size
}
return (fi);
}
3.9 写入
由于我们正在实现一个只读文件系统,因此我们忽略此方法并提供一个简单的实现:
public override int Write(string path, int offset, int count, byte[] buffer) {
Debug.WriteLine("Explorer wants to write "+
count+" bytes into a file called "+
path+" at byte offset "+offset);
return count; // pretend that we wrote all of the bytes
}
随机文件系统的完整列表可在源 Zip 文件中找到。
3.10 更改驱动程序文件
现在,我们将修改驱动程序文件 Driver.cs 以创建我们的新文件系统扩展。更改行(大约第 50 行):
FileServer s = new FileServer(int.Parse(args[0]),
new FileSystems.Mirror.MirrorFileSystem(args[1]) );
to
FileServer s = new FileServer(int.Parse(args[0]),
new RandomFileSystem(int.Parse(args[1])));
请注意,我们将第二个命令行参数作为整数传递给 RandomFileSystem
构造函数。因此,现在我们将此命令行参数更改为适当的值。选择 CSharpFileSystem 项目,然后选择“属性”。将“配置属性”、“调试”、“启动选项”、“命令行参数”下的命令行参数从“8052 ../../Test”更改为“8052 10”。这应该允许我们的新随机文件系统中每个文件夹最多有 10 个文件。应用更改,然后单击“确定”。现在,构建代码并通过按“F5”启动我们的新随机文件系统。
使用 Windows 资源管理器导航到 Galaxy 文件系统,并验证随机文件系统是否正常工作。您可能会遇到看似奇怪的行为(零文件、文件数量不断变化、每个文件的内容变化),但这就是 RandomFileSystem
的工作方式!您可以从 RandomFileSystem
复制文件,但在复制文件夹时要小心:您很有可能超过 260 个字符的最大路径长度,因为任何文件夹的深度都是一个期望值无限制的随机变量。
3.11 下一步
下一步,您可以阅读源代码包中 FileSystems.MirrorFileSystem.cs 中包含的 MirrorFileSystem
文件系统。这是一个更完整的文件系统示例,它镜像了本地 Windows 文件系统的指定文件夹。请务必将 CSharpFileSystem 项目的命令行参数改回其原始形式。
由于对于 MirrorFileSystem
,第二个命令行参数指定要镜像的文件夹,您可以随意将其更改为您选择的任何文件夹。例如,如果您愿意,可以将命令行参数更改为“8052 C:\Windows”。(第一个参数 8052 指定文件服务器将监听的端口,并且必须与 Galaxy 命名空间扩展使用的端口匹配。)
4 已知问题
虽然已实现基本文件操作(例如,剪切和粘贴、拖放),但 NSE 存在一些限制,应在将来的版本中修复。Galaxy v1.0 的这些限制如下:
- 如果您的文件系统在扩展启动之前被访问,资源管理器将挂起。这是可以修复的,但只要您注意在导航到文件系统扩展之前启动它,您就应该没问题。如果您遇到此问题,只需终止 explorer.exe 进程并使用任务管理器重新启动它。
- 文件的图标有限。图标仅限于文件夹图标或文本文件图标。这将在未来的版本中修复。
- 目前不支持文件重命名。这是我目前优先修复的问题,应该在代码的下一个版本中进行更改。
- 从/到 Galaxy 命名空间复制时不允许进行多选。此限制应在将来的版本中放宽。
- 文件删除没有确认。当您在 Windows 文件系统中删除文件时,通常会使用“是/否”对话框来确认删除。这在 Galaxy 中尚未实现。
- 尚不支持 Galaxy 中文件/文件夹的属性。
- 文件/文件夹的属性仅限于文件名和文件大小。未来的版本将支持显示文件的最后修改时间、创建时间等其他属性。
- 当前最大文件和路径长度为 260 个字符。如果使用 Unicode 路径,则可以将其扩展到 65,000 个字符。
5 致谢
学习 COM 和 NSE 接口的任务因几个人的贡献而变得更加容易,我们在此向他们表示感谢。首先,Pascal Hurni、Nemanja Trifunovic、Henk Devos 和 Michael Dunn 在 CodeProject.com 网站上发表的文章对于我们快速掌握 Windows 环境中的命名空间扩展和 COM 编程非常有价值。
Pascal Hurni 在 CodeProject 网站上发布的 GPL 代码以及 Bjarke Viksoe 发布的 GPL Amiga 磁盘文件命名空间扩展构成了我们 GFT 工具包的基础。最后,经常访问新闻组 microsoft.public.platformsdk.shell 的所有用户,特别是 Jim Barry,对发现我们实现中的细微错误提供了帮助。