我们遇到了一个奇怪的问题,似乎我们不是唯一的问题(见底部注释)。
问题是我们想用shell动词printto调用ShellExecute。我们正在检查它是否已注册,如果是,请启动流程。可以在ProcessStartInfo.Verbs
中检索已注册的动词。
ProcessStartInfo startInfo = new ProcessStartInfo();
startInfo.FileName = @"C:\test.jpg";
startInfo.Verb = "printto";
startInfo.Arguments = "MyPrinter";
startInfo.UseShellExecute = true;
if (!startInfo.Verbs.Contains("printto"))
throw new Exception("PrintTo is not supported!");
try
{
Process.Start(startInfo);
}
catch (Win32Exception ex) when (ex.NativeErrorCode == 1155)
{
Console.WriteLine("Somehow printto is NOT registered...");
}
当使用Photos UWP应用程序作为默认查看器运行Windows 10时,控制台将打印出一个代码为1155的Win32Exception,这意味着文件类型未注册(对于给定的动词)。如果(旧)Windows图片查看器是默认设置,则可以正常工作。
请注意,我们正在检查动词是否已注册,如果是,则仅调用此动词。似乎微软在这里做了不同的事情。
最大的问题是:为什么这两个MS API不能再一起玩了,我们怎么能绕过它呢?
注意: 有一个旧的讨论,答案不是特别正确,但也有一个稍微不同的问题描述: Windows 8 blows error on c# process for printing pdf file, how?
因此,我决定开始一个新问题,并希望它符合SO原则。答案 0 :(得分:1)
ProcessStartInfo.Verbs
属性有点破,因为它没有考虑更新版本的Windows(Windows 8及更高版本)如何检索已注册的应用程序。该属性仅检查为HKCR\.ext
下定义的ProgId注册的动词(如reference source中所示),并且不考虑其他位置,例如注册表项HKEY_CURRENT_USER\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\FileExts\.ext
下方或其他一些地方,例如通过政策定义。
最好的方法是不依赖于直接检查注册表(由ProcessStartInfo
类完成),而是使用适当的Windows API函数AssocQueryString
来检索关联的ProgId:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using Microsoft.Win32;
class Program
{
private static void Main(string[] args)
{
string fileName = @"E:\Pictures\Sample.jpg";
string progId = AssocQueryString(AssocStr.ASSOCSTR_PROGID, fileName);
var verbs = GetVerbsByProgId(progId);
if (!verbs.Contains("printto"))
{
throw new Exception("PrintTo is not supported!");
}
}
private static string[] GetVerbsByProgId(string progId)
{
var verbs = new List<string>();
if (!string.IsNullOrEmpty(progId))
{
using (var key = Registry.ClassesRoot.OpenSubKey(progId + "\\shell"))
{
if (key != null)
{
var names = key.GetSubKeyNames();
verbs.AddRange(
names.Where(
name =>
string.Compare(
name,
"new",
StringComparison.OrdinalIgnoreCase)
!= 0));
}
}
}
return verbs.ToArray();
}
private static string AssocQueryString(AssocStr association, string extension)
{
uint length = 0;
uint ret = AssocQueryString(
AssocF.ASSOCF_NONE, association, extension, "printto", null, ref length);
if (ret != 1) //expected S_FALSE
{
throw new Win32Exception();
}
var sb = new StringBuilder((int)length);
ret = AssocQueryString(
AssocF.ASSOCF_NONE, association, extension, null, sb, ref length);
if (ret != 0) //expected S_OK
{
throw new Win32Exception();
}
return sb.ToString();
}
[DllImport("Shlwapi.dll", CharSet = CharSet.Unicode, SetLastError = true)]
private static extern uint AssocQueryString(
AssocF flags,
AssocStr str,
string pszAssoc,
string pszExtra,
[Out] StringBuilder pszOut,
ref uint pcchOut);
[Flags]
private enum AssocF : uint
{
ASSOCF_NONE = 0x00000000,
ASSOCF_INIT_NOREMAPCLSID = 0x00000001,
ASSOCF_INIT_BYEXENAME = 0x00000002,
ASSOCF_OPEN_BYEXENAME = 0x00000002,
ASSOCF_INIT_DEFAULTTOSTAR = 0x00000004,
ASSOCF_INIT_DEFAULTTOFOLDER = 0x00000008,
ASSOCF_NOUSERSETTINGS = 0x00000010,
ASSOCF_NOTRUNCATE = 0x00000020,
ASSOCF_VERIFY = 0x00000040,
ASSOCF_REMAPRUNDLL = 0x00000080,
ASSOCF_NOFIXUPS = 0x00000100,
ASSOCF_IGNOREBASECLASS = 0x00000200,
ASSOCF_INIT_IGNOREUNKNOWN = 0x00000400,
ASSOCF_INIT_FIXED_PROGID = 0x00000800,
ASSOCF_IS_PROTOCOL = 0x00001000,
ASSOCF_INIT_FOR_FILE = 0x00002000
}
private enum AssocStr
{
ASSOCSTR_COMMAND = 1,
ASSOCSTR_EXECUTABLE,
ASSOCSTR_FRIENDLYDOCNAME,
ASSOCSTR_FRIENDLYAPPNAME,
ASSOCSTR_NOOPEN,
ASSOCSTR_SHELLNEWVALUE,
ASSOCSTR_DDECOMMAND,
ASSOCSTR_DDEIFEXEC,
ASSOCSTR_DDEAPPLICATION,
ASSOCSTR_DDETOPIC,
ASSOCSTR_INFOTIP,
ASSOCSTR_QUICKTIP,
ASSOCSTR_TILEINFO,
ASSOCSTR_CONTENTTYPE,
ASSOCSTR_DEFAULTICON,
ASSOCSTR_SHELLEXTENSION,
ASSOCSTR_DROPTARGET,
ASSOCSTR_DELEGATEEXECUTE,
ASSOCSTR_SUPPORTED_URI_PROTOCOLS,
ASSOCSTR_PROGID,
ASSOCSTR_APPID,
ASSOCSTR_APPPUBLISHER,
ASSOCSTR_APPICONREFERENCE,
ASSOCSTR_MAX
}
}
但是,这并不能解决您的实际问题,即在Windows 10上打印图像。如果您要求打印图像,则可以使用PrintDocument
中的System.Drawing.Printing
类来执行此操作。如本相关帖子中所述的命名空间:Print images in C#:
PrintDocument pd = new PrintDocument();
pd.PrintPage += PrintPage;
pd.Print();
private void PrintPage(object o, PrintPageEventArgs e)
{
System.Drawing.Image img = System.Drawing.Image.FromFile("D:\\Foto.jpg");
Point loc = new Point(100, 100);
e.Graphics.DrawImage(img, loc);
}