想表白的荒野 · 新华社长篇通讯追记黄文秀:用生命坚守初心和使 ...· 3 周前 · |
谦和的炒粉 · 资料:《聊斋志异·画皮》原文_手机新浪网· 2 月前 · |
留胡子的热带鱼 · 摩登家庭1-11季百度云/网盘资源终于找到了 ...· 3 月前 · |
玩足球的手套 · 杭州第四中学江东学校· 3 月前 · |
火爆的罐头 · 【Kindle Oasis2 ...· 4 月前 · |
我试图在C# .NET 3.5应用程序中打印到网络打印机上,并得到以下异常:
手术成功完成
是什么原因造成的,如何解决呢?
System.ComponentModel.Win32Exception: The operation completed successfully
at System.Drawing.Printing.PrinterSettings.GetHdevmodeInternal()
at System.Drawing.Printing.PrinterSettings.GetHdevmode(PageSettings pageSettings)
at System.Drawing.Printing.PrintController.OnStartPrint(PrintDocument document, PrintEventArgs e)
at System.Windows.Forms.PrintControllerWithStatusDialog.OnStartPrint(PrintDocument document, PrintEventArgs e)
at System.Drawing.Printing.PrintController.Print(PrintDocument document)
at System.Drawing.Printing.PrintDocument.Print()
为了缩小问题范围,我创建了一个简单的控制台应用程序。作为一个正常的用户运行,应用程序打印。当作为服务帐户运行时,它会为服务帐户错误。
针对我的问题的 解决方案 是卸载导致问题的驱动程序,并安装一个较旧的驱动程序。
发布于 2014-07-11 21:41:42
令人费解的消息是由.NET框架内的pinvoke代码中的一个bug引起的。失败的底层winapi调用是 函数 。它的pinvoke声明如下所示:
[DllImport("winspool.drv", CharSet=CharSet.Auto, SetLastError=true)]
public static extern int DocumentProperties(...);
SetLastError
属性是错误的。从MSDN链接中可以看出,该函数通过返回一个负值来指示失败。并且没有文档化来设置
GetLastError()
返回的错误代码。
这个错误的后果是,框架将调用
Marshal.GetLastWin32Error()
来获取错误代码,并将得到一个随机值,因为
DocumentProperties()
没有设置它。
0
的值并不是不可能的,它会生成“操作成功完成”异常消息。
因此,您需要忽略异常消息,这当然是非常没有帮助的。不幸的是,与大多数GDI函数一样,这个winapi函数属于一类函数,只产生一个“它不工作”的返回代码。它没有给出寻找这个问题的线索。这种怪癖有一个不太好的理由:当你调用
DocumentProperties()
时,Windows本身几乎不起作用;大部分工作是由打印机驱动程序完成的。在winapi中没有一组用于打印的错误代码。任何事情都是可能的:打印机驱动程序不是细微的代码块。告诉你问题是打印机驱动程序的工作。他们应该通过弹出自己的窗口来做到这一点。从理论上讲,他们是无论如何;激烈的竞争,在那个市场部分没有留下很多钱来支付一个好程序员的工资。
当然,当您从服务中打印时,这是无法工作的。没有任何方法可以看到这样的弹出窗口,这也是微软
强烈反对从服务中打印的核心原因。您和客户的IT人员都没有机会诊断问题。有关从服务中使用
博客帖子
的附加说明,请阅读此
PrintDocument
。
没有人喜欢得到这样的建议,但写在墙上。不要这样做。
发布于 2014-07-17 20:04:05
为了添加到@HansPassant的答案,下面是抛出异常的确切代码:
微软参考源, PrinterSettings.cs
private IntPtr GetHdevmodeInternal(string printer) {
// Create DEVMODE
int modeSize = SafeNativeMethods.DocumentProperties(NativeMethods.NullHandleRef, NativeMethods.NullHandleRef, printer, IntPtr.Zero, NativeMethods.NullHandleRef, 0);
if (modeSize < 1) {
throw new InvalidPrinterException(this);
IntPtr handle = SafeNativeMethods.GlobalAlloc(SafeNativeMethods.GMEM_MOVEABLE, (uint)modeSize); // cannot be <0 anyway
IntPtr pointer = SafeNativeMethods.GlobalLock(new HandleRef(null, handle));
//Get the DevMode only if its not cached....
if (cachedDevmode != null) {
Marshal.Copy(cachedDevmode, 0, pointer, devmodebytes);