有没有办法为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);
}
答案 0 :(得分:2)
这里的根本错误是您从http://
中省略了FileName
。添加它,一切都会好的。
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");
答案 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
}
}
}