10

I need to send documents to a network printer (\myserver\myprinter). I'm using the System.Printing classes to print, and it works fine when it's from a Windows Service, but from an ASP.NET app, it's only able to print to local printers, not network printers. The error I'm getting is "Printer Name is not valid" This is what I'm using to get the printer name:

public string PrinterName
{
   using (LocalPrintServer server = new LocalPrintServer())
   return server.GetPrintQueue(@"\\myserver\myprinter");
}

What are my options here? Is this a permissions problem?

Prabhu
  • 12,995
  • 33
  • 127
  • 210
  • 2
    Under which user context is ASP.NET running? Are you using impersonation? What are the permissions on the printer? – Heinzi Sep 16 '10 at 17:32
  • It's running in ASP.NET Development Server, so I assume it's running under my Windows account. I'm able to print directly from Notepad to that server printer. – Prabhu Sep 16 '10 at 20:17

3 Answers3

5

By default, an ASP.NET application runs on a special account with limited rights. Just enough to serve webpages, nothing more. So you'll have to configure the ASPNET user.

By contrast Windows services usually run under local System account (with high privileges)

H H
  • 263,252
  • 30
  • 330
  • 514
  • Thanks...would you know how I can configure it to give it the sufficient privileges? – Prabhu Sep 16 '10 at 17:45
  • Thanks. I checked that it also doesn't work from a Windows Forms App. – Prabhu Sep 16 '10 at 17:56
  • @Prabhu: I am unfamiliar with GetPrintQueue but I did notice you are destroying that `server` immediately. Are you sure this exact code does run inside a Service? – H H Sep 16 '10 at 18:03
  • And does your WinForms app user have rights to the network printers? We still not seem to know that. – H H Sep 16 '10 at 18:21
  • I'm still trying to figure out how to check that. – Prabhu Sep 16 '10 at 18:22
  • @Prabhu: Print to it from Word or Notepad – H H Sep 16 '10 at 18:38
  • That works fine...also I noticed that the app is running on the ASP.NET Development Server. I'm on .NET 4.0 and VS2010. – Prabhu Sep 16 '10 at 19:01
5

There are issues with credentials that you could solve by impersonation or elevating rights of the user the web app is running under.

However, we did it by adding the network printer as a printer on the server (add printer dialogue on server) and having the job sent to that printer.

We used the Printing.PrintDocument like so (Code in VB)....

Public Class SpecialReportPrintJob
  Inherits Printing.PrintDocument

Protected Overrides Sub OnBeginPrint(ByVal ev as Printing.PrintEventArgs)
  MyBase.OnBeginPrint(ev)

  Me.PrinterSettings.PrinterName = "PrinterNameUsedOnServer"

  'setup rest of stuff....
End Sub  
End Class
'And we then call it like so
Dim printSpecialReport as new SpecialReportPrintJob()
printSpecialReport.Print()
Kevin LaBranche
  • 20,908
  • 5
  • 52
  • 76
  • So can I use \\myserver\myprinter as the PrinterName? – Prabhu Sep 16 '10 at 17:53
  • we left out \\myserver\. Another words whatever we named it on the server is how we called it. No UNC pathing or anything. – Kevin LaBranche Sep 16 '10 at 17:55
  • Oh ok..so you mean you installed the network printer as a local printer? – Prabhu Sep 16 '10 at 17:57
  • It's still a network printer but when we created the printer we choose local but then used a new port that was the IP address. AKA - Choose local but then on the option to use a printer port don't use the LPT1 or such, create a new port against the IP of the box. It worked great for us.... – Kevin LaBranche Sep 16 '10 at 18:04
  • Ok so I need to get the IP Address from our admin that would map to \\myserver\myprinter? I'd also need to install the drivers? – Prabhu Sep 16 '10 at 18:06
  • Is there a way to print a text file using PrintDocument, so that the output file is a text document? I am trying to print a document to a fax printer and not a real printer. The Fax printer expects a text file. – Prabhu Sep 17 '10 at 22:59
  • That's esentially what we did (printed a text file). Check out this link: http://msdn.microsoft.com/en-us/library/system.drawing.printing.printdocument.aspx – Kevin LaBranche Sep 18 '10 at 01:47
  • Just to make something clear here, when you add a printer and add a new IP port, you are *technically* creating a local printer on the server, not a network printer. It is going over TCP/IP, not SAMBA. – Adam Nofsinger Oct 23 '13 at 12:43
1

The Network Printing from ASP.Net/C# can be done using:

If the Network is configured for Domain Users and Printer is added to print server:

  • PrinterName to be defined as = "\\PrintServerIP_OR_Name\\PRINTERNAME" Example: PrinterSettings.PrinterName = "\\15.1.1.1\\prn001"
  • Check the permission set on the Printer Access
  • Which either be Domain Users or Everyone
  • If Domain Users, then the C# code can be enclosed within the impersonation that can be used to call the print code which is as below:
/// <summary>
    /// Does the actual impersonation.
    /// </summary>
    /// <param name="userName">The name of the user to act as.</param>
    /// <param name="domainName">The domain name of the user to act as.</param>
    /// <param name="password">The password of the user to act as.</param>
    private void ImpersonateValidUser(
        string userName, 
        string domain, 
        string password )
    {
        WindowsIdentity tempWindowsIdentity = null;
        IntPtr token = IntPtr.Zero;
        IntPtr tokenDuplicate = IntPtr.Zero;

        try
        {
            if ( RevertToSelf() )
            {
                if ( LogonUser(
                    userName, 
                    domain, 
                    password, 
                    LOGON32_LOGON_INTERACTIVE,
                    LOGON32_PROVIDER_DEFAULT, 
                    ref token ) != 0 )
                {
                    if ( DuplicateToken( token, 2, ref tokenDuplicate ) != 0 )
                    {
                        tempWindowsIdentity = new WindowsIdentity( tokenDuplicate );
                        impersonationContext = tempWindowsIdentity.Impersonate();
                    }
                    else
                    {
                        throw new Win32Exception( Marshal.GetLastWin32Error() );
                    }
                }
                else
                {
                    throw new Win32Exception( Marshal.GetLastWin32Error() );
                }
            }
            else
            {
                throw new Win32Exception( Marshal.GetLastWin32Error() );
            }
        }
        finally
        {
            if ( token!= IntPtr.Zero )
            {
                CloseHandle( token );
            }
            if ( tokenDuplicate!=IntPtr.Zero )
            {
                CloseHandle( tokenDuplicate );
            }
        }
    }

    /// <summary>
    /// Reverts the impersonation.
    /// </summary>
    private void UndoImpersonation()
    {
        if ( impersonationContext!=null )
        {
            impersonationContext.Undo();
        }   
    }

    private WindowsImpersonationContext impersonationContext = null;

First make an call to impersonate the user and then call the print function that would look like below:

if(ImpersonateValidUser("username", "domain", "password"))
{
  PrintDetails();
  UndoImpersonation();
}