2

有没有一种方法可以执行 progid 动词的命令,而无需在注册表中挖掘并进行字符串操作?

使用 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”。在注册表中,我可以看到这个 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 找不到”错误,我不想发生这种情况,因为它与 URL 无关(来自:http: //blogs.msdn.com/b/ oldnewthing/archive/2010/07/01/10033224.aspx


这是我想出的解决方案:

    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);
    }
4

3 回答 3

2

这里的基本错误是您http://FileName. 添加它,一切都会好起来的。

shellExecuteInfo.lpFile = "http://google.com";

你根本不需要设置lpClasslpFile以开头的事实http://决定了类别。


与其打电话给ShellExecuteEx自己,不如为自己Process做:

ProcessStartInfo psi = new ProcessStartInfo();
psi.FileName = @"http://google.com";
psi.UseShellExecute = true;
Process.Start(psi);

甚至:

Process.Start(@"http://google.com");
于 2012-08-26T18:44:55.950 回答
2

显式类不会删除lpFile引用有效资源(文件或 URL)的要求。类指定了资源应该如何执行(而不是从文件类型或 URL 协议中推断出类),但您仍然必须传递一个有效的资源。google.com被视为文件名,因为它不是 URL,并且该文件不存在,因此您会收到“未找到”错误。

您尝试做的一般情况比仅提取命令行更复杂,因为大多数浏览器使用 DDE 而不是命令行作为它们的主要调用。(命令行是 DDE 失败时的后备。)

但是如果你真的要执行一个命令行,你可以使用AssocQueryString来获取ASSOCSTR_COMMAND,然后执行插入 viaSHEvaluateSystemCommandTemplate来获取命令行来执行。

于 2012-08-27T02:34:50.147 回答
0

我的代码包括检查以防止一些常见错误...希望它有所帮助:-)

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
        }
    }
}
于 2014-04-29T18:44:39.113 回答