PrintDocument.Print导致Win32Exception操作成功完成

时间:2009-09-09 16:46:49

标签: c# printing

我正在尝试使用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()
  • 该帐户有权使用网络打印机进行打印。为Everyone设置权限以进行打印。
  • 已删除并重新创建打印机。
  • 直接打印到打印机的假脱机设置已经双向切换。
  • 机器上的其他打印机工作正常
  • 网络上的其他客户端和同一台计算机上的应用程序可以毫无问题地打印到此打印机。

为了缩小问题范围,我创建了一个简单的控制台应用程序。作为普通用户运行,应用程序打印。当“作为服务帐户运行”时,服务帐户的错误

enter image description here

我的问题的解决方案是卸载导致问题的驱动程序,并安装较旧的驱动程序。

4 个答案:

答案 0 :(得分:17)

神秘的消息是由.NET Framework中的pinvoke代码中的错误引起的。失败的基础winapi调用是DocumentProperties() function。它的pinvoke声明如下:

[DllImport("winspool.drv", CharSet=CharSet.Auto, SetLastError=true)]
public static extern int DocumentProperties(...);

SetLastError属性错误。从MSDN链接可以看出,该函数通过返回负值来指示失败。并且 not 记录了以设置GetLastError()返回的错误代码。

此错误的后果是框架将调用Marshal.GetLastWin32Error()以获取错误代码,并且将获得随机值,因为DocumentProperties()未设置它。值0不太可能,这会产生“操作成功完成”异常消息。

所以你需要忽略异常消息;当然,这是非常无益的。不幸的是,这个winapi函数属于一类函数,就像大多数GDI函数一样,只产生“它不起作用”的返回代码。它没有提示在何处寻找问题。这个怪癖有一个不太合理的原因:当你拨打DocumentProperties()时,Windows本身的作用很小;大多数工作都是由打印机驱动程序完成的。在winapi中没有用于打印的错误代码集。一切皆有可能:打印机驱动程序不是微妙的代码块。打印机驱动程序的工作是告诉您有关问题的信息。他们应该通过弹出自己的窗口来实现这一目标。理论上他们无论如何;这个细分市场的恶性竞争并没有留下很多钱来支付这些天的优秀程序员工资。

当您从服务打印时,这当然不起作用。没有任何方法可以看到这样的弹出窗口,这是Microsoft 强烈阻止从服务打印的核心原因。您和您客户的IT员工都没有机会诊断问题。有关使用服务中的PrintDocument的其他说明,请阅读此blog post

没有人喜欢得到这样的建议,但写作就在墙上。 不要这样做

答案 1 :(得分:5)

要添加@ HansPassant的答案,以下是引发异常的确切代码:

Microsoft参考源,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);
    }
    else  {
        int returnCode = SafeNativeMethods.DocumentProperties(NativeMethods.NullHandleRef, NativeMethods.NullHandleRef, printer, pointer, NativeMethods.NullHandleRef, SafeNativeMethods.DM_OUT_BUFFER);
        if (returnCode < 0) {
            throw new Win32Exception();  // <--------
        }
    }

答案 2 :(得分:1)

很难回答没有代码和异常,说你一切都好。所以我只想提出一些想法来追踪问题

  1. 在本地打印机上进行测试,以确保应用程序正常运行。
  2. 尝试使用记事本或类似的东西打印到联网打印机
  3. 双重三重检查应用程序正在运行的用户是否有权打印到网络打印机。
  4. 在另一台联网打印机上测试(在为该打印机执行2和3之后)

答案 3 :(得分:0)

我也有这个问题。在我的例子中,我使用一个单独的线程来打印报告,并通过“ManualResetEvent”将其与主线程同步。 (我已经开发出这种方式,因为.Net将打印逻辑划分为“PrintPage”方法......)。

由于打印机驱动程序与此多线程环境之间的不兼容性(我无法解释其来源)而发生错误。

我通过将打印逻辑方法签名从“void”更改为“IEnumerable”解决了问题,并突破了yield return,这是一种类似于Unity3d游戏引擎的“CoRoutines”的方法。这样,就没有必要创建多个线程,并且我的打印代码变得井井有条。