C# Tips是博主开启的第一个项目,以C#为示例语言,旨在普及编程技巧、经典算法以及计算机视觉、图形学知识。
想必各位同学在双击打开非txt的文本文件时通常会遇到这样的困扰: 因为以.cue为后缀的文件并未与电脑中的任何程序关联,所以无法直接打开文件,选择一个能够打开文件的软件其实就是将cue文件与之进行关联。好处显而易见,就是凡是cue文件都可以通过指定软件直接双击打开。当然除此之外,文件如果还带有特定的图标,那么更能提升文件的逼格。
文件的关联信息存储在注册表(Win+R再输入regedit即可打开注册表)中,无论是修改默认程序还是指定程序,其本质都是修改了注册表信息。自定义文件关联信息通常存储在HKEY_CLASSES_ROOT结点下:
第一项为*,其含义为通用的关联方式,换句话说就是可以利用一个软件打开所有文件;其他项则是特定后缀文件的关联方式。对于第一种情况,以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));