C# Tips之程序关联文件

写在前面

C# Tips是博主开启的第一个项目,以C#为示例语言,旨在普及编程技巧、经典算法以及计算机视觉、图形学知识。

关联文件的意义

想必各位同学在双击打开非txt的文本文件时通常会遇到这样的困扰:
无法直接打开文件
因为以.cue为后缀的文件并未与电脑中的任何程序关联,所以无法直接打开文件,选择一个能够打开文件的软件其实就是将cue文件与之进行关联。好处显而易见,就是凡是cue文件都可以通过指定软件直接双击打开。当然除此之外,文件如果还带有特定的图标,那么更能提升文件的逼格。

修改注册表

文件的关联信息存储在注册表(Win+R再输入regedit即可打开注册表)中,无论是修改默认程序还是指定程序,其本质都是修改了注册表信息。自定义文件关联信息通常存储在HKEY_CLASSES_ROOT结点下:

  1. 第一项为*,其含义为通用的关联方式,换句话说就是可以利用一个软件打开所有文件;
  2. 其他项则是特定后缀文件的关联方式。

通用关联

对于第一种情况,以Sublime Text软件(可以正常打开所有纯文本格式文件)为例,需要实现如下图所示的功能,即右键cue文件,然后点击Open With Sublime Text,利用Sublime Text打开cue文件。
在这里插入图片描述
具体修改注册表的方法为:在*结点下的shell结点中添加结点Sublime Text,然后修改默认项数据为Open With Sublime Text,然后在Sublime Text结点下添加结点command,修改默认项数据为"D:\Sublime Text\sublime_text.exe"<“Sublime Text程序所在路径”> “%1”。

特定关联

对于第二种情况,最普遍的需求是将自己编写的文件格式关联到自己编写的程序上。拿博主之前写过的一个三维重建系统MeasureMan举例,该系统的工程文件是以.msm为后缀的自定义文本文件,如下图所示msm文件与程序图标相同,且通过双击可以直接打开msm文件。
在这里插入图片描述
在这里插入图片描述
具体修改注册表的方法为:在HKEY_CLASSES_ROOT结点下添加.msm/Shell/Open/Command,然后在.msm结点中添加File Type项并填入数据msm File,最后修改Command结点的默认项数据为<“MeasureMan程序所在路径”> “%1”。

程序关联文件代码

.NET提供了Registry、RegistryKey等类可以很方便地对注册表进行操作,如下代码实现了在HKEY_CLASSES_ROOT结点下添加结点的过程,注意添加或修改注册表项必须先获得管理员权限:如果结点不存在直接添加,关联当前程序路径;如果结点存在但当前程序路径与已有路径不同,则替换程序路径。

try
{
    RegistryKey mainkey = Registry.ClassesRoot.OpenSubKey(@".msm\Shell\Open\Command",false);
    string value="\"" + Application.StartupPath + "\\MeasureMan.exe\"" + " \"%1\"";
    if (mainkey == null)
    {
        RegistryKey key = Registry.ClassesRoot.CreateSubKey(".msm");
        key.SetValue("File Type", "msm File");
        key.CreateSubKey(@"Shell\Open\Command").SetValue("",value);
        SHChangeNotify(HChangeNotifyEventID.SHCNE_ASSOCCHANGED, HChangeNotifyFlags.SHCNF_IDLIST | HChangeNotifyFlags.SHCNF_FLUSH, IntPtr.Zero, IntPtr.Zero);
    }
    else if (!value.Equals(mainkey.GetValue("").ToString()))
    {
        RegistryKey anotherKey = Registry.ClassesRoot.OpenSubKey(@".msm\Shell\Open\Command", true);
        anotherKey.SetValue("", value);
        SHChangeNotify(HChangeNotifyEventID.SHCNE_ASSOCCHANGED, HChangeNotifyFlags.SHCNF_IDLIST | HChangeNotifyFlags.SHCNF_FLUSH, IntPtr.Zero, IntPtr.Zero);
    }
}
catch 
{
    MessageBox.Show("以管理员身份运行可将工程文件关联到此程序"); 
}

但在修改完注册表后一定要进行注册表的刷新,对应上面的SHChangeNotify方法,这不是一个.NET自带的方法,而是系统文件shell32.dll中的方法,引用方法的方式如下:

[DllImport("shell32.dll")]
static extern void SHChangeNotify(HChangeNotifyEventID wEventId,
                                   HChangeNotifyFlags uFlags,
                                   IntPtr dwItem1,
                                   IntPtr dwItem2);

其中HChangeNotifyEventID和HChangeNotifyFlags都是枚举类型,其定义如下:

enum HChangeNotifyEventID
{
    /// <summary>
    /// All events have occurred. 
    /// </summary>
    SHCNE_ALLEVENTS = 0x7FFFFFFF,

    /// <summary>
    /// A file type association has changed. <see cref="HChangeNotifyFlags.SHCNF_IDLIST"/> 
    /// must be specified in the <i>uFlags</i> parameter. 
    /// <i>dwItem1</i> and <i>dwItem2</i> are not used and must be <see langword="null"/>. 
    /// </summary>
    SHCNE_ASSOCCHANGED = 0x08000000,

    /// <summary>
    /// The attributes of an item or folder have changed. 
    /// <see cref="HChangeNotifyFlags.SHCNF_IDLIST"/> or 
    /// <see cref="HChangeNotifyFlags.SHCNF_PATH"/> must be specified in <i>uFlags</i>. 
    /// <i>dwItem1</i> contains the item or folder that has changed. 
    /// <i>dwItem2</i> is not used and should be <see langword="null"/>.
    /// </summary>
    SHCNE_ATTRIBUTES = 0x00000800,

    /// <summary>
    /// A nonfolder item has been created. 
    /// <see cref="HChangeNotifyFlags.SHCNF_IDLIST"/> or 
    /// <see cref="HChangeNotifyFlags.SHCNF_PATH"/> must be specified in <i>uFlags</i>. 
    /// <i>dwItem1</i> contains the item that was created. 
    /// <i>dwItem2</i> is not used and should be <see langword="null"/>.
    /// </summary>
    SHCNE_CREATE = 0x00000002,

    /// <summary>
    /// A nonfolder item has been deleted. 
    /// <see cref="HChangeNotifyFlags.SHCNF_IDLIST"/> or 
    /// <see cref="HChangeNotifyFlags.SHCNF_PATH"/> must be specified in <i>uFlags</i>. 
    /// <i>dwItem1</i> contains the item that was deleted. 
    /// <i>dwItem2</i> is not used and should be <see langword="null"/>. 
    /// </summary>
    SHCNE_DELETE = 0x00000004,

    /// <summary>
    /// A drive has been added. 
    /// <see cref="HChangeNotifyFlags.SHCNF_IDLIST"/> or 
    /// <see cref="HChangeNotifyFlags.SHCNF_PATH"/> must be specified in <i>uFlags</i>. 
    /// <i>dwItem1</i> contains the root of the drive that was added. 
    /// <i>dwItem2</i> is not used and should be <see langword="null"/>. 
    /// </summary>
    SHCNE_DRIVEADD = 0x00000100,

    /// <summary>
    /// A drive has been added and the Shell should create a new window for the drive. 
    /// <see cref="HChangeNotifyFlags.SHCNF_IDLIST"/> or 
    /// <see cref="HChangeNotifyFlags.SHCNF_PATH"/> must be specified in <i>uFlags</i>. 
    /// <i>dwItem1</i> contains the root of the drive that was added. 
    /// <i>dwItem2</i> is not used and should be <see langword="null"/>. 
    /// </summary>
    SHCNE_DRIVEADDGUI = 0x00010000,

    /// <summary>
    /// A drive has been removed. <see cref="HChangeNotifyFlags.SHCNF_IDLIST"/> or 
    /// <see cref="HChangeNotifyFlags.SHCNF_PATH"/> must be specified in <i>uFlags</i>. 
    /// <i>dwItem1</i> contains the root of the drive that was removed.
    /// <i>dwItem2</i> is not used and should be <see langword="null"/>. 
    /// </summary>
    SHCNE_DRIVEREMOVED = 0x00000080,

    /// <summary>
    /// Not currently used. 
    /// </summary>
    SHCNE_EXTENDED_EVENT = 0x04000000,

    /// <summary>
    /// The amount of free space on a drive has changed. 
    /// <see cref="HChangeNotifyFlags.SHCNF_IDLIST"/> or 
    /// <see cref="HChangeNotifyFlags.SHCNF_PATH"/> must be specified in <i>uFlags</i>. 
    /// <i>dwItem1</i> contains the root of the drive on which the free space changed.
    /// <i>dwItem2</i> is not used and should be <see langword="null"/>. 
    /// </summary>
    SHCNE_FREESPACE = 0x00040000,

    /// <summary>
    /// Storage media has been inserted into a drive. 
    /// <see cref="HChangeNotifyFlags.SHCNF_IDLIST"/> or 
    /// <see cref="HChangeNotifyFlags.SHCNF_PATH"/> must be specified in <i>uFlags</i>. 
    /// <i>dwItem1</i> contains the root of the drive that contains the new media. 
    /// <i>dwItem2</i> is not used and should be <see langword="null"/>. 
    /// </summary>
    SHCNE_MEDIAINSERTED = 0x00000020,

    /// <summary>
    /// Storage media has been removed from a drive. 
    /// <see cref="HChangeNotifyFlags.SHCNF_IDLIST"/> or 
    /// <see cref="HChangeNotifyFlags.SHCNF_PATH"/> must be specified in <i>uFlags</i>. 
    /// <i>dwItem1</i> contains the root of the drive from which the media was removed. 
    /// <i>dwItem2</i> is not used and should be <see langword="null"/>. 
    /// </summary>
    SHCNE_MEDIAREMOVED = 0x00000040,

    /// <summary>
    /// A folder has been created. <see cref="HChangeNotifyFlags.SHCNF_IDLIST"/> 
    /// or <see cref="HChangeNotifyFlags.SHCNF_PATH"/> must be specified in <i>uFlags</i>. 
    /// <i>dwItem1</i> contains the folder that was created. 
    /// <i>dwItem2</i> is not used and should be <see langword="null"/>. 
    /// </summary>
    SHCNE_MKDIR = 0x00000008,

    /// <summary>
    /// A folder on the local computer is being shared via the network. 
    /// <see cref="HChangeNotifyFlags.SHCNF_IDLIST"/> or 
    /// <see cref="HChangeNotifyFlags.SHCNF_PATH"/> must be specified in <i>uFlags</i>. 
    /// <i>dwItem1</i> contains the folder that is being shared. 
    /// <i>dwItem2</i> is not used and should be <see langword="null"/>. 
    /// </summary>
    SHCNE_NETSHARE = 0x00000200,

    /// <summary>
    /// A folder on the local computer is no longer being shared via the network. 
    /// <see cref="HChangeNotifyFlags.SHCNF_IDLIST"/> or 
    /// <see cref="HChangeNotifyFlags.SHCNF_PATH"/> must be specified in <i>uFlags</i>. 
    /// <i>dwItem1</i> contains the folder that is no longer being shared. 
    /// <i>dwItem2</i> is not used and should be <see langword="null"/>. 
    /// </summary>
    SHCNE_NETUNSHARE = 0x00000400,

    /// <summary>
    /// The name of a folder has changed. 
    /// <see cref="HChangeNotifyFlags.SHCNF_IDLIST"/> or 
    /// <see cref="HChangeNotifyFlags.SHCNF_PATH"/> must be specified in <i>uFlags</i>. 
    /// <i>dwItem1</i> contains the previous pointer to an item identifier list (PIDL) or name of the folder. 
    /// <i>dwItem2</i> contains the new PIDL or name of the folder. 
    /// </summary>
    SHCNE_RENAMEFOLDER = 0x00020000,

    /// <summary>
    /// The name of a nonfolder item has changed. 
    /// <see cref="HChangeNotifyFlags.SHCNF_IDLIST"/> or 
    /// <see cref="HChangeNotifyFlags.SHCNF_PATH"/> must be specified in <i>uFlags</i>. 
    /// <i>dwItem1</i> contains the previous PIDL or name of the item. 
    /// <i>dwItem2</i> contains the new PIDL or name of the item. 
    /// </summary>
    SHCNE_RENAMEITEM = 0x00000001,

    /// <summary>
    /// A folder has been removed. 
    /// <see cref="HChangeNotifyFlags.SHCNF_IDLIST"/> or 
    /// <see cref="HChangeNotifyFlags.SHCNF_PATH"/> must be specified in <i>uFlags</i>. 
    /// <i>dwItem1</i> contains the folder that was removed. 
    /// <i>dwItem2</i> is not used and should be <see langword="null"/>. 
    /// </summary>
    SHCNE_RMDIR = 0x00000010,

    /// <summary>
    /// The computer has disconnected from a server. 
    /// <see cref="HChangeNotifyFlags.SHCNF_IDLIST"/> or 
    /// <see cref="HChangeNotifyFlags.SHCNF_PATH"/> must be specified in <i>uFlags</i>. 
    /// <i>dwItem1</i> contains the server from which the computer was disconnected. 
    /// <i>dwItem2</i> is not used and should be <see langword="null"/>. 
    /// </summary>
    SHCNE_SERVERDISCONNECT = 0x00004000,

    /// <summary>
    /// The contents of an existing folder have changed, 
    /// but the folder still exists and has not been renamed. 
    /// <see cref="HChangeNotifyFlags.SHCNF_IDLIST"/> or 
    /// <see cref="HChangeNotifyFlags.SHCNF_PATH"/> must be specified in <i>uFlags</i>. 
    /// <i>dwItem1</i> contains the folder that has changed. 
    /// <i>dwItem2</i> is not used and should be <see langword="null"/>. 
    /// If a folder has been created, deleted, or renamed, use SHCNE_MKDIR, SHCNE_RMDIR, or 
    /// SHCNE_RENAMEFOLDER, respectively, instead. 
    /// </summary>
    SHCNE_UPDATEDIR = 0x00001000,

    /// <summary>
    /// An image in the system image list has changed. 
    /// <see cref="HChangeNotifyFlags.SHCNF_DWORD"/> must be specified in <i>uFlags</i>. 
    /// </summary>
    SHCNE_UPDATEIMAGE = 0x00008000,

}

enum HChangeNotifyFlags
{
    /// <summary>
    /// The <i>dwItem1</i> and <i>dwItem2</i> parameters are DWORD values. 
    /// </summary>
    SHCNF_DWORD = 0x0003,
    /// <summary>
    /// <i>dwItem1</i> and <i>dwItem2</i> are the addresses of ITEMIDLIST structures that 
    /// represent the item(s) affected by the change. 
    /// Each ITEMIDLIST must be relative to the desktop folder. 
    /// </summary>
    SHCNF_IDLIST = 0x0000,
    /// <summary>
    /// <i>dwItem1</i> and <i>dwItem2</i> are the addresses of null-terminated strings of 
    /// maximum length MAX_PATH that contain the full path names 
    /// of the items affected by the change. 
    /// </summary>
    SHCNF_PATHA = 0x0001,
    /// <summary>
    /// <i>dwItem1</i> and <i>dwItem2</i> are the addresses of null-terminated strings of 
    /// maximum length MAX_PATH that contain the full path names 
    /// of the items affected by the change. 
    /// </summary>
    SHCNF_PATHW = 0x0005,
    /// <summary>
    /// <i>dwItem1</i> and <i>dwItem2</i> are the addresses of null-terminated strings that 
    /// represent the friendly names of the printer(s) affected by the change. 
    /// </summary>
    SHCNF_PRINTERA = 0x0002,
    /// <summary>
    /// <i>dwItem1</i> and <i>dwItem2</i> are the addresses of null-terminated strings that 
    /// represent the friendly names of the printer(s) affected by the change. 
    /// </summary>
    SHCNF_PRINTERW = 0x0006,
    /// <summary>
    /// The function should not return until the notification 
    /// has been delivered to all affected components. 
    /// As this flag modifies other data-type flags, it cannot by used by itself.
    /// </summary>
    SHCNF_FLUSH = 0x1000,
    /// <summary>
    /// The function should begin delivering notifications to all affected components 
    /// but should return as soon as the notification process has begun. 
    /// As this flag modifies other data-type flags, it cannot by used by itself.
    /// </summary>
    SHCNF_FLUSHNOWAIT = 0x2000
}

最后,为了能双击打开msm文件,还需要修改Application.Run方法,Environment.GetCommandLineArgs可以获得当前程序运行的参数,至少有1个参数,就是程序所在路径,第二个参数可选,为msm文件路径,在cmd中操作同理。

string[] args = Environment.GetCommandLineArgs();
if (args.Length == 2)
    Application.Run(new MeasureMan(args[1]));
else
    Application.Run(new MeasureMan(null));
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值