WinSpool OpenPrinter访问被拒绝

时间:2014-02-24 13:11:46

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

这是第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上更改密码。

2 个答案:

答案 0 :(得分:2)

默认情况下,在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

答案 1 :(得分:0)

由于接受的答案不能解决我的问题,所以添加我的答案,对于可能会收到此错误的任何人,如果出现以下情况,这可能对您有用:

  • 在构建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));
    }
}