65.9K
CodeProject 正在变化。 阅读更多。
Home

命名空间扩展工具包

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.78/5 (9投票s)

2006年3月21日

CPOL

12分钟阅读

viewsIcon

88189

downloadIcon

1727

本文将向您展示如何为自定义数据构建您自己的 Windows 资源管理器界面。

Sample image

引言

Galaxy 文件系统工具包(GFT)允许开发人员创建用户级别的文件系统扩展,这些扩展可以通过 Windows 资源管理器查看。虽然工具包本身是使用 C++ COM/ATL 代码构建的,但扩展开发人员可以使用 C# 和 Java 等高级强类型、垃圾回收语言进行编程。因此,该工具包简化了 Windows 平台上用户级别文件系统的开发。GFT 已与 Java 文件系统扩展一起使用,以创建 NFS 版本 2 文件服务器的 Windows 资源管理器界面。其他可能的扩展示例包括:

  1. Google Gmail 的 Windows 资源管理器界面,功能类似于 Gmail 驱动器。Gmail 文件夹将被建模为文件系统文件夹,邮件消息将映射到文件。或者,文件中的块可以映射到单个消息,以允许更大的文件存储。
  2. SQL 数据库的 Windows 资源管理器界面。表可以建模为文件夹,行可以建模为文件夹中的文件。
  3. PlanetLab 的 Windows 资源管理器界面。文件夹将包含机器站点,文件将包含单个机器的属性,例如负载平均值。
  4. 使用 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 解决方案文件和源代码安装,步骤如下:

  1. 下载:从 CodeProject 网站下载最新的 GalaxySource.zip 文件。
  2. 解压:解压源代码,并打开 Galaxy.sln Visual Studio 解决方案文件。
  3. 构建并启动:导航到 Visual Studio 中的“构建”菜单,然后选择“构建解决方案”。然后,导航到 Visual Studio 中的“调试”菜单,然后选择“启动”。这将启动 C# Mirror 文件系统(您应该会看到一个空白的命令提示符窗口)。现在,转到第 2.3 节。

2.2 从二进制文件安装

从二进制文件安装涉及以下步骤:

  1. 下载:从网站下载最新的 GalaxyBinary.zip 二进制安装程序。这是一个可以解压到临时位置的 Zip 文件。
  2. 运行 setup.exe 程序。打开 setup.exeGalaxyToolkit.msi 安装文件。这将安装 C++ 命名空间扩展 (NSE) 和 C# 镜像文件系统。
  3. 启动镜像文件系统。导航到“开始”、“程序”、“Galaxy”,然后单击“启动 C# 镜像文件系统”链接。此步骤在较旧的 Windows 版本(例如 Win98)或未安装 .NET 可再发行组件的任何操作系统上将不起作用。从 Microsoft 网站获取 .NET 1.1 可再发行组件。

2.3 探索文件系统

此时,您应该已经安装了 Galaxy 命名空间扩展 (NSE),并且正在运行一个示例 C# 镜像文件系统扩展。

要探索此镜像文件系统,请启动 Windows 资源管理器进程(Windows 键 + E)并导航到“我的电脑”。您应该会注意到一个名为“Galaxy”的新系统文件夹。浏览到此文件夹应该会显示两个文件:COPYING.txtSubfolder。您可以打开文本文件(GNU GPL),也可以打开 Subfolder 文件夹,以及打开 Subfolder 文件夹中包含的 JPG 文件。(这些文件是 Test 文件夹中文件的镜像,该文件夹是随源代码分发或随二进制文件安装的,具体取决于您的安装方法。)

您可以尝试在 Galaxy 文件系统之间复制文件,但请注意尚不支持多选。(换句话说,您可以复制整个文件夹,但不能同时高亮显示和复制两个或多个文件。)其他注意事项列在第 4 节中。

3 教程

要创建您自己的 C# 文件系统扩展,您需要执行两个步骤:

  1. 实现一个抽象类 CSharpFileSystem.FileSystem;
  2. 更改 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 只使用了 FileNameFileSizeFolderFlag。)

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 的这些限制如下:

  1. 如果您的文件系统在扩展启动之前被访问,资源管理器将挂起。这是可以修复的,但只要您注意在导航到文件系统扩展之前启动它,您就应该没问题。如果您遇到此问题,只需终止 explorer.exe 进程并使用任务管理器重新启动它。
  2. 文件的图标有限。图标仅限于文件夹图标或文本文件图标。这将在未来的版本中修复。
  3. 目前不支持文件重命名。这是我目前优先修复的问题,应该在代码的下一个版本中进行更改。
  4. 从/到 Galaxy 命名空间复制时不允许进行多选。此限制应在将来的版本中放宽。
  5. 文件删除没有确认。当您在 Windows 文件系统中删除文件时,通常会使用“是/否”对话框来确认删除。这在 Galaxy 中尚未实现。
  6. 尚不支持 Galaxy 中文件/文件夹的属性。
  7. 文件/文件夹的属性仅限于文件名和文件大小。未来的版本将支持显示文件的最后修改时间、创建时间等其他属性。
  8. 当前最大文件和路径长度为 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,对发现我们实现中的细微错误提供了帮助。

© . All rights reserved.