printdlgexmarshaler.cs source code in C# .NET

Source code for the .NET framework in C#

                        

Code:

/ Net / Net / 3.5.50727.3053 / DEVDIV / depot / DevDiv / releases / Orcas / SP / wpf / src / Framework / MS / Internal / Printing / printdlgexmarshaler.cs / 1 / printdlgexmarshaler.cs

                            //  Microsoft Avalon 
//  Copyright (c) Microsoft Corporation, 2006
//
//  File:       Win32PrintDialog.cs
// 
//  02/17/2006 : robertan - created
// 
//------------------------------------------------------------------------------ 

using System; 
using System.Drawing.Printing;
using System.Printing.Interop;
using System.Printing;
using System.Runtime.InteropServices; 
using System.Security;
using System.Security.Permissions; 
using System.Windows.Controls; 

namespace MS.Internal.Printing 
{
    internal partial class Win32PrintDialog
    {
        ///  
        /// This class abstracts the Marshaling of the PrintDlgEx structure to/from
        /// Native memory for displaying the Win32 print dialog via the PrintDlgEx 
        /// API.  The reason for this abstraction is to handle a known issue with 
        /// this API.  The 32-bit and 64-bit versions of this method have different
        /// packing schemes for the input structure that need to be handled separately 
        /// to function correctly depending on the CPU archetecture.
        /// 
        private sealed class PrintDlgExMarshaler : IDisposable
        { 
            #region Constructor
 
            ///  
            /// Construct and initialize a PrintDlgExMarshaler instance.
            ///  
            /// 
            /// A Win32 window handle to use as the parent of this dialog.
            /// 
            ///  
            /// A reference to the Win32PrintDialog that contains the necessary
            /// data to display a Win32 Print Dialog via the PrintDlgEx call. 
            ///  
            /// 
            ///     Critical:    - Sets critical data to default values 
            ///                  - We want creation of this class to be tracked.
            /// 
            [SecurityCritical]
            internal 
            PrintDlgExMarshaler(
                IntPtr owner, 
                Win32PrintDialog dialog 
                )
            { 
                _ownerHandle = owner;
                _dialog = dialog;
                _unmanagedPrintDlgEx = IntPtr.Zero;
            } 

            #endregion Constructor 
 
            #region Finalizer
 
            ~PrintDlgExMarshaler()
            {
                this.Dispose(true);
            } 

            #endregion Finalizer 
 
            #region Internal properties
 
            /// 
            /// Gets an IntPtr that points to unmanaged memory that represents
            /// a PRINTDLGEX structure for calling into PrintDlgEx Win32 API.
            ///  
            /// 
            ///     Critical:    - This exposes critical unmanaged memory buffer 
            ///                    and should be treated as dangerous since it 
            ///                    is being passed to managed code.
            ///  
            internal IntPtr UnmanagedPrintDlgEx
            {
                [SecurityCritical]
                get 
                {
                    return _unmanagedPrintDlgEx; 
                } 
            }
 
            #endregion Internal properties

            #region Internal methods
 
            /// 
            /// This method synchronizes the internal PRINTDLGEX unmanaged data 
            /// structure with the internal Win32 print dialog configuration 
            /// parameters.  You call this prior to a call to PrintDlgEx Win32
            /// API to configure the unmanaged memory. 
            /// 
            /// 
            ///     Critical:    - Calls other critical code (ExtractPrintDataAndDevMode,
            ///                    AcquireResultFromPrintDlgExStruct, AcquirePrintQueue, 
            ///                    and AcquirePrintTicket).
            ///                  - Sets critical data on the Win32PrintDialog class 
            ///                    (PrintTicket and PrintQueue) 
            /// 
            [SecurityCritical] 
            internal
            UInt32
            SyncFromStruct()
            { 
                if (_unmanagedPrintDlgEx == IntPtr.Zero)
                { 
                    return NativeMethods.PD_RESULT_CANCEL; 
                }
 
                UInt32 dialogResult = AcquireResultFromPrintDlgExStruct(_unmanagedPrintDlgEx);

                if ((dialogResult == NativeMethods.PD_RESULT_PRINT) ||
                     (dialogResult == NativeMethods.PD_RESULT_APPLY)) 
                {
                    IntPtr devModeHandle; 
                    string printerName; 
                    UInt32 flags;
                    PageRange pageRange; 

                    ExtractPrintDataAndDevMode(
                        _unmanagedPrintDlgEx,
                        out printerName, 
                        out flags,
                        out pageRange, 
                        out devModeHandle); 

                    _dialog.PrintQueue = AcquirePrintQueue(printerName); 
                    _dialog.PrintTicket = AcquirePrintTicket(devModeHandle, printerName);

                    if ((flags & NativeMethods.PD_PAGENUMS) == NativeMethods.PD_PAGENUMS)
                    { 
                        if (pageRange.PageFrom > pageRange.PageTo)
                        { 
                            int temp = pageRange.PageTo; 
                            pageRange.PageTo = pageRange.PageFrom;
                            pageRange.PageFrom = temp; 
                        }

                        _dialog.PageRangeSelection = PageRangeSelection.UserPages;
                        _dialog.PageRange = pageRange; 
                    }
                    else 
                    { 
                        _dialog.PageRangeSelection = PageRangeSelection.AllPages;
                    } 
                }

                return dialogResult;
            } 

            ///  
            /// This method synchronizes the managed Win32 data with the PRINTDLGEX 
            /// unmanaged data structure.  It is used after a successful call to the
            /// PrintDlgEx Win32 API. 
            /// 
            /// 
            ///     Critical:    - Calls into critical code (FreeUnmanagedPrintDlgExStruct,
            ///                    GetDesktopWindow, and AllocateUnmanagedPrintDlgExStruct). 
            /// 
            [SecurityCritical] 
            internal 
            void
            SyncToStruct() 
            {
                if (_unmanagedPrintDlgEx != IntPtr.Zero)
                {
                    FreeUnmanagedPrintDlgExStruct(_unmanagedPrintDlgEx); 
                }
 
                // 
                // If parent is not valid then get the desktop window handle.
                // 
                if (_ownerHandle == IntPtr.Zero)
                {
                    _ownerHandle = MS.Win32.UnsafeNativeMethods.GetDesktopWindow();
                } 

                // 
                // Allocate an unmanaged PRINTDLGEX structure with our current internal settings. 
                //
                _unmanagedPrintDlgEx = AllocateUnmanagedPrintDlgExStruct(); 
            }

            #endregion Internal methods
 
            #region Private helper methods
 
            ///  
            /// Clean up any resources being used.
            ///  
            /// 
            /// true if managed resources should be disposed; otherwise, false.
            /// 
            ///  
            ///     Critical     - Accesses unmanaged critical pointer
            ///     TreatAsSafe  - Its only freeing the memory and nothing leaves this method. 
            ///  
            [SecurityCritical, SecurityTreatAsSafe]
            private 
            void
            Dispose(
                bool disposing
                ) 
            {
                if (disposing) 
                { 
                    if (_unmanagedPrintDlgEx != IntPtr.Zero)
                    { 
                        FreeUnmanagedPrintDlgExStruct(_unmanagedPrintDlgEx);
                        _unmanagedPrintDlgEx = IntPtr.Zero;
                    }
                } 
            }
 
            ///  
            /// Extracts the printer name, flags, pageRange, and devmode handle from the
            /// given PRINTDLGEX structure. 
            /// 
            /// 
            /// An unmanaged buffer representing the PRINTDLGEX structure.  This structure
            /// is passed around as an unmanaged buffer since the 32-bit and 64-bit versions 
            /// of this buffer are not the same and need to be handled uniquely.
            ///  
            ///  
            /// An out parameter to store store the printer name.
            ///  
            /// 
            /// The set of flags that are inside the PRINTDIALOGEX structure.
            /// 
            ///  
            /// The  user specified page range that is in PRINTDLGEX structure.
            /// This is only useful if the PD_PAGENUMS bit is set in the flags. 
            ///  
            /// 
            /// An out parameter to store the devmode handle. 
            /// 
            /// 
            ///     Critical:    - manipulating unmanaged buffers with critical data in them.
            ///  
            [SecurityCritical]
            private 
            void 
            ExtractPrintDataAndDevMode(
                IntPtr unmanagedBuffer, 
                out string printerName,
                out UInt32 flags,
                out PageRange pageRange,
                out IntPtr devModeHandle 
                )
            { 
                IntPtr devNamesHandle = IntPtr.Zero; 
                IntPtr pageRangePtr = IntPtr.Zero;
 
                //
                // Extract the devmode and devnames handles from the appropriate PRINTDLGEX structure
                //
                if (!Is64Bit()) 
                {
                    NativeMethods.PRINTDLGEX32 pdex = (NativeMethods.PRINTDLGEX32)Marshal.PtrToStructure( 
                        unmanagedBuffer, 
                        typeof(NativeMethods.PRINTDLGEX32));
                    devModeHandle = pdex.hDevMode; 
                    devNamesHandle = pdex.hDevNames;
                    flags = pdex.Flags;
                    pageRangePtr = pdex.lpPageRanges;
                } 
                else
                { 
                    NativeMethods.PRINTDLGEX64 pdex = (NativeMethods.PRINTDLGEX64)Marshal.PtrToStructure( 
                        unmanagedBuffer,
                        typeof(NativeMethods.PRINTDLGEX64)); 
                    devModeHandle = pdex.hDevMode;
                    devNamesHandle = pdex.hDevNames;
                    flags = pdex.Flags;
                    pageRangePtr = pdex.lpPageRanges; 
                }
 
                // 
                // Get a managed copy of the page ranges.  This only matters if the PD_PAGENUMS bit is
                // set in the flags. 
                //
                if (((flags & NativeMethods.PD_PAGENUMS) == NativeMethods.PD_PAGENUMS) &&
                     (pageRangePtr != IntPtr.Zero))
                { 
                    NativeMethods.PRINTPAGERANGE pageRangeStruct = (NativeMethods.PRINTPAGERANGE)Marshal.PtrToStructure(
                            pageRangePtr, 
                            typeof(NativeMethods.PRINTPAGERANGE)); 

                    pageRange = new PageRange((int)pageRangeStruct.nFromPage, (int)pageRangeStruct.nToPage); 
                }
                else
                {
                    pageRange = new PageRange(1); 
                }
 
                // 
                // Get a managed copy of the device name
                // 
                if (devNamesHandle != IntPtr.Zero)
                {
                    IntPtr pDevNames = IntPtr.Zero;
                    try 
                    {
                        pDevNames = UnsafeNativeMethods.GlobalLock(devNamesHandle); 
 
                        NativeMethods.DEVNAMES devNames = (NativeMethods.DEVNAMES)Marshal.PtrToStructure(
                            pDevNames, 
                            typeof(NativeMethods.DEVNAMES));
                        printerName = Marshal.PtrToStringAuto(
                            (IntPtr)((int)pDevNames + (devNames.wDeviceOffset * Marshal.SystemDefaultCharSize)));
                    } 
                    finally
                    { 
                        if (pDevNames != IntPtr.Zero) 
                        {
                            UnsafeNativeMethods.GlobalUnlock(devNamesHandle); 
                        }
                    }
                }
                else 
                {
                    printerName = string.Empty; 
                } 
            }
 
            /// 
            /// Acquires an instance of the PrintQueue that cooresponds to the given printer name.
            /// 
            ///  
            /// The printer name to search for.
            ///  
            ///  
            ///     Critical:   - Performs an elevation to access the printing subsystem to lookup
            ///                   the PrintQueue based on the printername. 
            /// 
            [SecurityCritical]
            private
            PrintQueue 
            AcquirePrintQueue(
                string printerName 
                ) 
            {
                PrintQueue printQueue = null; 

                EnumeratedPrintQueueTypes[] types = new EnumeratedPrintQueueTypes[] {
                    EnumeratedPrintQueueTypes.Local,
                    EnumeratedPrintQueueTypes.Connections 
                };
 
                // 
                // This forces us to acquire the cached version of the print queues.
                // This theoretically should prevent crashing in the printing system 
                // since all it is doing is reading the registry.
                //
                PrintQueueIndexedProperty[] props = new PrintQueueIndexedProperty[] {
                    PrintQueueIndexedProperty.Name, 
                    PrintQueueIndexedProperty.QueueAttributes
                }; 
 
                (new PrintingPermission(PrintingPermissionLevel.DefaultPrinting)).Assert();  //BlessedAssert
                try 
                {
                    //
                    // Get the PrintQueue instance for the printer
                    // 
                    using (LocalPrintServer server = new LocalPrintServer())
                    { 
                        foreach (PrintQueue queue in server.GetPrintQueues(props, types)) 
                        {
                            if (printerName.Equals(queue.FullName, StringComparison.OrdinalIgnoreCase)) 
                            {
                                printQueue = queue;
                                break;
                            } 
                        }
                    } 
                    if (printQueue != null) 
                    {
                        printQueue.InPartialTrust = true; 
                    }
                }
                finally
                { 
                    PrintingPermission.RevertAssert();
                } 
 
                return printQueue;
            } 

            /// 
            /// Acquires an instance of a PrintTicket given a handle to a DEVMODE and a printer name.
            ///  
            /// 
            /// The DEVMODE handle to use for the PrintTicket. 
            ///  
            /// 
            /// The printer name for the PrintTicket converter. 
            /// 
            /// 
            ///     Critical:   - Performs an elevation to access the printing subsystem to lookup
            ///                   the PrintQueue based on the printer in the PRINTDLG structure. 
            ///                 - Calls unmanaged code that has been suppressed.
            ///  
            [SecurityCritical] 
            private
            PrintTicket 
            AcquirePrintTicket(
                IntPtr devModeHandle,
                string printQueueName
                ) 
            {
                PrintTicket printTicket = null; 
                byte[] devModeData = null; 

                // 
                // Copy the devmode into a byte array
                //
                IntPtr pDevMode = IntPtr.Zero;
                try 
                {
                    pDevMode = UnsafeNativeMethods.GlobalLock(devModeHandle); 
 
                    NativeMethods.DEVMODE devMode = (NativeMethods.DEVMODE)Marshal.PtrToStructure(
                        pDevMode, 
                        typeof(NativeMethods.DEVMODE));
                    devModeData = new byte[devMode.dmSize + devMode.dmDriverExtra];
                    Marshal.Copy(pDevMode, devModeData, 0, devModeData.Length);
                } 
                finally
                { 
                    if (pDevMode != IntPtr.Zero) 
                    {
                        UnsafeNativeMethods.GlobalUnlock(devModeHandle); 
                    }
                }

                (new PrintingPermission(PrintingPermissionLevel.DefaultPrinting)).Assert(); //BlessedAssert 
                try
                { 
                    // 
                    // Convert the devmode data to a PrintTicket object
                    // 
                    using (PrintTicketConverter ptConverter = new PrintTicketConverter(
                                printQueueName,
                                PrintTicketConverter.MaxPrintSchemaVersion))
                    { 
                        printTicket = ptConverter.ConvertDevModeToPrintTicket(devModeData);
                    } 
                } 
                finally
                { 
                    PrintingPermission.RevertAssert();
                }

                return printTicket; 
            }
 
            ///  
            /// Extracts the result value from a given PRINTDLGEX structure.
            ///  
            /// 
            /// An unmanaged buffer representing the PRINTDLGEX structure.  This structure
            /// is passed around as an unmanaged buffer since the 32-bit and 64-bit versions
            /// of this buffer are not the same and need to be handled uniquely. 
            /// 
            ///  
            ///     Critical:    - manipulating unmanaged buffers with critical data in them. 
            /// 
            [SecurityCritical] 
            private
            UInt32
            AcquireResultFromPrintDlgExStruct(
                IntPtr unmanagedBuffer 
                )
            { 
                UInt32 result = 0; 

                // 
                // Extract the devmode and devnames handles from the appropriate PRINTDLGEX structure
                //
                if (!Is64Bit())
                { 
                    NativeMethods.PRINTDLGEX32 pdex = (NativeMethods.PRINTDLGEX32)Marshal.PtrToStructure(
                        unmanagedBuffer, 
                        typeof(NativeMethods.PRINTDLGEX32)); 
                    result = pdex.dwResultAction;
                } 
                else
                {
                    NativeMethods.PRINTDLGEX64 pdex = (NativeMethods.PRINTDLGEX64)Marshal.PtrToStructure(
                        unmanagedBuffer, 
                        typeof(NativeMethods.PRINTDLGEX64));
                    result = pdex.dwResultAction; 
                } 

                return result; 
            }

            /// 
            /// Allocates an unmanaged buffer for the PRINTDLGEX structure and 
            /// sets the initial values on it.
            /// 
            /// NOTE:  The reason for returning an unmanaged pointer to a buffer 
            /// and not just a structure that can be passed to the unmanaged APIs
            /// is because this class is providing an abstraction to a problem that 
            /// exists in the unmanaged print world where the 32-bit and 64-bit
            /// structure packing is inconsistent.
            /// 
            ///  
            ///     Critical:    - Allocates and returns unmanaged memory that will
            ///                    eventually contain possibly sensitive data. 
            ///  
            [SecurityCritical]
            private 
            IntPtr
            AllocateUnmanagedPrintDlgExStruct()
            {
                IntPtr unmanagedBuffer = IntPtr.Zero; 

                NativeMethods.PRINTPAGERANGE range; 
                range.nToPage = (uint)_dialog.PageRange.PageTo; 
                range.nFromPage = (uint)_dialog.PageRange.PageFrom;
 
                try
                {
                    if (!Is64Bit())
                    { 
                        NativeMethods.PRINTDLGEX32 pdex = new NativeMethods.PRINTDLGEX32();
                        pdex.hwndOwner = _ownerHandle; 
                        pdex.nMinPage = _dialog.MinPage; 
                        pdex.nMaxPage = _dialog.MaxPage;
                        pdex.Flags = 
                            NativeMethods.PD_ALLPAGES |
                            NativeMethods.PD_NOCURRENTPAGE |
                            NativeMethods.PD_NOSELECTION |
                            NativeMethods.PD_USEDEVMODECOPIESANDCOLLATE | 
                            NativeMethods.PD_DISABLEPRINTTOFILE |
                            NativeMethods.PD_HIDEPRINTTOFILE; 
 
                        if (_dialog.PageRangeEnabled)
                        { 
                            pdex.lpPageRanges = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(NativeMethods.PRINTPAGERANGE)));
                            pdex.nMaxPageRanges = 1;

                            if (_dialog.PageRangeSelection == PageRangeSelection.UserPages) 
                            {
                                pdex.nPageRanges = 1; 
                                Marshal.StructureToPtr(range, pdex.lpPageRanges, false); 
                                pdex.Flags |= NativeMethods.PD_PAGENUMS;
                            } 
                            else
                            {
                                pdex.nPageRanges = 0;
                            } 
                        }
                        else 
                        { 
                            pdex.lpPageRanges = IntPtr.Zero;
                            pdex.nMaxPageRanges = 0; 
                            pdex.Flags |= NativeMethods.PD_NOPAGENUMS;
                        }

                        // 
                        // If we know a print queue and print ticket, then we need to try to select
                        // the printer in the dialog and use the print ticket.  We allocate and setup 
                        // a DEVNAMES structure for this as well as convert the PrintTicket to a 
                        // DEVMODE.
                        // 
                        if (_dialog.PrintQueue != null)
                        {
                            pdex.hDevNames = AllocateAndInitializeDevNames(_dialog.PrintQueue.FullName);
 
                            if (_dialog.PrintTicket != null)
                            { 
                                pdex.hDevMode = AllocateAndInitializeDevMode( 
                                    _dialog.PrintQueue.FullName,
                                    _dialog.PrintTicket); 
                            }
                        }

                        int cbBufferSize = Marshal.SizeOf(typeof(NativeMethods.PRINTDLGEX32)); 
                        unmanagedBuffer = Marshal.AllocHGlobal(cbBufferSize);
                        Marshal.StructureToPtr(pdex, unmanagedBuffer, false); 
                    } 
                    else
                    { 
                        NativeMethods.PRINTDLGEX64 pdex = new NativeMethods.PRINTDLGEX64();
                        pdex.hwndOwner = _ownerHandle;
                        pdex.nMinPage = _dialog.MinPage;
                        pdex.nMaxPage = _dialog.MaxPage; 
                        pdex.Flags =
                            NativeMethods.PD_ALLPAGES | 
                            NativeMethods.PD_NOCURRENTPAGE | 
                            NativeMethods.PD_NOSELECTION |
                            NativeMethods.PD_USEDEVMODECOPIESANDCOLLATE | 
                            NativeMethods.PD_DISABLEPRINTTOFILE |
                            NativeMethods.PD_HIDEPRINTTOFILE;

                        if (_dialog.PageRangeEnabled) 
                        {
                            pdex.lpPageRanges = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(NativeMethods.PRINTPAGERANGE))); 
                            pdex.nMaxPageRanges = 1; 

                            if (_dialog.PageRangeSelection == PageRangeSelection.UserPages) 
                            {
                                pdex.nPageRanges = 1;
                                Marshal.StructureToPtr(range, pdex.lpPageRanges, false);
                                pdex.Flags |= NativeMethods.PD_PAGENUMS; 
                            }
                            else 
                            { 
                                pdex.nPageRanges = 0;
                            } 
                        }
                        else
                        {
                            pdex.lpPageRanges = IntPtr.Zero; 
                            pdex.nMaxPageRanges = 0;
                            pdex.Flags |= NativeMethods.PD_NOPAGENUMS; 
                        } 

                        // 
                        // If we know a print queue and print ticket, then we need to try to select
                        // the printer in the dialog and use the print ticket.  We allocate and setup
                        // a DEVNAMES structure for this as well as convert the PrintTicket to a
                        // DEVMODE. 
                        //
                        if (_dialog.PrintQueue != null) 
                        { 
                            pdex.hDevNames = AllocateAndInitializeDevNames(_dialog.PrintQueue.FullName);
 
                            if (_dialog.PrintTicket != null)
                            {
                                pdex.hDevMode = AllocateAndInitializeDevMode(
                                    _dialog.PrintQueue.FullName, 
                                    _dialog.PrintTicket);
                            } 
                        } 

                        int cbBufferSize = Marshal.SizeOf(typeof(NativeMethods.PRINTDLGEX64)); 
                        unmanagedBuffer = Marshal.AllocHGlobal(cbBufferSize);
                        Marshal.StructureToPtr(pdex, unmanagedBuffer, false);
                    }
                } 
                catch (Exception)
                { 
                    if (unmanagedBuffer != null) 
                    {
                        FreeUnmanagedPrintDlgExStruct(unmanagedBuffer); 
                        unmanagedBuffer = IntPtr.Zero;
                    }

                    throw; 
                }
 
                return unmanagedBuffer; 
            }
 
            /// 
            /// Frees an unmanaged buffer.
            /// 
            ///  
            /// An unmanaged buffer representing the PRINTDLGEX structure.  This structure
            /// is passed around as an unmanaged buffer since the 32-bit and 64-bit versions 
            /// of this buffer are not the same and need to be handled uniquely. 
            /// 
            ///  
            ///     Critical:    - Frees an unmanaged buffer with potentially sensitive data.
            /// 
            [SecurityCritical]
            private 
            void
            FreeUnmanagedPrintDlgExStruct( 
                IntPtr unmanagedBuffer 
                )
            { 
                if (unmanagedBuffer == IntPtr.Zero)
                {
                    return;
                } 

                IntPtr devModeHandle = IntPtr.Zero; 
                IntPtr devNamesHandle = IntPtr.Zero; 
                IntPtr pageRangePtr = IntPtr.Zero;
 
                //
                // Extract the devmode and devnames handles from the appropriate PRINTDLGEX structure
                //
                if (!Is64Bit()) 
                {
                    NativeMethods.PRINTDLGEX32 pdex = (NativeMethods.PRINTDLGEX32)Marshal.PtrToStructure( 
                        unmanagedBuffer, 
                        typeof(NativeMethods.PRINTDLGEX32));
                    devModeHandle = pdex.hDevMode; 
                    devNamesHandle = pdex.hDevNames;
                    pageRangePtr = pdex.lpPageRanges;
                }
                else 
                {
                    NativeMethods.PRINTDLGEX64 pdex = (NativeMethods.PRINTDLGEX64)Marshal.PtrToStructure( 
                        unmanagedBuffer, 
                        typeof(NativeMethods.PRINTDLGEX64));
                    devModeHandle = pdex.hDevMode; 
                    devNamesHandle = pdex.hDevNames;
                    pageRangePtr = pdex.lpPageRanges;
                }
 
                if (devModeHandle != IntPtr.Zero)
                { 
                    UnsafeNativeMethods.GlobalFree(devModeHandle); 
                }
 
                if (devNamesHandle != IntPtr.Zero)
                {
                    UnsafeNativeMethods.GlobalFree(devNamesHandle);
                } 

                if (pageRangePtr != IntPtr.Zero) 
                { 
                    UnsafeNativeMethods.GlobalFree(pageRangePtr);
                } 

                Marshal.FreeHGlobal(unmanagedBuffer);
            }
 
            /// 
            /// Returns a boolean value representing whether the current runtime is 
            /// 32-bit or 64-bit. 
            /// 
            ///  
            ///     Critical:    - Marshal.SizeOf LinkDemands
            ///     TreatAsSafe: - This method returns nothing that is unsafe.
            /// 
            [SecurityCritical, SecurityTreatAsSafe] 
            private
            bool 
            Is64Bit() 
            {
                IntPtr temp = IntPtr.Zero; 
                return Marshal.SizeOf(temp) == 8;
            }

            ///  
            /// This method allocates a DEVNAMES structure in unmanaged code and configures
            /// it to point to the specified printer. 
            ///  
            /// 
            /// The printer name to use for the DEVNAMES structure. 
            /// 
            /// 
            /// Returns an IntPtr pointing to a memory address in unmanaged code where
            /// the structure has been initialized. 
            /// 
            ///  
            ///     Critical:    - Allocates and manipulates unmanaged memory. 
            /// 
            [SecurityCritical] 
            private
            IntPtr
            AllocateAndInitializeDevNames(
                string printerName 
                )
            { 
                IntPtr hDevNames = IntPtr.Zero; 
                char[] printer = printerName.ToCharArray();
 
                //
                // Enough memory for the printer name character array + 3 null characters
                // + the DEVNAMES structure.  The 3 null characters are for the end of the
                // printer name string, and 2 distinct empty strings. 
                //
                int cbDevNames = ((printer.Length + 3) * Marshal.SystemDefaultCharSize) + 
                    Marshal.SizeOf(typeof(NativeMethods.DEVNAMES)); 
                hDevNames = Marshal.AllocHGlobal(cbDevNames);
 
                ushort baseOffset = (ushort)Marshal.SizeOf(typeof(NativeMethods.DEVNAMES));

                //
                // The wDeviceOffset contains an offset in characters to the printer name 
                // within the DEVNAMES structure.  The wDriverOffset and wOutputOffset
                // are offsets to two distinct empty strings that just happen to follow 
                // the printer name string.  For more information on how this structure is 
                // layed out, please review the MSDN documentation for DEVNAMES structure.
                // 
                NativeMethods.DEVNAMES devNames;
                devNames.wDeviceOffset = (ushort)(baseOffset / Marshal.SystemDefaultCharSize);
                devNames.wDriverOffset = (ushort)(devNames.wDeviceOffset + printer.Length + 1);
                devNames.wOutputOffset = (ushort)(devNames.wDriverOffset + 1); 
                devNames.wDefault = 0;
                Marshal.StructureToPtr(devNames, hDevNames, false); 
 
                //
                // Calculate the position of the string containing the printer name 
                //   - Printer name character array immediately follows the DEVNAMES structure
                //   - followed by 3 NULL characters
                //
                IntPtr offsetName = 
                    (IntPtr)((long)hDevNames + (long)baseOffset);
                IntPtr offsetNull = 
                    (IntPtr)((long)offsetName + (printer.Length * Marshal.SystemDefaultCharSize)); 

                // 
                // Write the printer name and 3 NULL characters.  The first NULL character
                // is to null terminate our printer name string.  The second and third null
                // characters are to create to distinct empty strings within the buffer.
                // 
                // NOTE: The byte array contains the NULL characters in a form that we are
                // able to write them out.  There are 3 * char size bytes total. 
                // 
                byte[] nulls = new byte[3 * Marshal.SystemDefaultCharSize];
                Array.Clear(nulls, 0, nulls.Length); 
                Marshal.Copy(
                    printer,
                    0,
                    offsetName, 
                    printer.Length);
                Marshal.Copy( 
                    nulls, 
                    0,
                    offsetNull, 
                    nulls.Length);

                return hDevNames;
            } 

            ///  
            /// This method allocates and initializes an unmanaged DEVMODE structure 
            /// for use to by the PrintDlgEx method based on the specified printer
            /// and PrintTicket object. 
            /// 
            /// 
            /// The printer that is being targetted by the PrintTicket.
            ///  
            /// 
            /// The PrintTicket that will be converted to a DEVMODE. 
            ///  
            /// 
            /// An unmanaged pointer to an unmanaged DEVMODE structure that can be 
            /// used in the PRINTDLGEX structure for a call to PrintDlgEx.
            /// 
            /// 
            ///     Critical:    - Allocates and manipulates unmanaged memory. 
            ///                  - Asserts for DefaultPrinting permissions to be
            ///                    able to successfully convert the PrintTicket 
            ///                    object to a DEVMODE via the PrintTicketConverter. 
            /// 
            [SecurityCritical] 
            private
            IntPtr
            AllocateAndInitializeDevMode(
                string printerName, 
                PrintTicket printTicket
                ) 
            { 
                byte[] devModeData = null;
 
                (new PrintingPermission(PrintingPermissionLevel.DefaultPrinting)).Assert();  //BlessedAssert
                try
                {
                    // 
                    // Convert the PrintTicket object to a DEVMODE
                    // 
                    using (PrintTicketConverter ptConverter = new PrintTicketConverter( 
                                printerName,
                                PrintTicketConverter.MaxPrintSchemaVersion)) 
                    {
                        devModeData = ptConverter.ConvertPrintTicketToDevMode(
                            printTicket,
                            BaseDevModeType.UserDefault); 
                    }
                } 
                finally 
                {
                    PrintingPermission.RevertAssert(); 
                }

                //
                // Make the dev mode data a DEVMODE structure in global memory 
                //
                IntPtr hDevMode = Marshal.AllocHGlobal(devModeData.Length); 
                Marshal.Copy(devModeData, 0, hDevMode, devModeData.Length); 

                return hDevMode; 
            }

            #endregion Private helper methods
 
            #region Private data
 
            private 
            Win32PrintDialog _dialog;
 
            /// 
            ///     Critical: This is a pointer to unmanaged memory containing
            ///               an unmanaged PRINTDLGEX structure.
            ///  
            [SecurityCritical]
            private 
            IntPtr _unmanagedPrintDlgEx; 

            ///  
            ///     Critical: This is a handle to a window that will be used
            ///               as the parent for the Win32 print dialog.
            /// 
            [SecurityCritical] 
            private
            IntPtr _ownerHandle; 
 
            #endregion Private data
 
            #region IDisposable implementation

            /// 
            /// Implements the Dispose method for IDisposable.  This ensures that 
            /// any unmanaged PRINTDLGEX structure that was allocated by the class
            /// will be property freed when this class goes away. 
            ///  
            public
            void 
            Dispose()
            {
                this.Dispose(true);
                GC.SuppressFinalize(this); 
            }
 
            #endregion IDisposable implementation 
        }
    } 
}

// File provided for Reference Use Only by Microsoft Corporation (c) 2007.
// Copyright (c) Microsoft Corporation. All rights reserved.
//  Microsoft Avalon 
//  Copyright (c) Microsoft Corporation, 2006
//
//  File:       Win32PrintDialog.cs
// 
//  02/17/2006 : robertan - created
// 
//------------------------------------------------------------------------------ 

using System; 
using System.Drawing.Printing;
using System.Printing.Interop;
using System.Printing;
using System.Runtime.InteropServices; 
using System.Security;
using System.Security.Permissions; 
using System.Windows.Controls; 

namespace MS.Internal.Printing 
{
    internal partial class Win32PrintDialog
    {
        ///  
        /// This class abstracts the Marshaling of the PrintDlgEx structure to/from
        /// Native memory for displaying the Win32 print dialog via the PrintDlgEx 
        /// API.  The reason for this abstraction is to handle a known issue with 
        /// this API.  The 32-bit and 64-bit versions of this method have different
        /// packing schemes for the input structure that need to be handled separately 
        /// to function correctly depending on the CPU archetecture.
        /// 
        private sealed class PrintDlgExMarshaler : IDisposable
        { 
            #region Constructor
 
            ///  
            /// Construct and initialize a PrintDlgExMarshaler instance.
            ///  
            /// 
            /// A Win32 window handle to use as the parent of this dialog.
            /// 
            ///  
            /// A reference to the Win32PrintDialog that contains the necessary
            /// data to display a Win32 Print Dialog via the PrintDlgEx call. 
            ///  
            /// 
            ///     Critical:    - Sets critical data to default values 
            ///                  - We want creation of this class to be tracked.
            /// 
            [SecurityCritical]
            internal 
            PrintDlgExMarshaler(
                IntPtr owner, 
                Win32PrintDialog dialog 
                )
            { 
                _ownerHandle = owner;
                _dialog = dialog;
                _unmanagedPrintDlgEx = IntPtr.Zero;
            } 

            #endregion Constructor 
 
            #region Finalizer
 
            ~PrintDlgExMarshaler()
            {
                this.Dispose(true);
            } 

            #endregion Finalizer 
 
            #region Internal properties
 
            /// 
            /// Gets an IntPtr that points to unmanaged memory that represents
            /// a PRINTDLGEX structure for calling into PrintDlgEx Win32 API.
            ///  
            /// 
            ///     Critical:    - This exposes critical unmanaged memory buffer 
            ///                    and should be treated as dangerous since it 
            ///                    is being passed to managed code.
            ///  
            internal IntPtr UnmanagedPrintDlgEx
            {
                [SecurityCritical]
                get 
                {
                    return _unmanagedPrintDlgEx; 
                } 
            }
 
            #endregion Internal properties

            #region Internal methods
 
            /// 
            /// This method synchronizes the internal PRINTDLGEX unmanaged data 
            /// structure with the internal Win32 print dialog configuration 
            /// parameters.  You call this prior to a call to PrintDlgEx Win32
            /// API to configure the unmanaged memory. 
            /// 
            /// 
            ///     Critical:    - Calls other critical code (ExtractPrintDataAndDevMode,
            ///                    AcquireResultFromPrintDlgExStruct, AcquirePrintQueue, 
            ///                    and AcquirePrintTicket).
            ///                  - Sets critical data on the Win32PrintDialog class 
            ///                    (PrintTicket and PrintQueue) 
            /// 
            [SecurityCritical] 
            internal
            UInt32
            SyncFromStruct()
            { 
                if (_unmanagedPrintDlgEx == IntPtr.Zero)
                { 
                    return NativeMethods.PD_RESULT_CANCEL; 
                }
 
                UInt32 dialogResult = AcquireResultFromPrintDlgExStruct(_unmanagedPrintDlgEx);

                if ((dialogResult == NativeMethods.PD_RESULT_PRINT) ||
                     (dialogResult == NativeMethods.PD_RESULT_APPLY)) 
                {
                    IntPtr devModeHandle; 
                    string printerName; 
                    UInt32 flags;
                    PageRange pageRange; 

                    ExtractPrintDataAndDevMode(
                        _unmanagedPrintDlgEx,
                        out printerName, 
                        out flags,
                        out pageRange, 
                        out devModeHandle); 

                    _dialog.PrintQueue = AcquirePrintQueue(printerName); 
                    _dialog.PrintTicket = AcquirePrintTicket(devModeHandle, printerName);

                    if ((flags & NativeMethods.PD_PAGENUMS) == NativeMethods.PD_PAGENUMS)
                    { 
                        if (pageRange.PageFrom > pageRange.PageTo)
                        { 
                            int temp = pageRange.PageTo; 
                            pageRange.PageTo = pageRange.PageFrom;
                            pageRange.PageFrom = temp; 
                        }

                        _dialog.PageRangeSelection = PageRangeSelection.UserPages;
                        _dialog.PageRange = pageRange; 
                    }
                    else 
                    { 
                        _dialog.PageRangeSelection = PageRangeSelection.AllPages;
                    } 
                }

                return dialogResult;
            } 

            ///  
            /// This method synchronizes the managed Win32 data with the PRINTDLGEX 
            /// unmanaged data structure.  It is used after a successful call to the
            /// PrintDlgEx Win32 API. 
            /// 
            /// 
            ///     Critical:    - Calls into critical code (FreeUnmanagedPrintDlgExStruct,
            ///                    GetDesktopWindow, and AllocateUnmanagedPrintDlgExStruct). 
            /// 
            [SecurityCritical] 
            internal 
            void
            SyncToStruct() 
            {
                if (_unmanagedPrintDlgEx != IntPtr.Zero)
                {
                    FreeUnmanagedPrintDlgExStruct(_unmanagedPrintDlgEx); 
                }
 
                // 
                // If parent is not valid then get the desktop window handle.
                // 
                if (_ownerHandle == IntPtr.Zero)
                {
                    _ownerHandle = MS.Win32.UnsafeNativeMethods.GetDesktopWindow();
                } 

                // 
                // Allocate an unmanaged PRINTDLGEX structure with our current internal settings. 
                //
                _unmanagedPrintDlgEx = AllocateUnmanagedPrintDlgExStruct(); 
            }

            #endregion Internal methods
 
            #region Private helper methods
 
            ///  
            /// Clean up any resources being used.
            ///  
            /// 
            /// true if managed resources should be disposed; otherwise, false.
            /// 
            ///  
            ///     Critical     - Accesses unmanaged critical pointer
            ///     TreatAsSafe  - Its only freeing the memory and nothing leaves this method. 
            ///  
            [SecurityCritical, SecurityTreatAsSafe]
            private 
            void
            Dispose(
                bool disposing
                ) 
            {
                if (disposing) 
                { 
                    if (_unmanagedPrintDlgEx != IntPtr.Zero)
                    { 
                        FreeUnmanagedPrintDlgExStruct(_unmanagedPrintDlgEx);
                        _unmanagedPrintDlgEx = IntPtr.Zero;
                    }
                } 
            }
 
            ///  
            /// Extracts the printer name, flags, pageRange, and devmode handle from the
            /// given PRINTDLGEX structure. 
            /// 
            /// 
            /// An unmanaged buffer representing the PRINTDLGEX structure.  This structure
            /// is passed around as an unmanaged buffer since the 32-bit and 64-bit versions 
            /// of this buffer are not the same and need to be handled uniquely.
            ///  
            ///  
            /// An out parameter to store store the printer name.
            ///  
            /// 
            /// The set of flags that are inside the PRINTDIALOGEX structure.
            /// 
            ///  
            /// The  user specified page range that is in PRINTDLGEX structure.
            /// This is only useful if the PD_PAGENUMS bit is set in the flags. 
            ///  
            /// 
            /// An out parameter to store the devmode handle. 
            /// 
            /// 
            ///     Critical:    - manipulating unmanaged buffers with critical data in them.
            ///  
            [SecurityCritical]
            private 
            void 
            ExtractPrintDataAndDevMode(
                IntPtr unmanagedBuffer, 
                out string printerName,
                out UInt32 flags,
                out PageRange pageRange,
                out IntPtr devModeHandle 
                )
            { 
                IntPtr devNamesHandle = IntPtr.Zero; 
                IntPtr pageRangePtr = IntPtr.Zero;
 
                //
                // Extract the devmode and devnames handles from the appropriate PRINTDLGEX structure
                //
                if (!Is64Bit()) 
                {
                    NativeMethods.PRINTDLGEX32 pdex = (NativeMethods.PRINTDLGEX32)Marshal.PtrToStructure( 
                        unmanagedBuffer, 
                        typeof(NativeMethods.PRINTDLGEX32));
                    devModeHandle = pdex.hDevMode; 
                    devNamesHandle = pdex.hDevNames;
                    flags = pdex.Flags;
                    pageRangePtr = pdex.lpPageRanges;
                } 
                else
                { 
                    NativeMethods.PRINTDLGEX64 pdex = (NativeMethods.PRINTDLGEX64)Marshal.PtrToStructure( 
                        unmanagedBuffer,
                        typeof(NativeMethods.PRINTDLGEX64)); 
                    devModeHandle = pdex.hDevMode;
                    devNamesHandle = pdex.hDevNames;
                    flags = pdex.Flags;
                    pageRangePtr = pdex.lpPageRanges; 
                }
 
                // 
                // Get a managed copy of the page ranges.  This only matters if the PD_PAGENUMS bit is
                // set in the flags. 
                //
                if (((flags & NativeMethods.PD_PAGENUMS) == NativeMethods.PD_PAGENUMS) &&
                     (pageRangePtr != IntPtr.Zero))
                { 
                    NativeMethods.PRINTPAGERANGE pageRangeStruct = (NativeMethods.PRINTPAGERANGE)Marshal.PtrToStructure(
                            pageRangePtr, 
                            typeof(NativeMethods.PRINTPAGERANGE)); 

                    pageRange = new PageRange((int)pageRangeStruct.nFromPage, (int)pageRangeStruct.nToPage); 
                }
                else
                {
                    pageRange = new PageRange(1); 
                }
 
                // 
                // Get a managed copy of the device name
                // 
                if (devNamesHandle != IntPtr.Zero)
                {
                    IntPtr pDevNames = IntPtr.Zero;
                    try 
                    {
                        pDevNames = UnsafeNativeMethods.GlobalLock(devNamesHandle); 
 
                        NativeMethods.DEVNAMES devNames = (NativeMethods.DEVNAMES)Marshal.PtrToStructure(
                            pDevNames, 
                            typeof(NativeMethods.DEVNAMES));
                        printerName = Marshal.PtrToStringAuto(
                            (IntPtr)((int)pDevNames + (devNames.wDeviceOffset * Marshal.SystemDefaultCharSize)));
                    } 
                    finally
                    { 
                        if (pDevNames != IntPtr.Zero) 
                        {
                            UnsafeNativeMethods.GlobalUnlock(devNamesHandle); 
                        }
                    }
                }
                else 
                {
                    printerName = string.Empty; 
                } 
            }
 
            /// 
            /// Acquires an instance of the PrintQueue that cooresponds to the given printer name.
            /// 
            ///  
            /// The printer name to search for.
            ///  
            ///  
            ///     Critical:   - Performs an elevation to access the printing subsystem to lookup
            ///                   the PrintQueue based on the printername. 
            /// 
            [SecurityCritical]
            private
            PrintQueue 
            AcquirePrintQueue(
                string printerName 
                ) 
            {
                PrintQueue printQueue = null; 

                EnumeratedPrintQueueTypes[] types = new EnumeratedPrintQueueTypes[] {
                    EnumeratedPrintQueueTypes.Local,
                    EnumeratedPrintQueueTypes.Connections 
                };
 
                // 
                // This forces us to acquire the cached version of the print queues.
                // This theoretically should prevent crashing in the printing system 
                // since all it is doing is reading the registry.
                //
                PrintQueueIndexedProperty[] props = new PrintQueueIndexedProperty[] {
                    PrintQueueIndexedProperty.Name, 
                    PrintQueueIndexedProperty.QueueAttributes
                }; 
 
                (new PrintingPermission(PrintingPermissionLevel.DefaultPrinting)).Assert();  //BlessedAssert
                try 
                {
                    //
                    // Get the PrintQueue instance for the printer
                    // 
                    using (LocalPrintServer server = new LocalPrintServer())
                    { 
                        foreach (PrintQueue queue in server.GetPrintQueues(props, types)) 
                        {
                            if (printerName.Equals(queue.FullName, StringComparison.OrdinalIgnoreCase)) 
                            {
                                printQueue = queue;
                                break;
                            } 
                        }
                    } 
                    if (printQueue != null) 
                    {
                        printQueue.InPartialTrust = true; 
                    }
                }
                finally
                { 
                    PrintingPermission.RevertAssert();
                } 
 
                return printQueue;
            } 

            /// 
            /// Acquires an instance of a PrintTicket given a handle to a DEVMODE and a printer name.
            ///  
            /// 
            /// The DEVMODE handle to use for the PrintTicket. 
            ///  
            /// 
            /// The printer name for the PrintTicket converter. 
            /// 
            /// 
            ///     Critical:   - Performs an elevation to access the printing subsystem to lookup
            ///                   the PrintQueue based on the printer in the PRINTDLG structure. 
            ///                 - Calls unmanaged code that has been suppressed.
            ///  
            [SecurityCritical] 
            private
            PrintTicket 
            AcquirePrintTicket(
                IntPtr devModeHandle,
                string printQueueName
                ) 
            {
                PrintTicket printTicket = null; 
                byte[] devModeData = null; 

                // 
                // Copy the devmode into a byte array
                //
                IntPtr pDevMode = IntPtr.Zero;
                try 
                {
                    pDevMode = UnsafeNativeMethods.GlobalLock(devModeHandle); 
 
                    NativeMethods.DEVMODE devMode = (NativeMethods.DEVMODE)Marshal.PtrToStructure(
                        pDevMode, 
                        typeof(NativeMethods.DEVMODE));
                    devModeData = new byte[devMode.dmSize + devMode.dmDriverExtra];
                    Marshal.Copy(pDevMode, devModeData, 0, devModeData.Length);
                } 
                finally
                { 
                    if (pDevMode != IntPtr.Zero) 
                    {
                        UnsafeNativeMethods.GlobalUnlock(devModeHandle); 
                    }
                }

                (new PrintingPermission(PrintingPermissionLevel.DefaultPrinting)).Assert(); //BlessedAssert 
                try
                { 
                    // 
                    // Convert the devmode data to a PrintTicket object
                    // 
                    using (PrintTicketConverter ptConverter = new PrintTicketConverter(
                                printQueueName,
                                PrintTicketConverter.MaxPrintSchemaVersion))
                    { 
                        printTicket = ptConverter.ConvertDevModeToPrintTicket(devModeData);
                    } 
                } 
                finally
                { 
                    PrintingPermission.RevertAssert();
                }

                return printTicket; 
            }
 
            ///  
            /// Extracts the result value from a given PRINTDLGEX structure.
            ///  
            /// 
            /// An unmanaged buffer representing the PRINTDLGEX structure.  This structure
            /// is passed around as an unmanaged buffer since the 32-bit and 64-bit versions
            /// of this buffer are not the same and need to be handled uniquely. 
            /// 
            ///  
            ///     Critical:    - manipulating unmanaged buffers with critical data in them. 
            /// 
            [SecurityCritical] 
            private
            UInt32
            AcquireResultFromPrintDlgExStruct(
                IntPtr unmanagedBuffer 
                )
            { 
                UInt32 result = 0; 

                // 
                // Extract the devmode and devnames handles from the appropriate PRINTDLGEX structure
                //
                if (!Is64Bit())
                { 
                    NativeMethods.PRINTDLGEX32 pdex = (NativeMethods.PRINTDLGEX32)Marshal.PtrToStructure(
                        unmanagedBuffer, 
                        typeof(NativeMethods.PRINTDLGEX32)); 
                    result = pdex.dwResultAction;
                } 
                else
                {
                    NativeMethods.PRINTDLGEX64 pdex = (NativeMethods.PRINTDLGEX64)Marshal.PtrToStructure(
                        unmanagedBuffer, 
                        typeof(NativeMethods.PRINTDLGEX64));
                    result = pdex.dwResultAction; 
                } 

                return result; 
            }

            /// 
            /// Allocates an unmanaged buffer for the PRINTDLGEX structure and 
            /// sets the initial values on it.
            /// 
            /// NOTE:  The reason for returning an unmanaged pointer to a buffer 
            /// and not just a structure that can be passed to the unmanaged APIs
            /// is because this class is providing an abstraction to a problem that 
            /// exists in the unmanaged print world where the 32-bit and 64-bit
            /// structure packing is inconsistent.
            /// 
            ///  
            ///     Critical:    - Allocates and returns unmanaged memory that will
            ///                    eventually contain possibly sensitive data. 
            ///  
            [SecurityCritical]
            private 
            IntPtr
            AllocateUnmanagedPrintDlgExStruct()
            {
                IntPtr unmanagedBuffer = IntPtr.Zero; 

                NativeMethods.PRINTPAGERANGE range; 
                range.nToPage = (uint)_dialog.PageRange.PageTo; 
                range.nFromPage = (uint)_dialog.PageRange.PageFrom;
 
                try
                {
                    if (!Is64Bit())
                    { 
                        NativeMethods.PRINTDLGEX32 pdex = new NativeMethods.PRINTDLGEX32();
                        pdex.hwndOwner = _ownerHandle; 
                        pdex.nMinPage = _dialog.MinPage; 
                        pdex.nMaxPage = _dialog.MaxPage;
                        pdex.Flags = 
                            NativeMethods.PD_ALLPAGES |
                            NativeMethods.PD_NOCURRENTPAGE |
                            NativeMethods.PD_NOSELECTION |
                            NativeMethods.PD_USEDEVMODECOPIESANDCOLLATE | 
                            NativeMethods.PD_DISABLEPRINTTOFILE |
                            NativeMethods.PD_HIDEPRINTTOFILE; 
 
                        if (_dialog.PageRangeEnabled)
                        { 
                            pdex.lpPageRanges = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(NativeMethods.PRINTPAGERANGE)));
                            pdex.nMaxPageRanges = 1;

                            if (_dialog.PageRangeSelection == PageRangeSelection.UserPages) 
                            {
                                pdex.nPageRanges = 1; 
                                Marshal.StructureToPtr(range, pdex.lpPageRanges, false); 
                                pdex.Flags |= NativeMethods.PD_PAGENUMS;
                            } 
                            else
                            {
                                pdex.nPageRanges = 0;
                            } 
                        }
                        else 
                        { 
                            pdex.lpPageRanges = IntPtr.Zero;
                            pdex.nMaxPageRanges = 0; 
                            pdex.Flags |= NativeMethods.PD_NOPAGENUMS;
                        }

                        // 
                        // If we know a print queue and print ticket, then we need to try to select
                        // the printer in the dialog and use the print ticket.  We allocate and setup 
                        // a DEVNAMES structure for this as well as convert the PrintTicket to a 
                        // DEVMODE.
                        // 
                        if (_dialog.PrintQueue != null)
                        {
                            pdex.hDevNames = AllocateAndInitializeDevNames(_dialog.PrintQueue.FullName);
 
                            if (_dialog.PrintTicket != null)
                            { 
                                pdex.hDevMode = AllocateAndInitializeDevMode( 
                                    _dialog.PrintQueue.FullName,
                                    _dialog.PrintTicket); 
                            }
                        }

                        int cbBufferSize = Marshal.SizeOf(typeof(NativeMethods.PRINTDLGEX32)); 
                        unmanagedBuffer = Marshal.AllocHGlobal(cbBufferSize);
                        Marshal.StructureToPtr(pdex, unmanagedBuffer, false); 
                    } 
                    else
                    { 
                        NativeMethods.PRINTDLGEX64 pdex = new NativeMethods.PRINTDLGEX64();
                        pdex.hwndOwner = _ownerHandle;
                        pdex.nMinPage = _dialog.MinPage;
                        pdex.nMaxPage = _dialog.MaxPage; 
                        pdex.Flags =
                            NativeMethods.PD_ALLPAGES | 
                            NativeMethods.PD_NOCURRENTPAGE | 
                            NativeMethods.PD_NOSELECTION |
                            NativeMethods.PD_USEDEVMODECOPIESANDCOLLATE | 
                            NativeMethods.PD_DISABLEPRINTTOFILE |
                            NativeMethods.PD_HIDEPRINTTOFILE;

                        if (_dialog.PageRangeEnabled) 
                        {
                            pdex.lpPageRanges = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(NativeMethods.PRINTPAGERANGE))); 
                            pdex.nMaxPageRanges = 1; 

                            if (_dialog.PageRangeSelection == PageRangeSelection.UserPages) 
                            {
                                pdex.nPageRanges = 1;
                                Marshal.StructureToPtr(range, pdex.lpPageRanges, false);
                                pdex.Flags |= NativeMethods.PD_PAGENUMS; 
                            }
                            else 
                            { 
                                pdex.nPageRanges = 0;
                            } 
                        }
                        else
                        {
                            pdex.lpPageRanges = IntPtr.Zero; 
                            pdex.nMaxPageRanges = 0;
                            pdex.Flags |= NativeMethods.PD_NOPAGENUMS; 
                        } 

                        // 
                        // If we know a print queue and print ticket, then we need to try to select
                        // the printer in the dialog and use the print ticket.  We allocate and setup
                        // a DEVNAMES structure for this as well as convert the PrintTicket to a
                        // DEVMODE. 
                        //
                        if (_dialog.PrintQueue != null) 
                        { 
                            pdex.hDevNames = AllocateAndInitializeDevNames(_dialog.PrintQueue.FullName);
 
                            if (_dialog.PrintTicket != null)
                            {
                                pdex.hDevMode = AllocateAndInitializeDevMode(
                                    _dialog.PrintQueue.FullName, 
                                    _dialog.PrintTicket);
                            } 
                        } 

                        int cbBufferSize = Marshal.SizeOf(typeof(NativeMethods.PRINTDLGEX64)); 
                        unmanagedBuffer = Marshal.AllocHGlobal(cbBufferSize);
                        Marshal.StructureToPtr(pdex, unmanagedBuffer, false);
                    }
                } 
                catch (Exception)
                { 
                    if (unmanagedBuffer != null) 
                    {
                        FreeUnmanagedPrintDlgExStruct(unmanagedBuffer); 
                        unmanagedBuffer = IntPtr.Zero;
                    }

                    throw; 
                }
 
                return unmanagedBuffer; 
            }
 
            /// 
            /// Frees an unmanaged buffer.
            /// 
            ///  
            /// An unmanaged buffer representing the PRINTDLGEX structure.  This structure
            /// is passed around as an unmanaged buffer since the 32-bit and 64-bit versions 
            /// of this buffer are not the same and need to be handled uniquely. 
            /// 
            ///  
            ///     Critical:    - Frees an unmanaged buffer with potentially sensitive data.
            /// 
            [SecurityCritical]
            private 
            void
            FreeUnmanagedPrintDlgExStruct( 
                IntPtr unmanagedBuffer 
                )
            { 
                if (unmanagedBuffer == IntPtr.Zero)
                {
                    return;
                } 

                IntPtr devModeHandle = IntPtr.Zero; 
                IntPtr devNamesHandle = IntPtr.Zero; 
                IntPtr pageRangePtr = IntPtr.Zero;
 
                //
                // Extract the devmode and devnames handles from the appropriate PRINTDLGEX structure
                //
                if (!Is64Bit()) 
                {
                    NativeMethods.PRINTDLGEX32 pdex = (NativeMethods.PRINTDLGEX32)Marshal.PtrToStructure( 
                        unmanagedBuffer, 
                        typeof(NativeMethods.PRINTDLGEX32));
                    devModeHandle = pdex.hDevMode; 
                    devNamesHandle = pdex.hDevNames;
                    pageRangePtr = pdex.lpPageRanges;
                }
                else 
                {
                    NativeMethods.PRINTDLGEX64 pdex = (NativeMethods.PRINTDLGEX64)Marshal.PtrToStructure( 
                        unmanagedBuffer, 
                        typeof(NativeMethods.PRINTDLGEX64));
                    devModeHandle = pdex.hDevMode; 
                    devNamesHandle = pdex.hDevNames;
                    pageRangePtr = pdex.lpPageRanges;
                }
 
                if (devModeHandle != IntPtr.Zero)
                { 
                    UnsafeNativeMethods.GlobalFree(devModeHandle); 
                }
 
                if (devNamesHandle != IntPtr.Zero)
                {
                    UnsafeNativeMethods.GlobalFree(devNamesHandle);
                } 

                if (pageRangePtr != IntPtr.Zero) 
                { 
                    UnsafeNativeMethods.GlobalFree(pageRangePtr);
                } 

                Marshal.FreeHGlobal(unmanagedBuffer);
            }
 
            /// 
            /// Returns a boolean value representing whether the current runtime is 
            /// 32-bit or 64-bit. 
            /// 
            ///  
            ///     Critical:    - Marshal.SizeOf LinkDemands
            ///     TreatAsSafe: - This method returns nothing that is unsafe.
            /// 
            [SecurityCritical, SecurityTreatAsSafe] 
            private
            bool 
            Is64Bit() 
            {
                IntPtr temp = IntPtr.Zero; 
                return Marshal.SizeOf(temp) == 8;
            }

            ///  
            /// This method allocates a DEVNAMES structure in unmanaged code and configures
            /// it to point to the specified printer. 
            ///  
            /// 
            /// The printer name to use for the DEVNAMES structure. 
            /// 
            /// 
            /// Returns an IntPtr pointing to a memory address in unmanaged code where
            /// the structure has been initialized. 
            /// 
            ///  
            ///     Critical:    - Allocates and manipulates unmanaged memory. 
            /// 
            [SecurityCritical] 
            private
            IntPtr
            AllocateAndInitializeDevNames(
                string printerName 
                )
            { 
                IntPtr hDevNames = IntPtr.Zero; 
                char[] printer = printerName.ToCharArray();
 
                //
                // Enough memory for the printer name character array + 3 null characters
                // + the DEVNAMES structure.  The 3 null characters are for the end of the
                // printer name string, and 2 distinct empty strings. 
                //
                int cbDevNames = ((printer.Length + 3) * Marshal.SystemDefaultCharSize) + 
                    Marshal.SizeOf(typeof(NativeMethods.DEVNAMES)); 
                hDevNames = Marshal.AllocHGlobal(cbDevNames);
 
                ushort baseOffset = (ushort)Marshal.SizeOf(typeof(NativeMethods.DEVNAMES));

                //
                // The wDeviceOffset contains an offset in characters to the printer name 
                // within the DEVNAMES structure.  The wDriverOffset and wOutputOffset
                // are offsets to two distinct empty strings that just happen to follow 
                // the printer name string.  For more information on how this structure is 
                // layed out, please review the MSDN documentation for DEVNAMES structure.
                // 
                NativeMethods.DEVNAMES devNames;
                devNames.wDeviceOffset = (ushort)(baseOffset / Marshal.SystemDefaultCharSize);
                devNames.wDriverOffset = (ushort)(devNames.wDeviceOffset + printer.Length + 1);
                devNames.wOutputOffset = (ushort)(devNames.wDriverOffset + 1); 
                devNames.wDefault = 0;
                Marshal.StructureToPtr(devNames, hDevNames, false); 
 
                //
                // Calculate the position of the string containing the printer name 
                //   - Printer name character array immediately follows the DEVNAMES structure
                //   - followed by 3 NULL characters
                //
                IntPtr offsetName = 
                    (IntPtr)((long)hDevNames + (long)baseOffset);
                IntPtr offsetNull = 
                    (IntPtr)((long)offsetName + (printer.Length * Marshal.SystemDefaultCharSize)); 

                // 
                // Write the printer name and 3 NULL characters.  The first NULL character
                // is to null terminate our printer name string.  The second and third null
                // characters are to create to distinct empty strings within the buffer.
                // 
                // NOTE: The byte array contains the NULL characters in a form that we are
                // able to write them out.  There are 3 * char size bytes total. 
                // 
                byte[] nulls = new byte[3 * Marshal.SystemDefaultCharSize];
                Array.Clear(nulls, 0, nulls.Length); 
                Marshal.Copy(
                    printer,
                    0,
                    offsetName, 
                    printer.Length);
                Marshal.Copy( 
                    nulls, 
                    0,
                    offsetNull, 
                    nulls.Length);

                return hDevNames;
            } 

            ///  
            /// This method allocates and initializes an unmanaged DEVMODE structure 
            /// for use to by the PrintDlgEx method based on the specified printer
            /// and PrintTicket object. 
            /// 
            /// 
            /// The printer that is being targetted by the PrintTicket.
            ///  
            /// 
            /// The PrintTicket that will be converted to a DEVMODE. 
            ///  
            /// 
            /// An unmanaged pointer to an unmanaged DEVMODE structure that can be 
            /// used in the PRINTDLGEX structure for a call to PrintDlgEx.
            /// 
            /// 
            ///     Critical:    - Allocates and manipulates unmanaged memory. 
            ///                  - Asserts for DefaultPrinting permissions to be
            ///                    able to successfully convert the PrintTicket 
            ///                    object to a DEVMODE via the PrintTicketConverter. 
            /// 
            [SecurityCritical] 
            private
            IntPtr
            AllocateAndInitializeDevMode(
                string printerName, 
                PrintTicket printTicket
                ) 
            { 
                byte[] devModeData = null;
 
                (new PrintingPermission(PrintingPermissionLevel.DefaultPrinting)).Assert();  //BlessedAssert
                try
                {
                    // 
                    // Convert the PrintTicket object to a DEVMODE
                    // 
                    using (PrintTicketConverter ptConverter = new PrintTicketConverter( 
                                printerName,
                                PrintTicketConverter.MaxPrintSchemaVersion)) 
                    {
                        devModeData = ptConverter.ConvertPrintTicketToDevMode(
                            printTicket,
                            BaseDevModeType.UserDefault); 
                    }
                } 
                finally 
                {
                    PrintingPermission.RevertAssert(); 
                }

                //
                // Make the dev mode data a DEVMODE structure in global memory 
                //
                IntPtr hDevMode = Marshal.AllocHGlobal(devModeData.Length); 
                Marshal.Copy(devModeData, 0, hDevMode, devModeData.Length); 

                return hDevMode; 
            }

            #endregion Private helper methods
 
            #region Private data
 
            private 
            Win32PrintDialog _dialog;
 
            /// 
            ///     Critical: This is a pointer to unmanaged memory containing
            ///               an unmanaged PRINTDLGEX structure.
            ///  
            [SecurityCritical]
            private 
            IntPtr _unmanagedPrintDlgEx; 

            ///  
            ///     Critical: This is a handle to a window that will be used
            ///               as the parent for the Win32 print dialog.
            /// 
            [SecurityCritical] 
            private
            IntPtr _ownerHandle; 
 
            #endregion Private data
 
            #region IDisposable implementation

            /// 
            /// Implements the Dispose method for IDisposable.  This ensures that 
            /// any unmanaged PRINTDLGEX structure that was allocated by the class
            /// will be property freed when this class goes away. 
            ///  
            public
            void 
            Dispose()
            {
                this.Dispose(true);
                GC.SuppressFinalize(this); 
            }
 
            #endregion IDisposable implementation 
        }
    } 
}

// File provided for Reference Use Only by Microsoft Corporation (c) 2007.
// Copyright (c) Microsoft Corporation. All rights reserved.
                        

Link Menu

Network programming in C#, Network Programming in VB.NET, Network Programming in .NET
This book is available now!
Buy at Amazon US or
Buy at Amazon UK