有没有办法执行一个progid动词的命令,而不必在registry中进行挖掘和string操作?
使用ShObjIdl.idl我可以运行以下命令来获取默认浏览器的ProgId:
var reg = new ShellObjects.ApplicationAssociationRegistration(); string progID; reg.QueryCurrentDefault("http", ShellObjects.ASSOCIATIONTYPE.AT_URLPROTOCOL, ShellObjects.ASSOCIATIONLEVEL.AL_EFFECTIVE, out progID);
这给了我“ChromeHTML.FHXQEQDDJYXVQSFWM2SVMV5GNA”。 在registry中,我可以看到这个progid具有以下shell / open / command:
"C:\Users\Paul\AppData\Local\Google\Chrome\Application\chrome.exe" -- "%1"
有没有一个API,我可以通过ProgId,连同动词和参数,它会运行它?
我去的一条路线是使用ShellExecuteEx:
var shellExecuteInfo = new SHELLEXECUTEINFO(); shellExecuteInfo.cbSize = Marshal.SizeOf(shellExecuteInfo); shellExecuteInfo.fMask = SEE_MASK_CLASSNAME; shellExecuteInfo.hwnd = IntPtr.Zero; shellExecuteInfo.lpVerb = "open"; shellExecuteInfo.lpFile = "google.com"; shellExecuteInfo.nShow = SW_SHOWNORMAL; shellExecuteInfo.lpClass = "http"; ShellExecuteEx(ref shellExecuteInfo);
然而,由于Window检查lpFile,而导致“Windows can not find”错误,我不想发生,因为它与URL无关(来自: http : //blogs.msdn.com/b/ oldnewthing / archive / 2010/07/01 / 10033224.aspx )
这是我提出的解决scheme:
private static void Main(string[] args) { if (!OpenUrlInDefaultBrowser("google.com")) Console.WriteLine("An error happened"); } [DllImport("Shlwapi.dll")] private static extern int AssocQueryString(ASSOCF flags, ASSOCSTR str, string pszAssoc, string pszExtra, StringBuilder pszOut, ref uint pcchOut); private enum ASSOCF { ASSOCF_NONE = 0x00000000 } private enum ASSOCSTR { ASSOCSTR_COMMAND = 1 } [DllImport("Shell32.dll", CharSet=CharSet.Auto)] private static extern int SHEvaluateSystemCommandTemplate(string pszCmdTemplate, out string ppszApplication, out string ppszCommandLine, out string ppszParameters); private static bool OpenUrlInDefaultBrowser(string url) { string browserProgId; if (!GetDefaultBrowserProgId(out browserProgId)) return false; string browserCommandTemplate; if (!GetCommandTemplate(browserProgId, out browserCommandTemplate)) return false; string browserExecutable; string parameters; if (!EvaluateCommandTemplate(browserCommandTemplate, out browserExecutable, out parameters)) return false; parameters = ReplaceSubstitutionParameters(parameters, url); try { Process.Start(browserExecutable, parameters); } catch (InvalidOperationException) { return false; } catch (Win32Exception) { return false; } catch (FileNotFoundException) { return false; } return true; } private static bool GetDefaultBrowserProgId(out string defaultBrowserProgId) { try { // midl "C:\Program Files (x86)\Windows Kits\8.0\Include\um\ShObjIdl.idl" // tlbimp ShObjIdl.tlb var applicationAssociationRegistration = new ApplicationAssociationRegistration(); applicationAssociationRegistration.QueryCurrentDefault("http", ShellObjects.ASSOCIATIONTYPE.AT_URLPROTOCOL, ShellObjects.ASSOCIATIONLEVEL.AL_EFFECTIVE, out defaultBrowserProgId); } catch (COMException) { defaultBrowserProgId = null; return false; } return !string.IsNullOrEmpty(defaultBrowserProgId); } private static bool GetCommandTemplate(string defaultBrowserProgId, out string commandTemplate) { var commandTemplateBufferSize = 0U; AssocQueryString(ASSOCF.ASSOCF_NONE, ASSOCSTR.ASSOCSTR_COMMAND, defaultBrowserProgId, "open", null, ref commandTemplateBufferSize); var commandTemplateStringBuilder = new StringBuilder((int)commandTemplateBufferSize); var hresult = AssocQueryString(ASSOCF.ASSOCF_NONE, ASSOCSTR.ASSOCSTR_COMMAND, defaultBrowserProgId, "open", commandTemplateStringBuilder, ref commandTemplateBufferSize); commandTemplate = commandTemplateStringBuilder.ToString(); return hresult == 0 && !string.IsNullOrEmpty(commandTemplate); } private static bool EvaluateCommandTemplate(string commandTemplate, out string application, out string parameters) { string commandLine; var hresult = SHEvaluateSystemCommandTemplate(commandTemplate, out application, out commandLine, out parameters); return hresult == 0 && !string.IsNullOrEmpty(application) && !string.IsNullOrEmpty(parameters); } private static string ReplaceSubstitutionParameters(string parameters, string replacement) { // Not perfect but good enough for this purpose return parameters.Replace("%L", replacement) .Replace("%l", replacement) .Replace("%1", replacement); }
显式类不会删除lpFile
引用有效资源(文件或URL)的要求。 该类指定如何执行资源(而不是从文件类型或URL协议推断类),但是您仍然必须传递有效的资源。 google.com
被视为文件名称,因为它不是一个URL,并且该文件不存在,所以你得到“找不到”的错误。
你所要做的一般情况比提取命令行更复杂,因为大多数浏览器使用DDE而不是命令行作为主要调用。 (命令行是DDE失败时的回退。)
但是如果你真的想执行一个命令行,可以使用AssocQueryString
来获取ASSOCSTR_COMMAND
,然后通过SHEvaluateSystemCommandTemplate
执行插入SHEvaluateSystemCommandTemplate
来获得执行的命令行。
这里的根本错误是你从FileName
省略了http://
。 补充一点,一切都会好的。
shellExecuteInfo.lpFile = "http://google.com";
你根本不需要设置lpClass
。 lpFile
以http://
开头的事实决定了这个类。
而不是自己调用ShellExecuteEx
,你可能也是为你做的Process
:
ProcessStartInfo psi = new ProcessStartInfo(); psi.FileName = @"http://google.com"; psi.UseShellExecute = true; Process.Start(psi);
甚至:
Process.Start(@"http://google.com");
我的代码,包括检查,以防止一些常见的错误…希望它有助于:-)
using System; using System.Diagnostics; using System.IO; using System.Runtime.InteropServices; using System.Text; namespace HQ.Util.Unmanaged { /// <summary> /// Usage: string executablePath = FileAssociation.GetExecFileAssociatedToExtension(pathExtension, "open"); /// Usage: string command FileAssociation.GetExecCommandAssociatedToExtension(pathExtension, "open"); /// </summary> public static class FileAssociation { /// <summary> /// /// </summary> /// <param name="ext"></param> /// <param name="verb"></param> /// <returns>Return null if not found</returns> public static string GetExecCommandAssociatedToExtension(string ext, string verb = null) { if (ext[0] != '.') { ext = "." + ext; } string executablePath = FileExtentionInfo(AssocStr.Command, ext, verb); // Ensure to not return the default OpenWith.exe associated executable in Windows 8 or higher if (!string.IsNullOrEmpty(executablePath) && File.Exists(executablePath) && !executablePath.ToLower().EndsWith(".dll")) { if (executablePath.ToLower().EndsWith("openwith.exe")) { return null; // 'OpenWith.exe' is th windows 8 or higher default for unknown extensions. I don't want to have it as associted file } return executablePath; } return executablePath; } /// <summary> /// /// </summary> /// <param name="ext"></param> /// <param name="verb"></param> /// <returns>Return null if not found</returns> public static string GetExecFileAssociatedToExtension(string ext, string verb = null) { if (ext[0] != '.') { ext = "." + ext; } string executablePath = FileExtentionInfo(AssocStr.Executable, ext, verb); // Will only work for 'open' verb if (string.IsNullOrEmpty(executablePath)) { executablePath = FileExtentionInfo(AssocStr.Command, ext, verb); // required to find command of any other verb than 'open' // Extract only the path if (!string.IsNullOrEmpty(executablePath) && executablePath.Length > 1) { if (executablePath[0] == '"') { executablePath = executablePath.Split('\"')[1]; } else if (executablePath[0] == '\'') { executablePath = executablePath.Split('\'')[1]; } } } // Ensure to not return the default OpenWith.exe associated executable in Windows 8 or higher if (!string.IsNullOrEmpty(executablePath) && File.Exists(executablePath) && !executablePath.ToLower().EndsWith(".dll")) { if (executablePath.ToLower().EndsWith("openwith.exe")) { return null; // 'OpenWith.exe' is th windows 8 or higher default for unknown extensions. I don't want to have it as associted file } return executablePath; } return executablePath; } [DllImport("Shlwapi.dll", SetLastError = true, CharSet = CharSet.Auto)] static extern uint AssocQueryString(AssocF flags, AssocStr str, string pszAssoc, string pszExtra, [Out] StringBuilder pszOut, [In][Out] ref uint pcchOut); private static string FileExtentionInfo(AssocStr assocStr, string doctype, string verb) { uint pcchOut = 0; AssocQueryString(AssocF.Verify, assocStr, doctype, verb, null, ref pcchOut); Debug.Assert(pcchOut != 0); if (pcchOut == 0) { return ""; } StringBuilder pszOut = new StringBuilder((int)pcchOut); AssocQueryString(AssocF.Verify, assocStr, doctype, verb, pszOut, ref pcchOut); return pszOut.ToString(); } [Flags] public enum AssocF { Init_NoRemapCLSID = 0x1, Init_ByExeName = 0x2, Open_ByExeName = 0x2, Init_DefaultToStar = 0x4, Init_DefaultToFolder = 0x8, NoUserSettings = 0x10, NoTruncate = 0x20, Verify = 0x40, RemapRunDll = 0x80, NoFixUps = 0x100, IgnoreBaseClass = 0x200 } public enum AssocStr { Command = 1, Executable, FriendlyDocName, FriendlyAppName, NoOpen, ShellNewValue, DDECommand, DDEIfExec, DDEApplication, DDETopic } } }