5
votes

This is 335th time this question being asked, by I've found no answer. I'm trying to send raw data directly to printer via WinSpool api from ASP.net C# application.

My code is just a copy from here.

Error goes here

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

It works fine for local printer but for shared network printer the result of OpenPrinter (result of GetLastError actually) is always 5 - Access Denied.

I've tried

  • different values for PRINTER_DEFAULTS with different combinations of DesiredAccess
  • give administrator privileges to user
  • setup printer like this

I must note that I just want to print, not change printer config or something that require administrative rights.

I can print to this shared printer from server using Printer Option page and test tool embedded in it. So printer works. How to gain access to it via API?


Update: looks like this code is working fine if called from Windows Application or Console Application. Then why access denied in Web Application?


Update 2: problem may be caused by the fact that printer installed on host PC and shared with virtual PC (or in production: printer is installed inside domain and shared to PC in DMZ) and there is no proper way to grant rights to this printer to users of virtual PC (or users outside of domain)


Update 3: and here is one more fact. If I browse to host PC from virtual in explorer (like this \\host_pc\C$) I get notified to enter user name and password to access host PC. If I check "save password" after that the whole "access denied"-problem will go away until I change password on host PC.

2
You seem to have answered your own question. It doesn't work because the user trying to print doesn't have permission to print. You're having trouble giving permission because the user isn't on a domain but the printer is. What's left?arx
What identity are you using for the application pool? and which user did you mean by "give administrator privileges to user"steve cook
My bad. I forgot to say that I've tried to change app pool identity to myself. And under my account I vas able to print from console but not from web application. Neither administrator privileges helped.Mak Sim
@arx problem is not in giving permissions but in fact that permissions working for console app but not working for web app for the same user account. I believe that it somehow related to GUI. Because web app is not using it so OS treats its security differently.Mak Sim
Can you also confirm that you are running IIS under full trust - and not under partial trust?steve cook

2 Answers

2
votes

By default, your ASP.Net website running under IIS runs under a low-privilege local user account IIS_APPPOOL\mysite. In order for you to allow clients to access domain resources from the website, you'll need to change the user that IIS runs under (known as the application pool identity) to a domain user that has the correct rights to everything (both the network printer, and IIS itself).

The simplest solution (there may be more secure ones) is to change the IIS APPPool to use the built-in NetworkService account. This account is automatically added to your domain as MyDomain\MyHostName$, so you can use that to grant printer permissions (or whatever else is needed).

To change the app pool identity, just open the IIS manager, select the right application pool and then hit "Advanced Settings", and look for the setting "Identity".

More info here: http://www.iis.net/learn/manage/configuring-security/application-pool-identities

1
votes

Adding my answer since the accepted answer didn't solve my issue, to any who may get this error this might be useful to you if:

  • You get an error building for AnyCPU or x64.
  • You don't get an error building for x86.

The issue seems to be in GC recycling the PRINTER_DEFAULTS struct and than the OpenPrinter tries writing to that location. The suggested solution is to use a class which will stay on the stack.

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));
    }
}