【问题标题】:WinSpool OpenPrinter Access DeniedWinSpool OpenPrinter 访问被拒绝
【发布时间】:2014-03-26 04:17:11
【问题描述】:

这是第 335 次问这个问题,我没有找到答案。 我正在尝试从 ASP.net C# 应用程序通过 WinSpool api 将原始数据直接发送到打印机。

我的代码只是here的副本。

此处出现错误

if( OpenPrinter( szPrinterName.Normalize(), out hPrinter, IntPtr.Zero ) )

它适用于本地打印机,但对于共享网络打印机,OpenPrinter 的结果(实际上是GetLastError 的结果)始终为5 - Access Denied

我试过了

  • PRINTER_DEFAULTS 的不同值与DesiredAccess 的不同组合
  • 为用户授予管理员权限
  • 设置打印机like this

我必须注意,我只想打印,而不是更改打印机配置或需要管理权限的东西。

我可以使用打印机选项页面和嵌入其中的测试工具从服务器打印到此共享打印机。所以打印机工作。如何通过 API 访问它?


更新:如果从 Windows 应用程序或控制台应用程序调用,此代码看起来工作正常。那么为什么在 Web 应用程序中拒绝访问?


更新 2:问题可能是由于打印机安装在主机 PC 上并与虚拟 PC 共享(或在生产中:打印机安装在域内并共享到 DMZ 中的 PC)并且没有适当的方法授予权限到这台打印机到虚拟 PC 的用户(或域外的用户)


更新 3:还有一个事实。如果我在资源管理器中从虚拟浏览到主机 PC(例如 \\host_pc\C$),我会收到通知输入用户名和密码以访问主机 PC。如果我在此之后检查“保存密码”,整个“访问被拒绝”问题就会消失,直到我在主机 PC 上更改密码。

【问题讨论】:

  • 您似乎已经回答了自己的问题。它不起作用,因为尝试打印的用户没有打印权限。您在授予权限时遇到问题,因为用户不在域中,但打印机在域中。还剩下什么?
  • 您在应用程序池中使用什么身份?您所说的“授予用户管理员权限”是指哪个用户
  • 我的错。我忘了说我已经尝试将应用程序池身份更改为自己。在我的帐户下,我可以从控制台打印,但不能从 Web 应用程序打印。管理员权限都没有帮助。
  • @arx 问题不在于授予权限,而实际上该权限适用于控制台应用程序,但不适用于同一用户帐户的 Web 应用程序。我相信它在某种程度上与 GUI 有关。因为网络应用程序没有使用它,所以操作系统对其安全性的处理方式不同。
  • 您能否确认您是在完全信任而不是部分信任下运行 IIS?

标签: c# asp.net vb.net winapi printing


【解决方案1】:

默认情况下,在 IIS 下运行的 ASP.Net 网站在低权限本地用户帐户 IIS_APPPOOL\mysite 下运行。为了允许客户端从网站访问域资源,您需要将运行 IIS 的用户(称为应用程序池标识)更改为对所有内容(包括网络打印机和 IIS 本身)。

最简单的解决方案(可能有更安全的解决方案)是将 IIS APPPool 更改为使用内置的 NetworkService 帐户。此帐户会作为 MyDomain\MyHostName$ 自动添加到您的域中,因此您可以使用它来授予打印机权限(或其他任何需要的权限)。

要更改应用程序池标识,只需打开 IIS 管理器,选择正确的应用程序池,然后点击“高级设置”,然后查找“标识”设置。

更多信息在这里: http://www.iis.net/learn/manage/configuring-security/application-pool-identities

【讨论】:

  • 我已尝试更改身份。没有效果。但我认为我应该尝试使用 NetworkService 帐户,因为它默认存在于任何机器上。感谢您的提示。
  • 如果您通过 Windows 任务计划程序设置控制台应用程序以使用 NetworkService 帐户运行,这可能有助于您调试。如果同一帐户上的相同代码在使用 NetworkService 的任务下运行良好,但在使用 NetworkService 的 IIS 下失败,您可以排除权限问题并专注于 IIS 配置。
【解决方案2】:

添加我的答案,因为接受的答案没有解决我的问题,对于任何可能遇到此错误的人,这可能对您有用,如果:

  • 为 AnyCPU 或 x64 构建时出现错误。
  • 您不会收到针对 x86 构建的错误。

问题似乎在于 GC 回收 PRINTER_DEFAULTS 结构,然后 OpenPrinter 尝试写入该位置。建议的解决方案是使用将保留在堆栈中的类。

public class PrinterSettings
{
    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
    internal class PRINTERDEFAULTSClass
    {
        public IntPtr pDatatype;
        public IntPtr pDevMode;
        public int DesiredAccess;
    } 

    [DllImport("winspool.Drv", EntryPoint = "OpenPrinterA", SetLastError = true, CharSet = CharSet.Ansi, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
    private static extern bool OpenPrinter([MarshalAs(UnmanagedType.LPStr)] string szPrinter, out IntPtr hPrinter, PRINTERDEFAULTSClass pdc);

    public DEVMODE GetPrinterSettings(string PrinterName)
    {
        DEVMODE dm;

        var pdc = new PRINTERDEFAULTSClass
        {
            pDatatype = new IntPtr(0),
            pDevMode = new IntPtr(0),
            DesiredAccess = PRINTER_ALL_ACCESS
        };

        var nRet = Convert.ToInt32(OpenPrinter(PrinterName,
                out hPrinter, pdc));
    }
}

【讨论】:

  • 嗨..我面临同样的问题。并想尝试您的解决方案,但对您的代码有一些疑问..您能否解释一下 DEVMODE 类型的含义以及在哪里/如何导入它,如果是外部的...... TKS
  • 嗨@JuanPabloGomez DEVMODERawPrinterHelper 类使用的结构,您可以找到它的许多实现,例如github.com/andyyou/SendToPrinter/blob/master/Printer/…
猜你喜欢
  • 1970-01-01
  • 2014-08-10
  • 2021-03-06
  • 2011-05-15
  • 2010-09-06
  • 2014-03-22
  • 2013-08-10
  • 2018-04-20
  • 2012-08-23
相关资源
最近更新 更多