none
WindowsImpersonation fails to print a pdf document on network printer. RRS feed

  • Question

  • I'm trying to access the network printers by doing impersonation (using this code) to print a pdf document from IIS (wcf application). Since my wcf application run under 'LocalSystem' app pool identity, I need to do the impersonation. But Printing doesn't work with the impersonation but I can list out the network printers successfully.

    I'm using the logonType as 'LOGON32_LOGON_INTERACTIVE' and logonProvider as 'LOGON32_PROVIDER_DEFAULT' which allows me to list out the network printers but when I send the document to the printer it doesn't print. it shows with Size empty in the print queue. Any Idea please???
    My 
    Impersonation Code:

    [PermissionSet(SecurityAction.Demand, Name = "FullTrust")]
    public class Impersonation : IDisposable
    {
    private readonly SafeTokenHandle _handle;
    private readonly WindowsImpersonationContext _context;
    bool disposed = false;
    
        // constants from winbase.h
        const int LOGON32_LOGON_INTERACTIVE = 2;
        const int LOGON32_LOGON_NETWORK = 3;
        const int LOGON32_LOGON_BATCH = 4;
        const int LOGON32_LOGON_SERVICE = 5;
        const int LOGON32_LOGON_UNLOCK = 7;
        const int LOGON32_LOGON_NETWORK_CLEARTEXT = 8;
        const int LOGON32_LOGON_NEW_CREDENTIALS = 9;
    
        const int LOGON32_PROVIDER_DEFAULT = 0;
        const int LOGON32_PROVIDER_WINNT35 = 1;
        const int LOGON32_PROVIDER_WINNT40 = 2;
        const int LOGON32_PROVIDER_WINNT50 = 3;
    
        public Impersonation(ImpersonateUserDetails user) : this(user.Domain, user.UserName, user.Password)
        { }
        public Impersonation(string domain, string username, string password)
        {
            // if domain name was blank, assume local machine
            if (string.IsNullOrEmpty(domain))
                domain = System.Environment.MachineName;
    
            var ok = LogonUser(username, domain, password,
                           LOGON32_LOGON_INTERACTIVE, LOGON32_PROVIDER_DEFAULT, out this._handle);
            if (!ok)
            {
                var errorCode = Marshal.GetLastWin32Error();
                throw new ApplicationException(string.Format("Could not impersonate the elevated user.  LogonUser returned error code {0}.", errorCode));
            }
    
            this._context = WindowsIdentity.Impersonate(this._handle.DangerousGetHandle());
        }
    
        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }
    
        protected virtual void Dispose(bool disposing)
        {
            if (disposed)
                return;
    
            if (disposing)
            {
                this._context.Dispose();
                this._handle.Dispose();
            }           
            disposed = true;
        }
    
        ~Impersonation()
        {
            Dispose(false);
        }
    
    
        [DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
        private static extern bool LogonUser(String lpszUsername, String lpszDomain, String lpszPassword, int dwLogonType, int dwLogonProvider, out SafeTokenHandle phToken);
    
        sealed class SafeTokenHandle : SafeHandleZeroOrMinusOneIsInvalid
        {
            private SafeTokenHandle()
                : base(true) { }
    
            [DllImport("kernel32.dll")]
            [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
            [SuppressUnmanagedCodeSecurity]
            [return: MarshalAs(UnmanagedType.Bool)]
            private static extern bool CloseHandle(IntPtr handle);
    
            protected override bool ReleaseHandle()
            {
                return CloseHandle(handle);
            }
        }
    }
    
    public class ImpersonateUserDetails
    {
        public string UserName { get; set; }
    
        public string Password { get; set; }
    
        public string Domain { get; set; }
    }

    Printing code to Network Printer:

    using (new Impersonation(domain, username, password)) { PrinterUtility pu = new PrinterUtility(); pu.Print(fileName, "TestFile.pdf", printerName); 
    }

    PrinterUtility Class:

    using System;
    using System.Drawing.Printing;
    using System.IO;
    using System.Drawing;
    using System.Text;
    
    namespace NetworkPrintWCF
    {
    public class PrinterUtility
    {
    private Font printFont;
    private StreamReader streamToPrint;
    
        private void pd_PrintPage(object sender, PrintPageEventArgs ev)
        {
            float linesPerPage = 0;
            float yPos = 0;
            int count = 0;
            float leftMargin = ev.MarginBounds.Left;
            float topMargin = ev.MarginBounds.Top;
            String line = null;
    
            // Calculate the number of lines per page.
            linesPerPage = ev.MarginBounds.Height /
               printFont.GetHeight(ev.Graphics);
    
            // Iterate over the file, printing each line.
            while (count < linesPerPage &&
               ((line = streamToPrint.ReadLine()) != null))
            {
                yPos = topMargin + (count * printFont.GetHeight(ev.Graphics));
                ev.Graphics.DrawString(line, printFont, Brushes.Black,
                   leftMargin, yPos, new StringFormat());
                count++;
            }
    
            // If more lines exist, print another page.
            if (line != null)
                ev.HasMorePages = true;
            else
                ev.HasMorePages = false;
        }
    
        // Print the file.
        public void Print(string filePath, string fileName, string printerNetworkPath)
        {
            try
            {
                streamToPrint = new StreamReader(filePath);
                try
                {
                    var printerSettings = new PrinterSettings
                    {
                        PrinterName = printerNetworkPath,
                        PrintFileName = fileName,
                        PrintRange = PrintRange.AllPages,
                    };
                    printerSettings.DefaultPageSettings.Margins = new Margins(0, 0, 0, 0);
    
                    printFont = new Font("Arial", 10);
                    PrintDocument pd = new PrintDocument();
                    pd.DocumentName = fileName;
                    pd.PrinterSettings = printerSettings;
                    //pd.PrintController = new StandardPrintController();
                    pd.PrintPage += new PrintPageEventHandler(pd_PrintPage);
                    // Print the document.
                    pd.Print();
                }
                finally
                {
                    streamToPrint.Close();
                }
            }
            catch
            {
                throw;
            }
        }
    }
    }

    I'm sure this is the problem with impersonation. because If I change my application's app pool identity to a domain user (same domain user credential I was trying to use to impersonate) then printing works fine.

    Web Server Environment: Windows Server 2012 R2, IIS 8.5.

    But with this impersonationated code, printing fails. Document sows up in print queue with NO size (kindly refer attached image)




    • Edited by Binod Kr Mahto Thursday, June 1, 2017 1:38 PM needs correction.
    Tuesday, May 30, 2017 7:37 AM

All replies

  • Hi Binod,

    Please check if your IIS Application pool is 64bit, if you use 32bit, please use 'LOGON32_LOGON_NEW_CREDENTIALS' instead of 'LOGON32_LOGON_INTERACTIVE'

    For more information, please refer to:

    https://blogs.msdn.microsoft.com/winsdk/2015/05/19/printing-successfully-using-impersonation-from-a-32-bit-application-on-a-64-bit-system/

    Best regards,

    Cole Wu


    MSDN Community Support
    Please remember to click "Mark as Answer" the responses that resolved your issue, and to click "Unmark as Answer" if not. This can be beneficial to other community members reading this thread. If you have any compliments or complaints to MSDN Support, feel free to contact MSDNFSF@microsoft.com.

    Wednesday, May 31, 2017 5:58 AM
    Moderator
  • I have been through the link you provided and Yes, my IIS application pool is 64 bit.
    Wednesday, May 31, 2017 9:06 AM
  • Hi BinodKrMahto,

    >>I have been through thelink you provided and Yes, my IIS application pool is 64 bit.

    It seems that the printer does not receive the document content and cache it in printer, please check the network and firewall and printer settings, which prevent the document transfer.

    Best regards,

    Cole Wu


    MSDN Community Support
    Please remember to click "Mark as Answer" the responses that resolved your issue, and to click "Unmark as Answer" if not. This can be beneficial to other community members reading this thread. If you have any compliments or complaints to MSDN Support, feel free to contact MSDNFSF@microsoft.com.

    Monday, June 5, 2017 9:06 AM
    Moderator
  • Same credentials (which I use for impersonation), If I set it to application pool identity (custom) then print works nicely. Now If I change application pool identity as 'LocalSystem' (or any other inbuilt system account) and call the printing related code under elevated user (I mean, doing the impersonation) then printing fails.

    With the same credentials if it fails in one scenario and passes in others. doesn't it mean that we don't have problem with network firewall or printer settings? Because printing through IIS passes successfully without impersonation (by setting application pool identity to domain user). 
    If we call the printing logic under impersonation, what change it makes?

    In the printer side, for connectivity, except Netware, NTP, POP3, Proxy Server, SLP & SSDP, these protocols rest all are enabled.

    Wednesday, June 7, 2017 9:14 AM