【问题标题】:Associate File Type and Icon关联文件类型和图标
【发布时间】:2009-12-14 21:43:34
【问题描述】:

好的,我已经查看了所有内容,但我很难弄清楚如何将自定义文件类型与我的 C# 应用程序相关联。我正在使用 Visual Studio 2008,并且由于公司部署而仅限于 .NET 2.0。

基本上,我真正想做的就是能够允许用户双击我的自定义文件类型或将其拖放到应用程序图标上并加载数据,以及在文件本身上显示自定义图标.我可以通过对话框打开文件,一切正常。

尝试阅读 Micosoft 的在线文档很难理解,并且无法真正提供我需要的所有详细信息。如果有人知道我在哪里可以找到一个好的教程或可以解释它,我将不胜感激。

谢谢 帕特里克

【问题讨论】:

  • 对限制感到遗憾; 3.5 SP1 中的 ClickOnce 内置了这个...
  • 这已在以下stack overflow thread 中得到彻底解答。我们一直在使用这个实现并且效果很好。它也是开源的并集成到 MSBuild 中。

标签: c# windows file-association


【解决方案1】:

几年前我在新闻组中wrote this up。浏览了一下,发现不是我写过的最清晰的,但还算完整。对于它的价值,我在下面转载了它:


首先,您需要在HKEY_CLASSES_ROOT 下创建一个子项,用于保存启动应用程序的命令。您应该将此键命名为半描述性的。您的文件扩展名[s] 将映射到此键。例如,默认情况下,TXT 文件映射到名为 txtfile 的键。使用此设置的好处是,如果您的应用程序可以处理多个扩展程序,您可以将它们全部映射到此键。例如,许多 图像编辑应用程序创建一个名为“imagefile”之类的子键,并将 .bmp、.jpg、.gif 等映射到该键。我们将把您的密钥称为“JoeBlowFile”。接下来,您需要将新的 JoeBlowFile 键的“默认”值设置为向用户描述他们拥有的文件类型的文本字符串。这是 Windows 资源管理器中“类型”下显示的内容。同样,以 TXT 文件为例,这里的一个好的字符串应该是“文本文件”或“文本文档”。 (默认情况下是后者。)您的字符串可能是“Joe Blow Data”。

现在,在您的新密钥下,您可以创建另一个名为“DefaultIcon”的子密钥。顾名思义,这设置了与这种类型的文件一起使用的图标。您应该创建一个自定义图标,以图形方式表示您的程序处理的文档。您可以将此图标保存为应用程序目录中的 ICO 文件,但更好的是,您可以将其作为资源包含在您的 EXE 或 DLL 中。无论哪种方式,您都需要将子项的默认值设置为表示 ICO、EXE 或 DLL 的完整路径和文件名的字符串。如果文件中有多个图标(特别是如果您将其作为资源包含在 EXE 或 DLL 中),您还需要添加一个逗号,以及一个从零开始的正索引号,表示您使用的图标'd like to use, or a negative resource ID, using any ID of any ID of you have assigned your icon in your resource script.所以你的可能是,例如“C:\Program Files\JoeBlow\JoeBlow.exe, 2”。

上一段的 C# 开发人员注意事项。不幸的是,C# 项目不能有资源脚本。通过将资源添加到项目并将它们指定为“嵌入式资源”而添加到 .NET 应用程序的资源包含在与以前的方法不兼容的新的 .NET 特定格式中。您可以在 VS.NET 上使用 C# 正确嵌入应用程序的唯一图标是应用程序图标,可从项目属性访问。如果您需要其他图标 - 例如一个 图标来表示由您的应用程序处理的文档文件,而不是表示应用程序本身 - 您需要包含 ICO 文件本身,使用嵌入图标的 C++ 编译 DLL,或者创建和编译资源脚本并包含它在项目属性中的项目中。

无论您是否选择使用DefaultIcon 键,您现在都需要在您的JoeBlowFile 键下创建一个名为“shell”的子键。在shell 键下,您将为您希望用户能够从上下文菜单(右键单击菜单)对您的文件类型执行的每个命令创建单独的键。这些项目被称为“动词”。为了保持一致性,其中一个应该是“打开的”——如果存在,这个键将是默认的(即当用户双击您类型的文件时,将执行打开命令)。相反,您可以将“shell”键的默认值设置为您希望默认执行的动词。您可以选择将每个动词键的默认值设置为您希望在用户右键单击您的类型的文件时出现在上下文菜单中的文本。在此文本中可以使用与号 (&) 来指定后面的字符将被加下划线,这意味着用户可以按下与该字符对应的键来从上下文菜单中选择该命令。例如,对于“open”键,您可以将“Open with &Joe Blow's app”作为默认值。然后,带有下划线 J 的文本将出现在该类型文件的上下文菜单中,用户可以按字母 J 来启动 Joe Blow 的应用程序。

必须唯一要做的就是使用“打开”(和后续)键创建 另一个键作为该键的子键,称为“命令”。命令键的默认值必须设置为一个字符串,该字符串仅表示执行该操作所需的命令。例如,“open”键下的命令键中的默认字符串可能是“C:\Program Files\JoeBlow\JoeBlow.exe”“%1”。请注意应用程序的路径\文件名和%1 周围的引号。如果路径或文件名中有任何空格,则它们仅在应用程序的路径\文件名周围是必需的,但对于 32 位应用程序,它们绝对是 %1 周围的要求。 %1 就像旧的 MS-DOS 批处理 (.bat) 文件中的 %1%1 被命令行上的第一个参数替换,在这种情况下,它成为你的应用程序应该打开的文件的文件名。因为您事先不知道路径或文件名是否包含您应该 open 将包含空格,您必须在%1 周围加上引号。

您的应用程序的其他必需参数也应包括在内。例如,“命令”键中的默认值,在“打印”键下可能是“C:\Program Files\JoeBlow\JoeBlow.exe”“%1”/print”或“”C:\Program文件\JoeBlow\JoeBlow.exe”/打印“%1”。如何在应用中处理命令行参数由您决定。

关于可替换参数的注释,如上面提到的“%1”。显然,“%1”参数/可能/被替换为要打开的文件名。情况并非总是如此,而且我还没有弄清楚 Windows 使用什么标准来确定它会通过什么——短期还是长期。可能是如果注册表中列出的可执行路径是长文件名,Windows会用长文件名替换%1来启动,但是如果可执行路径是短文件名,或者可以解释为1,Windows会替换@ 987654335@ 带有短文件名。如果您想确保始终获得 long 文件名,请改用“%L”。您可以使用大写 L(就像我所做的那样)或小写 L,但我更喜欢使用大写,因为小写“l”看起来太像数字“1”了。

更重要的是,如果您的程序知道如何处理 Shell 项目 ID,您可以得到 that 而不是带有“%i”参数的长文件名。同样,大写或小写“i”同样适用,但我发现大写“I”更难与小写“l”和数字“1”区分开来。如果您不知道 Shell Item ID 是什么,没关系。您可能永远不需要使用一个。

您终于完成了JoeBlowFile 键。剩下的就比较简单了。您只需在 HKEY_CLASSES_ROOT 下创建(如果尚不存在)另一个子项,并将其命名为与文档类型的扩展名相同。要使用 txtfile 示例,名称应为“.txt”(带点,但不带引号)。您的(Joe Blow 的)可能是“.jbf”(对于 Joe Blow 文件)。现在必须将此键的默认值设置为您创建的第一个键的名称,在我们使用的示例中为“JoeBlowFile”。

就是这样。你在注册表中完成了。请记住,您必须确保您的应用程序 以与您在“shell”键下设置的命令一致的方式处理命令行。当您的应用启动时,Window 不会自动为您打开该文件……您必须这样做。

从图形上看是这样的:

HKEY_CLASSES_ROOT | +--.jbf = JoeBlowFile | +--JoeBlowFile = 乔吹数据 | +--DefaultIcon = C:\Program Files\JoeBlow\JoeBlow.exe, 2 | +--壳牌 | +--open = 使用 &Joe Blow 的应用打开 | | | +--command = "C:\Program Files\JoeBlow\JoeBlow.exe" "%1" | +--打印 | +--command = "C:\Program Files\JoeBlow\JoeBlow.exe" "%1" /print

如果您还不知道如何修改注册表,请在 MSDN 中查找所有功能 以“Reg”开头,包括 RegOpenKeyEx、RegCreateKeyEx 和 RegSetValueEx。您也可以通过创建“.reg”文件并简单地使用 ShellExecuteEx() 在其上调用“regedit.exe /s”来实现这一点。 (/s 使 Regedit 不会弹出消息框,询问“您确定要将 [name of file.reg] 中的信息添加到注册表吗?”) REG 文件的格式简单明了.这是一个示例 REG 文件,用于从上面添加“JoeBlow”示例:

REGEDIT4 [HKEY_CLASSES_ROOT\.jbf] @="JoeBlowFile" [HKEY_CLASSES_ROOT\JoeBlowFile] @="乔吹数据" [HKEY_CLASSES_ROOT\JoeBlowFile\DefaultIcon] @="C:\\Program Files\\JoeBlow\\JoeBlow.exe, 2" [HKEY_CLASSES_ROOT\JoeBlowFile\Shell] [HKEY_CLASSES_ROOT\JoeBlowFile\Shell\open] @="使用 &Joe Blow 的应用打开" [HKEY_CLASSES_ROOT\JoeBlowFile\Shell\open\command] @="\"C:\\Program Files\\JoeBlow\\JoeBlow.exe\" \"%1\"" [HKEY_CLASSES_ROOT\JoeBlowFile\Shell\print] @="&打印" [HKEY_CLASSES_ROOT\JoeBlowFile\Shell\print\command] @="\"C:\\Program Files\\JoeBlow\\JoeBlow.exe \"%1\" /print"

确保将“REGEDIT4”作为文件的第一行,否则它将不起作用。还 确保在最后一行按回车键,否则不会读入该行。总而言之,以这种方式将程序添加到注册表并不像听起来那么方便,因为如果您的应用程序安装在任何位置除了到 C:\Program Files\JoeBlow。

以上说明针对的是使用 C 或直接对 Win32 API 进行编程的用户 C++。对于 .NET 上的 C#,它要容易一些。查看 Registry 类,或者您甚至可以使用 VS.NET 中的部署项目以图形方式完成大部分工作。


要将本机可访问的资源添加到 .NET 程序集,您将需要一个资源脚本。资源脚本是纯文本文件,类似于代码文件。其实就是代码;由资源编译器 rc.exe 编译的声明性代码。示例资源脚本如下:

#include <windows.h>

#define IDI_APP    100
#define IDI_FILE   200
#define ID_VERSION   1

IDI_APP  ICON "Resources\\Application.ico"
IDI_FILE ICON "Resources\\JowBlowFile.ico"

ID_VERSION VERSIONINFO
    FILEVERSION 1, 0, 19, 186     // change this to your version
    PRODUCTVERSION 1, 0, 19, 186  // change this to your version
    FILEOS VOS__WINDOWS32
    FILETYPE VFT_APP {
        BLOCK "StringFileInfo" {
            BLOCK "040904B0" { // 0x409 = U.S. English, 0x04B0 = dec 1200 = Unicode
                VALUE "CompanyName",      "Joe Blow, Inc.\0"
                VALUE "FileDescription",  "Joe Blow's App\0"
                VALUE "FileVersion",      "1.0.19.186\0" // change this to your version
                VALUE "InternalName",     "JoeBlow\0"
                VALUE "LegalCopyright",   "Copyright © 2008-2009 Joe Blow Incorporated\0"
                VALUE "OriginalFilename", "JoeBlow.exe\0"
                VALUE "ProductName",      "Joe Blow\0"
                VALUE "ProductVersion",   "1.0.19.189\0" // change this to your version
            }
        }
        BLOCK "VarFileInfo" {
            VALUE "Translation", 0x409 /*U.S. English*/, 1200 /*Unicode*/
        }
    }

这样做的最大缺点是您必须手动将版本信息添加到资源脚本中(除非您想完全放弃版本信息)。在我的应用程序中,我添加了一个自定义构建步骤,该步骤运行我编写的程序,该程序直接在可执行文件中更新版本信息,这样我就不必手动更新资源脚本中的版本号,但该程序不适合公开发布,否则超出本文的范围。

现在您需要调用资源编译器将此资源脚本构建为二进制资源文件。将此脚本另存为 JoeBlow.rc,然后启动 Visual Studio 命令提示符。它位于 Visual Studio 开始菜单文件夹中的工具下。如果您没有安装 Visual Studio,我相信您会将 rc.exe 作为 SDK 的一部分。微软似乎也在提供最新版本here

在 VS cmd 提示符下(或者路径中有 rc.exe),只需键入:

rc JoeBlow.rc

就这么简单。鉴于我包含的图标存在,上述资源脚本应该可以正确编译。这将在同一目录中创建一个名为 JoeBlow.res 的新文件。现在,假设您正在使用 Visual Studio,您所要做的就是编辑项目属性以包含此资源文件。

这些说明适用于 Visual Studio 2005 或 2008。我不记得在旧版本中如何做到这一点,或者即使你可以做到,我还没有尝试过 2010,但它可能类似。在解决方案资源管理器中右键单击项目并选择属性(或从主菜单栏上的项目菜单中选择属性)。在 Application 选项卡上,这是您应该看到的第一个选项卡,底部是一个 Resources 组框。在这里,您有两个选项:“图标和清单”或“资源文件”。选择后一个选项。这将启用文本框,您可以在其中键入(或浏览到)新资源文件 JoeBlow.res。

最后,只需构建您的项目,然后,当您浏览与您的应用关联的文件时,您已经嵌入了原生 PE 格式的图标,shell 可以访问这些图标。现在,如果您将HKEY_CLASSES_ROOT\JoeBlowFile\DefaultIcon 的值设置为C:\Program Files\JoeBlow\JoeBlow.exe,1(使用从零开始的索引号)或C:\Program Files\JoeBlow\JoeBlow.exe,-200(使用资源标识符的负数,上面的#defined 为IDI_FILE),您的图标将在资源管理器中显示 .jbf 文件。

要让您的新图标在安装后立即显示,您可能需要刷新 shell 的图标缓存。我找到了关于如何做到这一点的说明here。基本要点是将外壳图标大小(HKEY_CURRENT_USER\Control Panel\Desktop\WindowMetrics)从其当前值更改为不同的值,然后再变回来,在每次更改后广播 WM_SETTINGCHANGE 消息。

祝你好运。如果您还需要什么,请告诉我。

【讨论】:

  • 您好,谢谢您,它让我更好地了解了它是如何保存在注册表中的。我现在唯一遇到的问题是试图让图标工作。
  • 图标有什么问题?你是如何提供图标的?您是否将其保存在单独的文件 (.ico) 中,或者您是否尝试将其编译到您的 .exe 或 .dll 中?请参阅我在此答案中关于在 C# 程序集中包含本机资源的注释。您必须创建一个资源脚本 (.rc) 并使用资源编译器对其进行编译(启动 VS cmd 提示符,并使用 rc.exe),然后在项目的 Application 选项卡中将生成的 .res 文件设置为应用程序的资源文件特性。我会尝试用更好的细节更新我的答案。
  • 我已经将它包含在项目的资源中,我只是不知道它捆绑在哪里,我假设在清单文件之一中,但我不确定。我有一个类可以在应用加载时链接文件类型,它只是我无法显示的图标。
  • 您“将其包含在项目的资源中”......以我描述的方式,还是以正常的 .NET 方式?
  • 在 VisualStudio 2008 > 项目 > 属性 > 资源 > 图标 >
【解决方案2】:

这个CodeProject article 有一些源代码,通过代码演示文件关联。

【讨论】:

  • 我找到了这个mentalis.org/soft/class.qpx?id=5 并将其用作模板,现在我只需要弄清楚如何让图标显示出来,并将文件加载到应用程序的实例中(如果是)已经在运行了。
  • @patrickst1 - 以这种方式运行应用程序的单个实例本身就值得另一个问题。默认的 Windows 行为是加载应用程序的新副本,因此您需要进行一些洗牌。
  • 非常感谢。所以除了图标,我什么都可以工作,你在哪里?资源文件夹中的唯一文件(在调试阶段不存在,但那是另一回事)是主应用程序图标。我需要知道我的自定义图标的存储位置。
  • 图标(通常)嵌入在您的可执行文件中。因此,您必须指定可执行文件的路径和名称以及图标的索引。如果有多个嵌入图标,则该索引用于选择一个嵌入图标。如果你只有一个,那就是零。你可以尝试类似的东西。 myProgramAssociationInfo.SetDefaultIcon(new ProgramIcon(new Uri(Assembly.GetEntryAssembly().CodeBase).AbsolutePath))
【解决方案3】:

Here 讨论了如何使用命令行实用程序assocftype 来执行此操作。您可以在程序部署期间(或在应用程序运行时)调用命令 shell 来执行此操作。

【讨论】:

  • 嗨,谢谢,这让我更好地了解了它在系统中的工作原理,但不确定如何在安装程序中运行 shell 命令。
  • @patrickst1 - 这也值得另一个问题。说出您正在使用的安装程序。
  • 只是使用 VisualStudio 2008 创建的默认值
  • ftypeassoc 很适合分配关联,但似乎没有任何图标。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-08-11
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多