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

自定义文件扩展名控件

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.83/5 (27投票s)

2010年3月30日

CPOL

7分钟阅读

viewsIcon

78002

downloadIcon

3458

轻松为您的应用程序分配扩展名并设置图标。

引言

CustomFileExtension 是一个帮助您为自定义文件类型定义文件扩展名的控件。此组件在窗体上不可见,而是在其下方。

什么是文件关联?

文件关联是一组注册表项,用于确定您的新文件类型将如何行为,它将具有什么图标以及将使用哪个应用程序运行它。没有 CustomFileExtension 控件也可以做到这一点,但这种方式比每次自己编写代码要简单快捷得多。

关键在于在正确的位置创建注册表项。

理论

这里有一些理论解释

HKEY_CLASSES_ROOT 中,您需要创建一个名为所需扩展名的键。例如 '.cp'(不带引号)。在新创建的键的数据字段中,输入 MyApplication,它将成为注册表中该扩展名的引用。

仍然在 HKEY_CLASSES_ROOT 中,添加一个名为 MyApplication 的引用键。在此键的数据字段中,您可以输入一些文本,这将是在资源管理器中该文件类型的描述。要拥有图标,我们需要在 MyApplication 键内添加一个名为 DefaultIcon 的子键。DefaultIcon 子键的数据字段将是您希望您的文件类型拥有的图标的路径。将图标路径括起来,如下所示:“icon_path”。您也可以拥有一个位于 Exe 或 DLL 文件资源中的图标,如下所示:

"application_path",x

其中 x 是图标在资源中的索引。

最后,我们需要指定应用程序的路径,以便应用程序能够打开您的文件类型。

MyApplication 键中,创建另一个名为 shell 的子键。在 shell 子键中,创建另一个名为 open 的子键。在 open 子键的数据字段中,输入当您右键单击具有 .cp 扩展名的文件时将在上下文菜单中显示的文本。

最后,在 open 子键中创建另一个名为 command 的子键。在 command 子键中,我们可以设置应用程序 .exe 的完整路径,以便文件类型与应用程序关联,如下所示:

"application_path" "%1"

%1 充当参数变量,因此如果我们双击一个具有 .cp 扩展名的文件,我们将把该单击文件的完整路径传递过去,以便我们可以在应用程序中打开它。

注册表应该看起来像这样

扩展名部分

custom-file-extension/ss101.png

处理程序部分

custom-file-extension/ss100.png

打开方式...

有一个选项可以为您的自定义格式指定一个打开它的应用程序。例如,您可以使用记事本编辑 .html 文件,但也可以在 Visual Studio 中编辑。使用此控件,您可以为自己的应用程序拥有相同的功能。

custom-file-extension/ss112.png

这背后的原理只是在扩展名键的 OpenWithProgids 子键下添加,并在 OpenWithProgids 子键下开始添加字符串值来引用不同的应用程序。

之后,您需要创建与每个处理程序匹配的键,与您的扩展名处于同一级别。每个键都必须有一个 shell 子键。然后,shell 必须有一个 open 子键,open 必须有一个 command 子键。在 command 子键中,您需要输入其他应用程序的完整路径,并在末尾加上 %1

当您使用 RegisterFileTypes() 方法时,该控件将为选定的扩展名分配关联,如果您选择了应用程序,OpenWithProgids 将被填充。

在同一应用程序实例中打开文件

您还可以让您的程序在已运行的应用程序中打开文件,而不是运行应用程序的新实例。进程之间进行通信,并通过 SendMessage Windows API 发送。

这次没有注册表操作。

添加了两个新方法

  • SendFilePath,它有一个 args 参数用于传递,并检查应用程序实例,准备文件路径,然后在应用程序正在运行时将文件路径发送到正在运行的应用程序。如果应用程序有多个实例,该方法返回 true
  • GetData 是另一个在接收端使用的方法。它接受一个类型为 string 的数据参数,并返回一个文件路径数组。

关于理论和阴谋论的就到这里。让我们看看代码。

源代码

源代码很简单。我们利用了 Registry 类。所以您需要添加一个 using 语句

using Microsoft.Win32;

关于代码。

Registry.SetValue("HKEY_CLASSES_ROOT\.cp", "", "MyApplication");
Registry.SetValue("HKEY_CLASSES_ROOT\MyApplication", "", "Some description");

Registry.ClassesRoot.CreateSubKey("DefaultIcon");
if (embeddedIcon)
    Registry.SetValue("HKEY_CLASSES_ROOT\MyApplication\DefaultIcon", 
                      "", "Application_Path", "Icon_Position");
else
    Registry.SetValue("HKEY_CLASSES_ROOT\MyApplication\DefaultIcon", "", "Icon_Path");

Registry.SetValue("HKEY_CLASSES_ROOT\MyApplication\shell", "", "");
Registry.SetValue("HKEY_CLASSES_ROOT\MyApplication\shell\open", "", "Open me");
Registry.SetValue("HKEY_CLASSES_ROOT\MyApplication\shell\open\command", 
                  "", "Application_Path" ""%1""");

执行此代码后,您需要通知资源管理器有关更改。我不确定 .NET 中是否有等效函数,但您可以使用 PInvoke 调用 SHChangeNotify 函数,该函数将通知资源管理器有关更改。您可以使用 DllImport 调用该函数,但首先,添加另一个 using 语句

using System.Runtime.InteropServices;

以及所需的枚举,这些枚举可以在 这里 [^] 和 这里 [^] 找到。

在添加关联后,我们所要做的就是删除关联。

Registry.ClassesRoot.DeleteSubKey(ext);
Registry.ClassesRoot.DeleteSubKeyTree(handler);

打开方式...

我们已经解释了此选项,但让我们看一下它的代码。

for (int i = 0; i < openWith.Count; i++)
{
    string app = openWith[i].AppPath;
    string originalApp = app;
    app = app.Substring(app.LastIndexOf("\\") + 1);
    app = app.Substring(0, app.LastIndexOf('.'));

    string runHandler = "CustomFileExtension." + app + ".Run.Handler";

    Registry.ClassesRoot.CreateSubKey(ext + "\\OpenWithProgids");
    Registry.SetValue(HKCR + ext + "\\OpenWithProgids", runHandler, "");

    // openwith handler
    Registry.SetValue(HKCR + runHandler, "", "");
                
    Registry.SetValue(HKCR + runHandler + "\\shell", "", "");
    Registry.SetValue(HKCR + runHandler + "\\shell\\open", "", "");
    Registry.SetValue(HKCR + runHandler + "\\shell\\open\\command", "", "\"" + 
                      originalApp + @""" ""%1""");
}

此代码位于 RegisterFileType() 方法中。当您注册一个新扩展名时,该应用程序将被添加到 打开方式 对话框中。

当您调用 RemoveFileType() 方法时,它将删除 打开方式 对话框使用的所有处理程序。

在同一应用程序实例中打开文件

SendFilePath 方法中,我们所要做的就是组合文件路径,列出当前正在运行的进程,然后发送消息。

Process proc = Process.GetCurrentProcess();
Process[] processes = Process.GetProcessesByName(proc.ProcessName);

if (processes.Length == 0)
    return false;

string message = "";

for (int i = 0; i < args.Length; i++)
    message += args[i] + ";";

byte[] data = Encoding.Default.GetBytes(message);
int dataLen = data.Length;

COPYDATASTRUCT cds;
cds.dwData = (IntPtr)100;
cds.lpData = message;
cds.cbData = dataLen + 1;

if (processes.Length > 1)
{
    foreach (Process p in processes)
    {
        if (p.Id != proc.Id)
        {
            SendMessage(p.MainWindowHandle, WM_COPYDATA, 0, ref cds);
            return true;
        }
    }
}

在您的应用程序中,您像这样使用此方法

CustomFileExtensionControl.CustomFileExtension ctf = 
         new CustomFileExtensionControl.CustomFileExtension();
if (!ctf.SendFilePath(args))
    Application.Run(new Form1(args));

因此,当应用程序已运行时,我们将参数传递给正在运行的应用程序。在您的应用程序中,您还需要添加一个 WndProc 事件,当控件发送消息时会调用该事件。

protected override void WndProc(ref Message message)
{
    if (message.Msg == WM_COPYDATA)
    {
        COPYDATASTRUCT mystr = (COPYDATASTRUCT)message.GetLParam(typeof(COPYDATASTRUCT));
        Type mytype = mystr.GetType();
        mystr = (COPYDATASTRUCT)message.GetLParam(mytype);
        
        string [] args = customFileExtension1.GetData(mystr.lpData);
        OpenFile(args);
    }

    base.WndProc(ref message);
}

我们在这里可以看到 GetData 方法的使用,它将使 OpenFile 函数能够读取文件。

感谢 scosta_FST 提出关于在运行的应用程序中打开文件的问题。

设置

设置此控件非常简单。将组件拖放到窗体上后,选择它并设置属性。这里解释了可用属性

这是最终的应用程序文件名及其扩展名(.exe)。例如 MyApplication.exe 必须与实际的应用程序名称匹配。

当您在“平铺”视图中选择列表视图时,这将在资源管理器中显示。它会显示描述,例如 CP's File。

如果设置为 true,则图标来自 .exe 文件内的资源,然后您还需要设置 IconPosition 来选择资源中的正确图标。

这是您的自定义文件类型的实际扩展名,将用于通过您的应用程序(ApplicationName)打开该文件。

这用于将注册表中的扩展名键与相应的键关联,以便通过您的应用程序打开扩展名文件。

ApplicationName 一样,IconName 是您的图标名称加上您希望文件类型拥有的扩展名(.ico)。例如 MyIcon.ico

这是您的图标在 Exe 的资源文件(或 .dll)中的位置。0 是第一个图标,1 是第二个,依此类推。

设置此属性可在右键单击自定义文件类型时通过打开上下文菜单显示自定义文本。这是默认操作,与双击文件相同。

这是出现在 打开方式 对话框中的应用程序文件路径集合。

  • 应用程序名称
  • 描述
  • EmbeddedIcon
  • Extension
  • Handler
  • IconName
  • IconPosition
  • OpenText
  • OpenWith

使用方法

使用此控件非常简单。要将组件添加到窗体,请右键单击工具栏并选择 选择项目...。将打开一个对话框,单击 浏览,找到 .dll 文件并单击“确定”。现在您可以将组件拖放到窗体上。选择控件并设置属性,然后您可以关联文件类型,使用 RegisterFileType() 函数,并使用 RemoveFileType() 删除关联。例如:customFileExtension.RegisterFileType()customFileExtension.RemoveFileType()

就是这样。

版本历史

  • 2010 年 3 月 26 日 - 初版。
  • 2010 年 4 月 8 日 - 添加了 打开方式 功能。
  • 2010 年 4 月 20 日 - 现在可以在运行的应用程序实例中打开文件。
© . All rights reserved.