运行ProgId动词的命令

时间:2012-08-26 14:55:11

标签: c# .net windows winapi com

有没有办法为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);
    }

3 个答案:

答案 0 :(得分:2)

这里的根本错误是您从http://中省略了FileName。添加它,一切都会好的。

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

您根本不需要设置lpClasslpFilehttp://开头的事实决定了这个类。


不如自己致电ShellExecuteEx,您也可以Process为您做这件事:

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

甚至:

Process.Start(@"http://google.com");

答案 1 :(得分:2)

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

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

但是如果您真的想要执行命令行,可以使用AssocQueryString获取ASSOCSTR_COMMAND,然后通过SHEvaluateSystemCommandTemplate执行插入以获取命令行。

答案 2 :(得分: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
        }
    }
}