Consolidate PrintController code and cleanup some interop (dotnet/corefx#36736)
authorHugh Bellamy <hughbellars@gmail.com>
Thu, 3 Oct 2019 21:42:42 +0000 (22:42 +0100)
committerSantiago Fernandez Madero <safern@microsoft.com>
Thu, 3 Oct 2019 21:42:42 +0000 (14:42 -0700)
* Consolidate PrintController code and cleanup some interop

* Cleanup access modifiers in PrintDocument

* Add tests and fix debug assert failures

* Remove argument validation from APIs that should be called from internal members only and update tests

Commit migrated from https://github.com/dotnet/corefx/commit/07262aa6bce06f1e270e6a9b494bfe6eb1260b08

17 files changed:
src/libraries/Common/src/Interop/Windows/Kernel32/Interop.GlobalFree.cs [new file with mode: 0644]
src/libraries/Common/src/Interop/Windows/Kernel32/Interop.GlobalLock.cs
src/libraries/System.Drawing.Common/src/System.Drawing.Common.csproj
src/libraries/System.Drawing.Common/src/System/Drawing/Gdiplus.cs
src/libraries/System.Drawing.Common/src/System/Drawing/Printing/DefaultPrintController.cs
src/libraries/System.Drawing.Common/src/System/Drawing/Printing/PageSettings.Windows.cs
src/libraries/System.Drawing.Common/src/System/Drawing/Printing/PreviewPrintController.Unix.cs
src/libraries/System.Drawing.Common/src/System/Drawing/Printing/PreviewPrintController.Windows.cs
src/libraries/System.Drawing.Common/src/System/Drawing/Printing/PreviewPrintController.cs [new file with mode: 0644]
src/libraries/System.Drawing.Common/src/System/Drawing/Printing/PrintController.Unix.cs
src/libraries/System.Drawing.Common/src/System/Drawing/Printing/PrintController.Windows.cs
src/libraries/System.Drawing.Common/src/System/Drawing/Printing/PrintController.cs [new file with mode: 0644]
src/libraries/System.Drawing.Common/src/System/Drawing/Printing/PrintDocument.Windows.cs
src/libraries/System.Drawing.Common/src/System/Drawing/Printing/PrinterSettings.Windows.cs
src/libraries/System.Drawing.Common/tests/Printing/PreviewPrintControllerTests.cs [new file with mode: 0644]
src/libraries/System.Drawing.Common/tests/Printing/PrintControllerTests.cs [new file with mode: 0644]
src/libraries/System.Drawing.Common/tests/System.Drawing.Common.Tests.csproj

diff --git a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.GlobalFree.cs b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.GlobalFree.cs
new file mode 100644 (file)
index 0000000..676b3a0
--- /dev/null
@@ -0,0 +1,22 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System;
+using System.Runtime.InteropServices;
+
+internal static partial class Interop
+{
+    internal static partial class Kernel32
+    {
+        [DllImport(Libraries.Kernel32, ExactSpelling = true, SetLastError = true)]
+        public static extern IntPtr GlobalFree(IntPtr handle);
+
+        public static IntPtr GlobalFree(HandleRef handle)
+        {
+            IntPtr result = GlobalFree(handle.Handle);
+            GC.KeepAlive(handle.Wrapper);
+            return result;
+        }
+    }
+}
index c82e330..4deccdd 100644 (file)
@@ -9,10 +9,24 @@ internal partial class Interop
 {
     internal partial class Kernel32
     {
-        [DllImport(Libraries.Kernel32, PreserveSig = true)]
-        internal static extern IntPtr GlobalLock(IntPtr hMem);
+        [DllImport(Libraries.Kernel32, ExactSpelling = true, SetLastError = true)]
+        public static extern IntPtr GlobalLock(IntPtr hMem);
 
-        [DllImport(Libraries.Kernel32, PreserveSig = true)]
-        internal static extern int GlobalUnlock(IntPtr hMem);
+        public static IntPtr GlobalLock(HandleRef hMem)
+        {
+            IntPtr result = GlobalLock(hMem.Handle);
+            GC.KeepAlive(hMem.Wrapper);
+            return result;
+        }
+
+        [DllImport(Libraries.Kernel32, ExactSpelling = true)]
+        public static extern IntPtr GlobalUnlock(IntPtr hMem);
+
+        public static IntPtr GlobalUnlock(HandleRef hMem)
+        {
+            IntPtr result = GlobalUnlock(hMem.Handle);
+            GC.KeepAlive(hMem.Wrapper);
+            return result;
+        }
     }
 }
index 30f9911..7f57129 100644 (file)
     <Compile Include="System\Drawing\Imaging\Metafile.cs" />
     <Compile Include="System\Drawing\Printing\PrinterUnit.cs" />
     <Compile Include="System\Drawing\Printing\PreviewPageInfo.cs" />
+    <Compile Include="System\Drawing\Printing\PreviewPrintController.cs" />
     <Compile Include="System\Drawing\Printing\PrintEventHandler.cs" />
     <Compile Include="System\Drawing\Printing\PrintAction.cs" />
+    <Compile Include="System\Drawing\Printing\PrintController.cs" />
     <Compile Include="System\Drawing\Printing\PrintPageEventHandler.cs" />
     <Compile Include="System\Drawing\Printing\QueryPageSettingsEventArgs.cs" />
     <Compile Include="System\Drawing\Printing\QueryPageSettingsEventHandler.cs" />
     <Compile Include="$(CommonPath)\Interop\Windows\Kernel32\Interop.FreeLibrary.cs">
       <Link>Common\Interop\Windows\Kernel32\Interop.FreeLibrary.cs</Link>
     </Compile>
+    <Compile Include="$(CommonPath)\Interop\Windows\Kernel32\Interop.GlobalFree.cs">
+      <Link>Common\Interop\Windows\Kernel32\Interop.GlobalFree.cs</Link>
+    </Compile>
+    <Compile Include="$(CommonPath)\Interop\Windows\Kernel32\Interop.GlobalLock.cs">
+      <Link>Common\Interop\Windows\Kernel32\Interop.GlobalLock.cs</Link>
+    </Compile>
     <Compile Include="$(CommonPath)\Interop\Windows\Kernel32\Interop.LoadLibraryEx.cs">
       <Link>Common\Interop\Windows\Kernel32\Interop.LoadLibraryEx.cs</Link>
     </Compile>
index d219129..61b6cd6 100644 (file)
@@ -388,9 +388,6 @@ namespace System.Drawing
         [DllImport(ExternDll.Gdi32, SetLastError = true, ExactSpelling = true)]
         public static extern IntPtr CreateDIBSection(HandleRef hdc, ref NativeMethods.BITMAPINFO_FLAT bmi, int iUsage, ref IntPtr ppvBits, IntPtr hSection, int dwOffset);
 
-        [DllImport(ExternDll.Kernel32, SetLastError = true, ExactSpelling = true, CharSet = CharSet.Auto)]
-        public static extern IntPtr GlobalFree(HandleRef handle);
-
         [DllImport(ExternDll.Gdi32, SetLastError = true, CharSet = CharSet.Auto)]
         public static extern int StartDoc(HandleRef hDC, DOCINFO lpDocInfo);
 
@@ -425,15 +422,9 @@ namespace System.Drawing
         public static extern int EnumPrinters(int flags, string name, int level, IntPtr pPrinterEnum/*buffer*/,
                                               int cbBuf, out int pcbNeeded, out int pcReturned);
 
-        [DllImport(ExternDll.Kernel32, SetLastError = true, ExactSpelling = true, CharSet = CharSet.Auto)]
-        public static extern IntPtr GlobalLock(HandleRef handle);
-
         [DllImport(ExternDll.Gdi32, SetLastError = true, CharSet = CharSet.Auto)]
         public static extern IntPtr /*HDC*/ ResetDC(HandleRef hDC, HandleRef /*DEVMODE*/ lpDevMode);
 
-        [DllImport(ExternDll.Kernel32, SetLastError = true, ExactSpelling = true, CharSet = CharSet.Auto)]
-        public static extern bool GlobalUnlock(HandleRef handle);
-
         [DllImport(ExternDll.Gdi32, SetLastError = true, ExactSpelling = true)]
         public static extern IntPtr CreateRectRgn(int x1, int y1, int x2, int y2);
 
index c5c4f8b..56c7898 100644 (file)
@@ -29,7 +29,7 @@ namespace System.Drawing.Printing
             if (!document.PrinterSettings.IsValid)
                 throw new InvalidPrinterException(document.PrinterSettings);
 
-            _dc = document.PrinterSettings.CreateDeviceContext(modeHandle);
+            _dc = document.PrinterSettings.CreateDeviceContext(_modeHandle);
             SafeNativeMethods.DOCINFO info = new SafeNativeMethods.DOCINFO();
             info.lpszDocName = document.DocumentName;
             if (document.PrinterSettings.PrintToFile)
@@ -62,8 +62,8 @@ namespace System.Drawing.Printing
             Debug.Assert(_dc != null && _graphics == null, "PrintController methods called in the wrong order?");
 
             base.OnStartPage(document, e);
-            e.PageSettings.CopyToHdevmode(modeHandle);
-            IntPtr modePointer = SafeNativeMethods.GlobalLock(new HandleRef(this, modeHandle));
+            e.PageSettings.CopyToHdevmode(_modeHandle);
+            IntPtr modePointer = Interop.Kernel32.GlobalLock(new HandleRef(this, _modeHandle));
             try
             {
                 IntPtr result = SafeNativeMethods.ResetDC(new HandleRef(_dc, _dc.Hdc), new HandleRef(null, modePointer));
@@ -71,7 +71,7 @@ namespace System.Drawing.Printing
             }
             finally
             {
-                SafeNativeMethods.GlobalUnlock(new HandleRef(this, modeHandle));
+                Interop.Kernel32.GlobalUnlock(new HandleRef(this, _modeHandle));
             }
 
             // int horizontalResolution = Windows.GetDeviceCaps(dc.Hdc, SafeNativeMethods.HORZRES);
index ce49323..598e42d 100644 (file)
@@ -46,10 +46,9 @@ namespace System.Drawing.Printing
             get
             {
                 IntPtr modeHandle = printerSettings.GetHdevmode();
-
                 Rectangle pageBounds = GetBounds(modeHandle);
 
-                SafeNativeMethods.GlobalFree(new HandleRef(this, modeHandle));
+                Interop.Kernel32.GlobalFree(new HandleRef(this, modeHandle));
                 return pageBounds;
             }
         }
@@ -164,13 +163,13 @@ namespace System.Drawing.Printing
                 if (_paperSource == null)
                 {
                     IntPtr modeHandle = printerSettings.GetHdevmode();
-                    IntPtr modePointer = SafeNativeMethods.GlobalLock(new HandleRef(this, modeHandle));
+                    IntPtr modePointer = Interop.Kernel32.GlobalLock(new HandleRef(this, modeHandle));
                     SafeNativeMethods.DEVMODE mode = (SafeNativeMethods.DEVMODE)Marshal.PtrToStructure(modePointer, typeof(SafeNativeMethods.DEVMODE));
 
                     PaperSource result = PaperSourceFromMode(mode);
 
-                    SafeNativeMethods.GlobalUnlock(new HandleRef(this, modeHandle));
-                    SafeNativeMethods.GlobalFree(new HandleRef(this, modeHandle));
+                    Interop.Kernel32.GlobalUnlock(new HandleRef(this, modeHandle));
+                    Interop.Kernel32.GlobalFree(new HandleRef(this, modeHandle));
 
                     return result;
                 }
@@ -233,13 +232,13 @@ namespace System.Drawing.Printing
                 if (_printerResolution == null)
                 {
                     IntPtr modeHandle = printerSettings.GetHdevmode();
-                    IntPtr modePointer = SafeNativeMethods.GlobalLock(new HandleRef(this, modeHandle));
+                    IntPtr modePointer = Interop.Kernel32.GlobalLock(new HandleRef(this, modeHandle));
                     SafeNativeMethods.DEVMODE mode = (SafeNativeMethods.DEVMODE)Marshal.PtrToStructure(modePointer, typeof(SafeNativeMethods.DEVMODE));
 
                     PrinterResolution result = PrinterResolutionFromMode(mode);
 
-                    SafeNativeMethods.GlobalUnlock(new HandleRef(this, modeHandle));
-                    SafeNativeMethods.GlobalFree(new HandleRef(this, modeHandle));
+                    Interop.Kernel32.GlobalUnlock(new HandleRef(this, modeHandle));
+                    Interop.Kernel32.GlobalFree(new HandleRef(this, modeHandle));
 
                     return result;
                 }
@@ -281,7 +280,7 @@ namespace System.Drawing.Printing
         /// </summary>
         public void CopyToHdevmode(IntPtr hdevmode)
         {
-            IntPtr modePointer = SafeNativeMethods.GlobalLock(new HandleRef(null, hdevmode));
+            IntPtr modePointer = Interop.Kernel32.GlobalLock(hdevmode);
             SafeNativeMethods.DEVMODE mode = (SafeNativeMethods.DEVMODE)Marshal.PtrToStructure(modePointer, typeof(SafeNativeMethods.DEVMODE));
 
             if (_color.IsNotDefault && ((mode.dmFields & SafeNativeMethods.DM_COLOR) == SafeNativeMethods.DM_COLOR))
@@ -370,11 +369,11 @@ namespace System.Drawing.Printing
                 int retCode = SafeNativeMethods.DocumentProperties(NativeMethods.NullHandleRef, NativeMethods.NullHandleRef, printerSettings.PrinterName, modePointer, modePointer, SafeNativeMethods.DM_IN_BUFFER | SafeNativeMethods.DM_OUT_BUFFER);
                 if (retCode < 0)
                 {
-                    SafeNativeMethods.GlobalFree(new HandleRef(null, modePointer));
+                    Interop.Kernel32.GlobalFree(modePointer);
                 }
             }
 
-            SafeNativeMethods.GlobalUnlock(new HandleRef(null, hdevmode));
+            Interop.Kernel32.GlobalUnlock(hdevmode);
         }
 
         private short ExtraBytes
@@ -382,13 +381,13 @@ namespace System.Drawing.Printing
             get
             {
                 IntPtr modeHandle = printerSettings.GetHdevmodeInternal();
-                IntPtr modePointer = SafeNativeMethods.GlobalLock(new HandleRef(this, modeHandle));
+                IntPtr modePointer = Interop.Kernel32.GlobalLock(new HandleRef(this, modeHandle));
                 SafeNativeMethods.DEVMODE mode = (SafeNativeMethods.DEVMODE)Marshal.PtrToStructure(modePointer, typeof(SafeNativeMethods.DEVMODE));
 
                 short result = mode?.dmDriverExtra ?? 0;
 
-                SafeNativeMethods.GlobalUnlock(new HandleRef(this, modeHandle));
-                SafeNativeMethods.GlobalFree(new HandleRef(this, modeHandle));
+                Interop.Kernel32.GlobalUnlock(new HandleRef(this, modeHandle));
+                Interop.Kernel32.GlobalFree(new HandleRef(this, modeHandle));
 
                 return result;
             }
@@ -427,15 +426,17 @@ namespace System.Drawing.Printing
                     ownHandle = true;
                 }
 
-                IntPtr modePointer = SafeNativeMethods.GlobalLock(new HandleRef(null, modeHandle));
+                IntPtr modePointer = Interop.Kernel32.GlobalLock(modeHandle);
                 SafeNativeMethods.DEVMODE mode = (SafeNativeMethods.DEVMODE)Marshal.PtrToStructure(modePointer, typeof(SafeNativeMethods.DEVMODE));
 
                 PaperSize result = PaperSizeFromMode(mode);
 
-                SafeNativeMethods.GlobalUnlock(new HandleRef(null, modeHandle));
+                Interop.Kernel32.GlobalUnlock(modeHandle);
 
                 if (ownHandle)
-                    SafeNativeMethods.GlobalFree(new HandleRef(null, modeHandle));
+                {
+                    Interop.Kernel32.GlobalFree(modeHandle);
+                }
 
                 return result;
             }
@@ -509,9 +510,11 @@ namespace System.Drawing.Printing
         public void SetHdevmode(IntPtr hdevmode)
         {
             if (hdevmode == IntPtr.Zero)
+            {
                 throw new ArgumentException(SR.Format(SR.InvalidPrinterHandle, hdevmode));
+            }
 
-            IntPtr pointer = SafeNativeMethods.GlobalLock(new HandleRef(null, hdevmode));
+            IntPtr pointer = Interop.Kernel32.GlobalLock(hdevmode);
             SafeNativeMethods.DEVMODE mode = (SafeNativeMethods.DEVMODE)Marshal.PtrToStructure(pointer, typeof(SafeNativeMethods.DEVMODE));
 
             if ((mode.dmFields & SafeNativeMethods.DM_COLOR) == SafeNativeMethods.DM_COLOR)
@@ -528,7 +531,7 @@ namespace System.Drawing.Printing
             _paperSource = PaperSourceFromMode(mode);
             _printerResolution = PrinterResolutionFromMode(mode);
 
-            SafeNativeMethods.GlobalUnlock(new HandleRef(null, hdevmode));
+            Interop.Kernel32.GlobalUnlock(hdevmode);
         }
 
         /// <summary>
index 859bb3f..db57f08 100644 (file)
 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 //
 
-using System;
-using System.Collections;
-using System.Drawing.Imaging;
-
 namespace System.Drawing.Printing
 {
-    public class PreviewPrintController : PrintController
+    public partial class PreviewPrintController : PrintController
     {
-        private bool useantialias;
-        private ArrayList pageInfoList;
-
-        public PreviewPrintController()
-        {
-            pageInfoList = new ArrayList();
-        }
-        public override bool IsPreview
-        {
-            get { return true; }
-        }
-
         public override void OnEndPage(PrintDocument document, PrintPageEventArgs e)
         {
         }
@@ -59,13 +43,16 @@ namespace System.Drawing.Printing
         public override void OnStartPrint(PrintDocument document, PrintEventArgs e)
         {
             if (!document.PrinterSettings.IsValid)
+            {
                 throw new InvalidPrinterException(document.PrinterSettings);
+            }
 
-            /* maybe we should reuse the images, and clear them? */
-            foreach (PreviewPageInfo pi in pageInfoList)
+            foreach (PreviewPageInfo pi in _list)
+            {
                 pi.Image.Dispose();
+            }
 
-            pageInfoList.Clear();
+            _list.Clear();
         }
 
         public override void OnEndPrint(PrintDocument document, PrintEventArgs e)
@@ -79,26 +66,12 @@ namespace System.Drawing.Printing
             PreviewPageInfo info = new PreviewPageInfo(image, new Size(e.PageSettings.PaperSize.Width,
                                              e.PageSettings.PaperSize.Height));
 
-            pageInfoList.Add(info);
+            _list.Add(info);
 
             Graphics g = Graphics.FromImage(info.Image);
             g.FillRectangle(new SolidBrush(Color.White), new Rectangle(new Point(0, 0), new Size(image.Width, image.Height)));
 
             return g;
         }
-
-        public virtual bool UseAntiAlias
-        {
-            get { return useantialias; }
-            set { useantialias = value; }
-        }
-
-        public PreviewPageInfo[] GetPreviewPageInfo()
-        {
-            PreviewPageInfo[] pi = new PreviewPageInfo[pageInfoList.Count];
-            pageInfoList.CopyTo(pi);
-            return pi;
-        }
-
     }
 }
index 685deae..aa63b8e 100644 (file)
@@ -2,8 +2,6 @@
 // The .NET Foundation licenses this file to you under the MIT license.
 // See the LICENSE file in the project root for more information.
 
-using System.Collections;
-using System.Diagnostics;
 using System.Drawing.Drawing2D;
 using System.Drawing.Imaging;
 using System.Drawing.Internal;
@@ -15,46 +13,26 @@ namespace System.Drawing.Printing
     /// <summary>
     /// A PrintController which "prints" to a series of images.
     /// </summary>
-    public class PreviewPrintController : PrintController
+    public partial class PreviewPrintController : PrintController
     {
-        private readonly IList _list = new ArrayList(); // list of PreviewPageInfo
-        private System.Drawing.Graphics _graphics;
+        private Graphics _graphics;
         private DeviceContext _dc;
-        private bool _antiAlias;
-
-        private void CheckSecurity()
-        {
-        }
-
-        /// <summary>
-        /// This is new public property which notifies if this controller is used for PrintPreview.
-        /// </summary>
-        public override bool IsPreview
-        {
-            get
-            {
-                return true;
-            }
-        }
 
         /// <summary>
         /// Implements StartPrint for generating print preview information.
         /// </summary>
         public override void OnStartPrint(PrintDocument document, PrintEventArgs e)
         {
-            Debug.Assert(_dc == null && _graphics == null, "PrintController methods called in the wrong order?");
-
-            // For security purposes, don't assume our public methods are called in any particular order
-            CheckSecurity();
-
             base.OnStartPrint(document, e);
 
             if (!document.PrinterSettings.IsValid)
+            {
                 throw new InvalidPrinterException(document.PrinterSettings);
+            }
 
             // We need a DC as a reference; we don't actually draw on it.
             // We make sure to reuse the same one to improve performance.
-            _dc = document.PrinterSettings.CreateInformationContext(modeHandle);
+            _dc = document.PrinterSettings.CreateInformationContext(_modeHandle);
         }
 
         /// <summary>
@@ -62,16 +40,11 @@ namespace System.Drawing.Printing
         /// </summary>
         public override Graphics OnStartPage(PrintDocument document, PrintPageEventArgs e)
         {
-            Debug.Assert(_dc != null && _graphics == null, "PrintController methods called in the wrong order?");
-
-            // For security purposes, don't assume our public methods are called in any particular order
-            CheckSecurity();
-
             base.OnStartPage(document, e);
 
             if (e.CopySettingsToDevMode)
             {
-                e.PageSettings.CopyToHdevmode(modeHandle);
+                e.PageSettings.CopyToHdevmode(_modeHandle);
             }
 
             Size size = e.PageBounds.Size;
@@ -100,7 +73,6 @@ namespace System.Drawing.Printing
             {
                 // Adjust the origin of the graphics object to be at the
                 // user-specified margin location
-                //
                 int dpiX = UnsafeNativeMethods.GetDeviceCaps(new HandleRef(_dc, _dc.Hdc), SafeNativeMethods.LOGPIXELSX);
                 int dpiY = UnsafeNativeMethods.GetDeviceCaps(new HandleRef(_dc, _dc.Hdc), SafeNativeMethods.LOGPIXELSY);
                 int hardMarginX_DU = UnsafeNativeMethods.GetDeviceCaps(new HandleRef(_dc, _dc.Hdc), SafeNativeMethods.PHYSICALOFFSETX);
@@ -112,11 +84,9 @@ namespace System.Drawing.Printing
                 _graphics.TranslateTransform(document.DefaultPageSettings.Margins.Left, document.DefaultPageSettings.Margins.Top);
             }
 
-
             _graphics.PrintingHelper = printGraphics;
 
-
-            if (_antiAlias)
+            if (UseAntiAlias)
             {
                 _graphics.TextRenderingHint = TextRenderingHint.AntiAlias;
                 _graphics.SmoothingMode = SmoothingMode.AntiAlias;
@@ -129,13 +99,11 @@ namespace System.Drawing.Printing
         /// </summary>
         public override void OnEndPage(PrintDocument document, PrintPageEventArgs e)
         {
-            Debug.Assert(_dc != null && _graphics != null, "PrintController methods called in the wrong order?");
-
-            // For security purposes, don't assume our public methods are called in any particular order
-            CheckSecurity();
-
-            _graphics.Dispose();
-            _graphics = null;
+            if (_graphics != null)
+            {
+                _graphics.Dispose();
+                _graphics = null;
+            }
 
             base.OnEndPage(document, e);
         }
@@ -145,37 +113,13 @@ namespace System.Drawing.Printing
         /// </summary>
         public override void OnEndPrint(PrintDocument document, PrintEventArgs e)
         {
-            Debug.Assert(_dc != null && _graphics == null, "PrintController methods called in the wrong order?");
-
-            // For security purposes, don't assume our public methods are called in any particular order
-            CheckSecurity();
-
-            _dc.Dispose();
-            _dc = null;
-
-            base.OnEndPrint(document, e);
-        }
-
-        public PreviewPageInfo[] GetPreviewPageInfo()
-        {
-            // For security purposes, don't assume our public methods are called in any particular order
-            CheckSecurity();
-
-            PreviewPageInfo[] temp = new PreviewPageInfo[_list.Count];
-            _list.CopyTo(temp, 0);
-            return temp;
-        }
-
-        public virtual bool UseAntiAlias
-        {
-            get
+            if (_dc != null)
             {
-                return _antiAlias;
-            }
-            set
-            {
-                _antiAlias = value;
+                _dc.Dispose();
+                _dc = null;
             }
+
+            base.OnEndPrint(document, e);
         }
     }
 }
diff --git a/src/libraries/System.Drawing.Common/src/System/Drawing/Printing/PreviewPrintController.cs b/src/libraries/System.Drawing.Common/src/System/Drawing/Printing/PreviewPrintController.cs
new file mode 100644 (file)
index 0000000..1fc9f0e
--- /dev/null
@@ -0,0 +1,25 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System.Collections;
+using System.Drawing.Imaging;
+
+namespace System.Drawing.Printing
+{
+    public partial class PreviewPrintController : PrintController
+    {
+        private readonly IList _list = new ArrayList();
+
+        public override bool IsPreview => true;
+
+        public virtual bool UseAntiAlias { get; set; }
+
+        public PreviewPageInfo[] GetPreviewPageInfo()
+        {
+            var temp = new PreviewPageInfo[_list.Count];
+            _list.CopyTo(temp, 0);
+            return temp;
+        }
+    }
+}
index 60205cc..cef21ce 100644 (file)
 
 namespace System.Drawing.Printing
 {
-
-    public abstract class PrintController
+    public abstract partial class PrintController
     {
-
-        public virtual bool IsPreview
-        {
-            get { return false; }
-        }
-        public virtual void OnEndPage(PrintDocument document, PrintPageEventArgs e)
-        {
-        }
-
         public virtual void OnStartPrint(PrintDocument document, PrintEventArgs e)
         {
         }
@@ -50,10 +40,5 @@ namespace System.Drawing.Printing
         public virtual void OnEndPrint(PrintDocument document, PrintEventArgs e)
         {
         }
-
-        public virtual Graphics OnStartPage(PrintDocument document, PrintPageEventArgs e)
-        {
-            return null;
-        }
     }
 }
index 2780d94..8dec0c4 100644 (file)
@@ -10,47 +10,47 @@ namespace System.Drawing.Printing
     /// <summary>
     /// Controls how a document is printed.
     /// </summary>
-    public abstract class PrintController
+    public abstract partial class PrintController
     {
-        // DEVMODEs are pretty expensive, so we cache one here and share it with the
-        // Standard and Preview print controllers.  If it weren't for all the rules about API changes,
-        // I'd consider making this protected.
-
-        #region SafeDeviceModeHandle Class
-
         /// <summary>
         /// Represents a SafeHandle for a Printer's Device Mode struct handle (DEVMODE)
         /// </summary>
+        /// <remarks>
+        /// DEVMODEs are pretty expensive, so we cache one here and share it
+        /// with the Standard and Preview print controllers.
+        /// </remarks>
         internal sealed class SafeDeviceModeHandle : SafeHandle
         {
-            // This constructor is used by the P/Invoke marshaling layer
-            // to allocate a SafeHandle instance.  P/Invoke then does the
-            // appropriate method call, storing the handle in this class.
-            private SafeDeviceModeHandle() : base(IntPtr.Zero, true) { return; }
-
-            internal SafeDeviceModeHandle(IntPtr handle)
-                : base(IntPtr.Zero, true)  // "true" means "owns the handle"
+            /// <summary>
+            /// This constructor is used by the P/Invoke marshaling layer
+            /// to allocate a SafeHandle instance. P/Invoke then does the
+            /// appropriate method call, storing the handle in this class.
+            /// </summary>
+            private SafeDeviceModeHandle() : base(IntPtr.Zero, ownsHandle: true)
             {
-                SetHandle(handle);
             }
 
-            public override bool IsInvalid
+            internal SafeDeviceModeHandle(IntPtr handle) : base(IntPtr.Zero, ownsHandle: true)
             {
-                get { return handle == IntPtr.Zero; }
+                SetHandle(handle);
             }
 
-            // Specifies how to free the handle.
-            // The boolean returned should be true for success and false if the runtime
-            // should fire a SafeHandleCriticalFailure MDA (CustomerDebugProbe) if that
-            // MDA is enabled.
+            public override bool IsInvalid => handle == IntPtr.Zero;
+
+            /// <summary>
+            /// Specifies how to free the handle.
+            /// The boolean returned should be true for success and false if the runtime
+            /// should fire a SafeHandleCriticalFailure MDA (CustomerDebugProbe) if that
+            /// MDA is enabled.
+            /// </summary>
             protected override bool ReleaseHandle()
             {
                 if (!IsInvalid)
                 {
-                    SafeNativeMethods.GlobalFree(new HandleRef(this, handle));
+                    Interop.Kernel32.GlobalFree(new HandleRef(this, handle));
                 }
-                handle = IntPtr.Zero;
 
+                handle = IntPtr.Zero;
                 return true;
             }
 
@@ -65,34 +65,14 @@ namespace System.Drawing.Printing
             }
         }
 
-        #endregion
-
-        internal SafeDeviceModeHandle modeHandle = null;
-
-        /// <summary>
-        /// Initializes a new instance of the <see cref='PrintController'/> class.
-        /// </summary>
-        protected PrintController()
-        {
-        }
-
-
-        /// <summary>
-        /// This is new public property which notifies if this controller is used for PrintPreview.
-        /// </summary>
-        public virtual bool IsPreview
-        {
-            get
-            {
-                return false;
-            }
-        }
+        private protected SafeDeviceModeHandle _modeHandle = null;
 
-        // WARNING: if you have nested PrintControllers, this method won't get called on the inner one.
-        // Add initialization code to StartPrint or StartPage instead.
+        /// <remarks>
+        /// If you have nested PrintControllers, this method won't get called on the inner one.
+        /// Add initialization code to StartPrint or StartPage instead.
+        /// </remarks>
         internal void Print(PrintDocument document)
         {
-            //
             // Get the PrintAction for this event
             PrintAction printAction;
             if (IsPreview)
@@ -106,17 +86,17 @@ namespace System.Drawing.Printing
 
             // Check that user has permission to print to this particular printer
             PrintEventArgs printEvent = new PrintEventArgs(printAction);
-            document._OnBeginPrint(printEvent);
+            document.OnBeginPrint(printEvent);
             if (printEvent.Cancel)
             {
-                document._OnEndPrint(printEvent);
+                document.OnEndPrint(printEvent);
                 return;
             }
 
             OnStartPrint(document, printEvent);
             if (printEvent.Cancel)
             {
-                document._OnEndPrint(printEvent);
+                document.OnEndPrint(printEvent);
                 OnEndPrint(document, printEvent);
                 return;
             }
@@ -136,7 +116,7 @@ namespace System.Drawing.Printing
             {
                 try
                 {
-                    document._OnEndPrint(printEvent);
+                    document.OnEndPrint(printEvent);
                     printEvent.Cancel = canceled | printEvent.Cancel;
                 }
                 finally
@@ -146,15 +126,19 @@ namespace System.Drawing.Printing
             }
         }
 
-        // Returns true if print was aborted.
-        // WARNING: if you have nested PrintControllers, this method won't get called on the inner one
-        // Add initialization code to StartPrint or StartPage instead.
+        /// <summary>
+        /// Returns true if print was aborted.
+        /// </summary>
+        /// <remarks>
+        /// If you have nested PrintControllers, this method won't get called on the inner one
+        /// Add initialization code to StartPrint or StartPage instead.
+        /// </remarks>
         private bool PrintLoop(PrintDocument document)
         {
             QueryPageSettingsEventArgs queryEvent = new QueryPageSettingsEventArgs((PageSettings)document.DefaultPageSettings.Clone());
             while (true)
             {
-                document._OnQueryPageSettings(queryEvent);
+                document.OnQueryPageSettings(queryEvent);
                 if (queryEvent.Cancel)
                 {
                     return true;
@@ -166,7 +150,7 @@ namespace System.Drawing.Printing
 
                 try
                 {
-                    document._OnPrintPage(pageEvent);
+                    document.OnPrintPage(pageEvent);
                     OnEndPage(document, pageEvent);
                 }
                 finally
@@ -182,10 +166,6 @@ namespace System.Drawing.Printing
                 {
                     return false;
                 }
-                else
-                {
-                    // loop
-                }
             }
         }
 
@@ -197,7 +177,7 @@ namespace System.Drawing.Printing
             while (true)
             {
                 queryEvent.PageSettingsChanged = false;
-                document._OnQueryPageSettings(queryEvent);
+                document.OnQueryPageSettings(queryEvent);
                 if (queryEvent.Cancel)
                 {
                     return true;
@@ -231,7 +211,7 @@ namespace System.Drawing.Printing
 
                 try
                 {
-                    document._OnPrintPage(pageEvent);
+                    document.OnPrintPage(pageEvent);
                     OnEndPage(document, pageEvent);
                 }
                 finally
@@ -253,10 +233,10 @@ namespace System.Drawing.Printing
 
         private PrintPageEventArgs CreatePrintPageEvent(PageSettings pageSettings)
         {
-            Debug.Assert((modeHandle != null), "modeHandle is null.  Someone must have forgot to call base.StartPrint");
+            Debug.Assert((_modeHandle != null), "modeHandle is null.  Someone must have forgot to call base.StartPrint");
 
 
-            Rectangle pageBounds = pageSettings.GetBounds(modeHandle);
+            Rectangle pageBounds = pageSettings.GetBounds(_modeHandle);
             Rectangle marginBounds = new Rectangle(pageSettings.Margins.Left,
                                                    pageSettings.Margins.Top,
                                                    pageBounds.Width - (pageSettings.Margins.Left + pageSettings.Margins.Right),
@@ -266,28 +246,12 @@ namespace System.Drawing.Printing
             return pageEvent;
         }
 
-
         /// <summary>
         /// When overridden in a derived class, begins the control sequence of when and how to print a document.
         /// </summary>
         public virtual void OnStartPrint(PrintDocument document, PrintEventArgs e)
         {
-            modeHandle = (SafeDeviceModeHandle)document.PrinterSettings.GetHdevmode(document.DefaultPageSettings);
-        }
-
-        /// <summary>
-        /// When overridden in a derived class, begins the control sequence of when and how to print a page in a document.
-        /// </summary>
-        public virtual Graphics OnStartPage(PrintDocument document, PrintPageEventArgs e)
-        {
-            return null;
-        }
-
-        /// <summary>
-        /// When overridden in a derived class, completes the control sequence of when and how to print a page in a document.
-        /// </summary>
-        public virtual void OnEndPage(PrintDocument document, PrintPageEventArgs e)
-        {
+            _modeHandle = (SafeDeviceModeHandle)document.PrinterSettings.GetHdevmode(document.DefaultPageSettings);
         }
 
         /// <summary>
@@ -295,11 +259,7 @@ namespace System.Drawing.Printing
         /// </summary>
         public virtual void OnEndPrint(PrintDocument document, PrintEventArgs e)
         {
-            Debug.Assert((modeHandle != null), "modeHandle is null.  Someone must have forgot to call base.StartPrint");
-            if (modeHandle != null)
-            {
-                modeHandle.Close();
-            }
+            _modeHandle?.Close();
         }
     }
 }
diff --git a/src/libraries/System.Drawing.Common/src/System/Drawing/Printing/PrintController.cs b/src/libraries/System.Drawing.Common/src/System/Drawing/Printing/PrintController.cs
new file mode 100644 (file)
index 0000000..c7b7d74
--- /dev/null
@@ -0,0 +1,30 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+namespace System.Drawing.Printing
+{
+    public abstract partial class PrintController
+    {
+        protected PrintController()
+        {
+        }
+
+        public virtual bool IsPreview => false;
+
+        /// <summary>
+        /// When overridden in a derived class, begins the control sequence of when and how to print a page in a document.
+        /// </summary>
+        public virtual Graphics OnStartPage(PrintDocument document, PrintPageEventArgs e)
+        {
+            return null;
+        }
+
+        /// <summary>
+        /// When overridden in a derived class, completes the control sequence of when and how to print a page in a document.
+        /// </summary>
+        public virtual void OnEndPage(PrintDocument document, PrintPageEventArgs e)
+        {
+        }
+    }
+}
index 7c8b8f3..ea844d5 100644 (file)
@@ -205,60 +205,36 @@ namespace System.Drawing.Printing
             }
         }
 
-        internal void _OnBeginPrint(PrintEventArgs e)
-        {
-            OnBeginPrint(e);
-        }
-
         /// <summary>
         /// Raises the <see cref='BeginPrint'/> event.
         /// </summary>
-        protected virtual void OnBeginPrint(PrintEventArgs e)
-        {
-            if (_beginPrintHandler != null)
-                _beginPrintHandler(this, e);
-        }
-
-        internal void _OnEndPrint(PrintEventArgs e)
+        protected internal virtual void OnBeginPrint(PrintEventArgs e)
         {
-            OnEndPrint(e);
+            _beginPrintHandler?.Invoke(this, e);
         }
 
         /// <summary>
         /// Raises the <see cref='EndPrint'/> event.
         /// </summary>
-        protected virtual void OnEndPrint(PrintEventArgs e)
+        protected internal virtual void OnEndPrint(PrintEventArgs e)
         {
-            if (_endPrintHandler != null)
-                _endPrintHandler(this, e);
-        }
-
-        internal void _OnPrintPage(PrintPageEventArgs e)
-        {
-            OnPrintPage(e);
+            _endPrintHandler?.Invoke(this, e);
         }
 
         /// <summary>
         /// Raises the <see cref='PrintPage'/> event.
         /// </summary>
-        protected virtual void OnPrintPage(PrintPageEventArgs e)
-        {
-            if (_printPageHandler != null)
-                _printPageHandler(this, e);
-        }
-
-        internal void _OnQueryPageSettings(QueryPageSettingsEventArgs e)
+        protected internal virtual void OnPrintPage(PrintPageEventArgs e)
         {
-            OnQueryPageSettings(e);
+            _printPageHandler?.Invoke(this, e);
         }
 
         /// <summary>
         /// Raises the <see cref='QueryPageSettings'/> event.
         /// </summary>
-        protected virtual void OnQueryPageSettings(QueryPageSettingsEventArgs e)
+        protected internal virtual void OnQueryPageSettings(QueryPageSettingsEventArgs e)
         {
-            if (_queryHandler != null)
-                _queryHandler(this, e);
+            _queryHandler?.Invoke(this, e);
         }
 
         /// <summary>
index 97b8824..540e00b 100644 (file)
@@ -566,16 +566,16 @@ namespace System.Drawing.Printing
             }
             finally
             {
-                SafeNativeMethods.GlobalFree(new HandleRef(null, modeHandle));
+                Interop.Kernel32.GlobalFree(modeHandle);
             }
             return dc;
         }
 
         internal DeviceContext CreateDeviceContext(IntPtr hdevmode)
         {
-            IntPtr modePointer = SafeNativeMethods.GlobalLock(new HandleRef(null, hdevmode));
+            IntPtr modePointer = Interop.Kernel32.GlobalLock(hdevmode);
             DeviceContext dc = DeviceContext.CreateDC(DriverName, PrinterNameInternal, (string)null, new HandleRef(null, modePointer));
-            SafeNativeMethods.GlobalUnlock(new HandleRef(null, hdevmode));
+            Interop.Kernel32.GlobalUnlock(hdevmode);
             return dc;
         }
 
@@ -594,7 +594,7 @@ namespace System.Drawing.Printing
             }
             finally
             {
-                SafeNativeMethods.GlobalFree(new HandleRef(null, modeHandle));
+                Interop.Kernel32.GlobalFree(modeHandle);
             }
             return dc;
         }
@@ -602,9 +602,9 @@ namespace System.Drawing.Printing
         // A read-only DC, which is faster than CreateHdc
         internal DeviceContext CreateInformationContext(IntPtr hdevmode)
         {
-            IntPtr modePointer = SafeNativeMethods.GlobalLock(new HandleRef(null, hdevmode));
+            IntPtr modePointer = Interop.Kernel32.GlobalLock(hdevmode);
             DeviceContext dc = DeviceContext.CreateIC(DriverName, PrinterNameInternal, (string)null, new HandleRef(null, modePointer));
-            SafeNativeMethods.GlobalUnlock(new HandleRef(null, hdevmode));
+            Interop.Kernel32.GlobalUnlock(hdevmode);
             return dc;
         }
 
@@ -735,17 +735,17 @@ namespace System.Drawing.Printing
                     return SR.NoDefaultPrinter;
 
                 IntPtr handle = data.hDevNames;
-                IntPtr names = SafeNativeMethods.GlobalLock(new HandleRef(data, handle));
+                IntPtr names = Interop.Kernel32.GlobalLock(new HandleRef(data, handle));
                 if (names == IntPtr.Zero)
                     throw new Win32Exception();
 
                 string name = ReadOneDEVNAME(names, 1);
-                SafeNativeMethods.GlobalUnlock(new HandleRef(data, handle));
+                Interop.Kernel32.GlobalUnlock(new HandleRef(data, handle));
                 names = IntPtr.Zero;
 
                 // Windows allocates them, but we have to free them
-                SafeNativeMethods.GlobalFree(new HandleRef(data, data.hDevNames));
-                SafeNativeMethods.GlobalFree(new HandleRef(data, data.hDevMode));
+                Interop.Kernel32.GlobalFree(new HandleRef(data, data.hDevNames));
+                Interop.Kernel32.GlobalFree(new HandleRef(data, data.hDevMode));
 
                 return name;
             }
@@ -759,17 +759,17 @@ namespace System.Drawing.Printing
                     return SR.NoDefaultPrinter;
 
                 IntPtr handle = data.hDevNames;
-                IntPtr names = SafeNativeMethods.GlobalLock(new HandleRef(data, handle));
+                IntPtr names = Interop.Kernel32.GlobalLock(new HandleRef(data, handle));
                 if (names == IntPtr.Zero)
                     throw new Win32Exception();
 
                 string name = ReadOneDEVNAME(names, 1);
-                SafeNativeMethods.GlobalUnlock(new HandleRef(data, handle));
+                Interop.Kernel32.GlobalUnlock(new HandleRef(data, handle));
                 names = IntPtr.Zero;
 
                 // Windows allocates them, but we have to free them
-                SafeNativeMethods.GlobalFree(new HandleRef(data, data.hDevNames));
-                SafeNativeMethods.GlobalFree(new HandleRef(data, data.hDevMode));
+                Interop.Kernel32.GlobalFree(new HandleRef(data, data.hDevNames));
+                Interop.Kernel32.GlobalFree(new HandleRef(data, data.hDevMode));
 
                 return name;
             }
@@ -788,18 +788,18 @@ namespace System.Drawing.Printing
                     return SR.NoDefaultPrinter;
 
                 IntPtr handle = data.hDevNames;
-                IntPtr names = SafeNativeMethods.GlobalLock(new HandleRef(data, handle));
+                IntPtr names = Interop.Kernel32.GlobalLock(new HandleRef(data, handle));
                 if (names == IntPtr.Zero)
                     throw new Win32Exception();
 
                 string name = ReadOneDEVNAME(names, 2);
 
-                SafeNativeMethods.GlobalUnlock(new HandleRef(data, handle));
+                Interop.Kernel32.GlobalUnlock(new HandleRef(data, handle));
                 names = IntPtr.Zero;
 
                 // Windows allocates them, but we have to free them
-                SafeNativeMethods.GlobalFree(new HandleRef(data, data.hDevNames));
-                SafeNativeMethods.GlobalFree(new HandleRef(data, data.hDevMode));
+                Interop.Kernel32.GlobalFree(new HandleRef(data, data.hDevNames));
+                Interop.Kernel32.GlobalFree(new HandleRef(data, data.hDevMode));
 
                 return name;
             }
@@ -813,18 +813,18 @@ namespace System.Drawing.Printing
                     return SR.NoDefaultPrinter;
 
                 IntPtr handle = data.hDevNames;
-                IntPtr names = SafeNativeMethods.GlobalLock(new HandleRef(data, handle));
+                IntPtr names = Interop.Kernel32.GlobalLock(new HandleRef(data, handle));
                 if (names == IntPtr.Zero)
                     throw new Win32Exception();
 
                 string name = ReadOneDEVNAME(names, 2);
 
-                SafeNativeMethods.GlobalUnlock(new HandleRef(data, handle));
+                Interop.Kernel32.GlobalUnlock(new HandleRef(data, handle));
                 names = IntPtr.Zero;
 
                 // Windows allocates them, but we have to free them
-                SafeNativeMethods.GlobalFree(new HandleRef(data, data.hDevNames));
-                SafeNativeMethods.GlobalFree(new HandleRef(data, data.hDevMode));
+                Interop.Kernel32.GlobalFree(new HandleRef(data, data.hDevNames));
+                Interop.Kernel32.GlobalFree(new HandleRef(data, data.hDevMode));
 
                 return name;
             }
@@ -854,12 +854,11 @@ namespace System.Drawing.Printing
         /// <summary>
         /// Creates a handle to a DEVMODE structure which correspond too the printer settings.When you are done with the
         /// handle, you must deallocate it yourself:
-        ///   Windows.GlobalFree(handle);
+        ///   Interop.Kernel32.GlobalFree(handle);
         ///   Where "handle" is the return value from this method.
         /// </summary>
         public IntPtr GetHdevmode()
         {
-            // Don't assert unmanaged code -- anyone using handles should have unmanaged code permission
             IntPtr modeHandle = GetHdevmodeInternal();
             _defaultPageSettings.CopyToHdevmode(modeHandle);
             return modeHandle;
@@ -881,7 +880,7 @@ namespace System.Drawing.Printing
                 throw new InvalidPrinterException(this);
             }
             IntPtr handle = SafeNativeMethods.GlobalAlloc(SafeNativeMethods.GMEM_MOVEABLE, (uint)modeSize); // cannot be <0 anyway
-            IntPtr pointer = SafeNativeMethods.GlobalLock(new HandleRef(null, handle));
+            IntPtr pointer = Interop.Kernel32.GlobalLock(handle);
 
             //Get the DevMode only if its not cached....
             if (_cachedDevmode != null)
@@ -933,20 +932,20 @@ namespace System.Drawing.Printing
             int retCode = SafeNativeMethods.DocumentProperties(NativeMethods.NullHandleRef, NativeMethods.NullHandleRef, printer, pointer, pointer, SafeNativeMethods.DM_IN_BUFFER | SafeNativeMethods.DM_OUT_BUFFER);
             if (retCode < 0)
             {
-                SafeNativeMethods.GlobalFree(new HandleRef(null, handle));
-                SafeNativeMethods.GlobalUnlock(new HandleRef(null, handle));
+                Interop.Kernel32.GlobalFree(handle);
+                Interop.Kernel32.GlobalUnlock(handle);
                 return IntPtr.Zero;
             }
 
 
-            SafeNativeMethods.GlobalUnlock(new HandleRef(null, handle));
+            Interop.Kernel32.GlobalUnlock(handle);
             return handle;
         }
 
         /// <summary>
         /// Creates a handle to a DEVMODE structure which correspond to the printer and page settings.
         /// When you are done with the handle, you must deallocate it yourself:
-        ///   Windows.GlobalFree(handle);
+        ///   Interop.Kernel32.GlobalFree(handle);
         ///   Where "handle" is the return value from this method.
         /// </summary>
         public IntPtr GetHdevmode(PageSettings pageSettings)
@@ -960,7 +959,7 @@ namespace System.Drawing.Printing
         /// <summary>
         /// Creates a handle to a DEVNAMES structure which correspond to the printer settings.
         /// When you are done with the handle, you must deallocate it yourself:
-        ///   Windows.GlobalFree(handle);
+        ///   Interop.Kernel32.GlobalFree(handle);
         ///   Where "handle" is the return value from this method.
         /// </summary>
         public IntPtr GetHdevnames()
@@ -977,7 +976,7 @@ namespace System.Drawing.Printing
             short offset = (short)(8 / Marshal.SystemDefaultCharSize); // Offsets are in characters, not bytes
             uint namesSize = (uint)checked(Marshal.SystemDefaultCharSize * (offset + namesCharacters)); // always >0
             IntPtr handle = SafeNativeMethods.GlobalAlloc(SafeNativeMethods.GMEM_MOVEABLE | SafeNativeMethods.GMEM_ZEROINIT, namesSize);
-            IntPtr namesPointer = SafeNativeMethods.GlobalLock(new HandleRef(null, handle));
+            IntPtr namesPointer = Interop.Kernel32.GlobalLock(handle);
 
             Marshal.WriteInt16(namesPointer, offset); // wDriverOffset
             offset += WriteOneDEVNAME(driver, namesPointer, offset);
@@ -987,7 +986,7 @@ namespace System.Drawing.Printing
             offset += WriteOneDEVNAME(outPort, namesPointer, offset);
             Marshal.WriteInt16((IntPtr)(checked((long)namesPointer + 6)), offset); // wDefault
 
-            SafeNativeMethods.GlobalUnlock(new HandleRef(null, handle));
+            Interop.Kernel32.GlobalUnlock(handle);
             return handle;
         }
 
@@ -1016,7 +1015,7 @@ namespace System.Drawing.Printing
                     }
                 }
 
-                IntPtr modePointer = SafeNativeMethods.GlobalLock(new HandleRef(this, modeHandle));
+                IntPtr modePointer = Interop.Kernel32.GlobalLock(new HandleRef(this, modeHandle));
                 SafeNativeMethods.DEVMODE mode = (SafeNativeMethods.DEVMODE)Marshal.PtrToStructure(modePointer, typeof(SafeNativeMethods.DEVMODE));
                 switch (field)
                 {
@@ -1061,13 +1060,13 @@ namespace System.Drawing.Printing
                         result = defaultValue;
                         break;
                 }
-                SafeNativeMethods.GlobalUnlock(new HandleRef(this, modeHandle));
+                Interop.Kernel32.GlobalUnlock(new HandleRef(this, modeHandle));
             }
             finally
             {
                 if (ownHandle)
                 {
-                    SafeNativeMethods.GlobalFree(new HandleRef(this, modeHandle));
+                    Interop.Kernel32.GlobalFree(new HandleRef(this, modeHandle));
                 }
             }
             return result;
@@ -1209,7 +1208,7 @@ namespace System.Drawing.Printing
             if (hdevmode == IntPtr.Zero)
                 throw new ArgumentException(SR.Format(SR.InvalidPrinterHandle, hdevmode));
 
-            IntPtr pointer = SafeNativeMethods.GlobalLock(new HandleRef(null, hdevmode));
+            IntPtr pointer = Interop.Kernel32.GlobalLock(hdevmode);
             SafeNativeMethods.DEVMODE mode = (SafeNativeMethods.DEVMODE)Marshal.PtrToStructure(pointer, typeof(SafeNativeMethods.DEVMODE));
 
             //Copy entire public devmode as a byte array...
@@ -1243,7 +1242,7 @@ namespace System.Drawing.Printing
                 _collate = (mode.dmCollate == SafeNativeMethods.DMCOLLATE_TRUE);
             }
 
-            SafeNativeMethods.GlobalUnlock(new HandleRef(null, hdevmode));
+            Interop.Kernel32.GlobalUnlock(hdevmode);
         }
 
         /// <summary>
@@ -1252,9 +1251,11 @@ namespace System.Drawing.Printing
         public void SetHdevnames(IntPtr hdevnames)
         {
             if (hdevnames == IntPtr.Zero)
+            {
                 throw new ArgumentException(SR.Format(SR.InvalidPrinterHandle, hdevnames));
+            }
 
-            IntPtr namesPointer = SafeNativeMethods.GlobalLock(new HandleRef(null, hdevnames));
+            IntPtr namesPointer = Interop.Kernel32.GlobalLock(hdevnames);
 
             _driverName = ReadOneDEVNAME(namesPointer, 0);
             _printerName = ReadOneDEVNAME(namesPointer, 1);
@@ -1262,7 +1263,7 @@ namespace System.Drawing.Printing
 
             PrintDialogDisplayed = true;
 
-            SafeNativeMethods.GlobalUnlock(new HandleRef(null, hdevnames));
+            Interop.Kernel32.GlobalUnlock(hdevnames);
         }
 
         /// <summary>
diff --git a/src/libraries/System.Drawing.Common/tests/Printing/PreviewPrintControllerTests.cs b/src/libraries/System.Drawing.Common/tests/Printing/PreviewPrintControllerTests.cs
new file mode 100644 (file)
index 0000000..98003b4
--- /dev/null
@@ -0,0 +1,148 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System.Collections.Generic;
+using Xunit;
+
+namespace System.Drawing.Printing.Tests
+{
+    public class PreviewPrintControllerTests
+    {
+        [Fact]
+        public void Ctor_Default()
+        {
+            var controller = new PreviewPrintController();
+            Assert.True(controller.IsPreview);
+        }
+
+        [ConditionalFact(Helpers.AnyInstalledPrinters, Helpers.IsDrawingSupported)]
+        public void OnStartPage_InvokeWithPrint_ReturnsNull()
+        {
+            using (var document = new PrintDocument())
+            {
+                var controller = new PreviewPrintController();
+                controller.OnStartPrint(document, new PrintEventArgs());
+
+                var printEventArgs = new PrintPageEventArgs(null, Rectangle.Empty, Rectangle.Empty, new PageSettings());
+                Assert.NotNull(controller.OnStartPage(document, printEventArgs));
+
+                // Call OnEndPage.
+                controller.OnEndPage(document, printEventArgs);
+
+                // Call EndPrint.
+                controller.OnEndPrint(document, new PrintEventArgs());
+            }
+        }
+
+        [Fact]
+        public void OnStartPage_InvokeNullDocument_ThrowsNullReferenceException()
+        {
+            var controller = new PreviewPrintController();
+            var e = new PrintPageEventArgs(null, Rectangle.Empty, Rectangle.Empty, null);
+            Assert.Throws<NullReferenceException>(() => controller.OnStartPage(null, e));
+        }
+
+        [Fact]
+        public void OnStartPage_InvokeNullEventArgs_ThrowsNullReferenceException()
+        {
+            using (var document = new PrintDocument())
+            {
+                var controller = new PreviewPrintController();
+                Assert.Throws<NullReferenceException>(() => controller.OnStartPage(document, null));
+            }
+        }
+
+        [ConditionalFact(Helpers.AnyInstalledPrinters, Helpers.IsDrawingSupported)]
+        public void OnStartPage_InvokeNullEventArgsPageSettings_ReturnsNull()
+        {
+            using (var document = new PrintDocument())
+            {
+                var controller = new PreviewPrintController();
+                controller.OnStartPrint(document, new PrintEventArgs());
+
+                var printEventArgs = new PrintPageEventArgs(null, Rectangle.Empty, Rectangle.Empty, null);
+                Assert.Throws<NullReferenceException>(() => controller.OnStartPage(document, printEventArgs));
+            }
+        }
+
+        [Fact]
+        public void OnStartPage_PrintNotStarted_ThrowsNullReferenceException()
+        {
+            using (var document = new PrintDocument())
+            {
+                var controller = new PreviewPrintController();
+                var e = new PrintPageEventArgs(null, Rectangle.Empty, Rectangle.Empty, null);
+                Assert.Throws<NullReferenceException>(() => controller.OnStartPage(document, e));
+            }
+        }
+
+        [ConditionalFact(Helpers.IsDrawingSupported)]
+        [SkipOnTargetFramework(TargetFrameworkMonikers.NetFramework, "Fixed a NullReferenceException")]
+        public void OnEndPage_InvokeWithoutStarting_Nop()
+        {
+            using (var document = new PrintDocument())
+            {
+                var controller = new PreviewPrintController();
+                controller.OnEndPage(document, new PrintPageEventArgs(null, Rectangle.Empty, Rectangle.Empty, null));
+                controller.OnEndPage(null, null);
+            }
+        }
+
+        public static IEnumerable<object[]> PrintEventArgs_TestData()
+        {
+            yield return new object[] { null };
+            yield return new object[] { new PrintEventArgs() };
+        }
+
+        [ConditionalTheory(Helpers.AnyInstalledPrinters, Helpers.IsDrawingSupported)]
+        [MemberData(nameof(PrintEventArgs_TestData))]
+        public void OnStartPrint_InvokeWithDocument_Success(PrintEventArgs e)
+        {
+            using (var document = new PrintDocument())
+            {
+                var controller = new PreviewPrintController();
+                controller.OnStartPrint(document, e);
+
+                // Call OnEndPrint
+                controller.OnEndPrint(document, e);
+            }
+        }
+
+        [ConditionalFact(Helpers.AnyInstalledPrinters, Helpers.IsDrawingSupported)]
+        [PlatformSpecific(TestPlatforms.Windows)]
+        public void OnStartPrint_InvokeMultipleTimes_Success()
+        {
+            using (var document = new PrintDocument())
+            {
+                var controller = new PreviewPrintController();
+                controller.OnStartPrint(document, new PrintEventArgs());
+                controller.OnStartPrint(document, new PrintEventArgs());
+
+                // Call OnEndPrint
+                controller.OnEndPrint(document, new PrintEventArgs());
+            }
+        }
+
+        [Fact]
+        public void OnStartPrint_InvokeNullDocument_ThrowsNullReferenceException()
+        {
+            var controller = new PreviewPrintController();
+            Assert.Throws<NullReferenceException>(() => controller.OnStartPrint(null, new PrintEventArgs()));
+        }
+
+        [Theory]
+        [MemberData(nameof(PrintEventArgs_TestData))]
+        [PlatformSpecific(TestPlatforms.Windows)]
+        [SkipOnTargetFramework(TargetFrameworkMonikers.NetFramework, "Fixed a NullReferenceException")]
+        public void OnEndPrint_InvokeWithoutStarting_Nop(PrintEventArgs e)
+        {
+            using (var document = new PrintDocument())
+            {
+                var controller = new PreviewPrintController();
+                controller.OnEndPrint(document, e);
+                controller.OnEndPrint(null, e);
+            }
+        }
+    }
+}
diff --git a/src/libraries/System.Drawing.Common/tests/Printing/PrintControllerTests.cs b/src/libraries/System.Drawing.Common/tests/Printing/PrintControllerTests.cs
new file mode 100644 (file)
index 0000000..4292773
--- /dev/null
@@ -0,0 +1,119 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System.Collections.Generic;
+using Xunit;
+
+namespace System.Drawing.Printing.Tests
+{
+    public class PrintControllerTests
+    {
+        [Fact]
+        public void Ctor_Default()
+        {
+            var controller = new SubPrintController();
+            Assert.False(controller.IsPreview);
+        }
+
+        [ConditionalFact(Helpers.AnyInstalledPrinters, Helpers.IsDrawingSupported)]
+        public void OnStartPage_InvokeWithPrint_ReturnsNull()
+        {
+            using (var document = new PrintDocument())
+            {
+                var controller = new SubPrintController();
+                controller.OnStartPrint(document, new PrintEventArgs());
+
+                var printEventArgs = new PrintPageEventArgs(null, Rectangle.Empty, Rectangle.Empty, null);
+                Assert.Null(controller.OnStartPage(document, printEventArgs));
+
+                // Call OnEndPage.
+                controller.OnEndPage(document, printEventArgs);
+
+                // Call EndPrint.
+                controller.OnEndPrint(document, new PrintEventArgs());
+            }
+        }
+
+        [Fact]
+        public void OnStartPage_Invoke_ReturnsNull()
+        {
+            using (var document = new PrintDocument())
+            {
+                var controller = new SubPrintController();
+                Assert.Null(controller.OnStartPage(document, new PrintPageEventArgs(null, Rectangle.Empty, Rectangle.Empty, null)));
+                Assert.Null(controller.OnStartPage(null, null));
+            }
+        }
+
+        [Fact]
+        public void OnEndPage_InvokeWithoutStarting_Nop()
+        {
+            using (var document = new PrintDocument())
+            {
+                var controller = new SubPrintController();
+                controller.OnEndPage(document, new PrintPageEventArgs(null, Rectangle.Empty, Rectangle.Empty, null));
+                controller.OnEndPage(null, null);
+            }
+        }
+
+        public static IEnumerable<object[]> PrintEventArgs_TestData()
+        {
+            yield return new object[] { null };
+            yield return new object[] { new PrintEventArgs() };
+        }
+
+        [ConditionalTheory(Helpers.IsDrawingSupported)]
+        [MemberData(nameof(PrintEventArgs_TestData))]
+        public void OnStartPrint_InvokeWithDocument_Success(PrintEventArgs e)
+        {
+            using (var document = new PrintDocument())
+            {
+                var controller = new SubPrintController();
+                controller.OnStartPrint(document, e);
+
+                // Call OnEndPrint
+                controller.OnEndPrint(document, e);
+            }
+        }
+
+        [ConditionalTheory(Helpers.IsDrawingSupported)]
+        [MemberData(nameof(PrintEventArgs_TestData))]
+        public void OnStartPrint_InvokeWithDocumentSeveralTimes_Success(PrintEventArgs e)
+        {
+            using (var document = new PrintDocument())
+            {
+                var controller = new SubPrintController();
+                controller.OnStartPrint(document, e);
+                controller.OnStartPrint(document, e);
+
+                // Call OnEndPrint
+                controller.OnEndPrint(document, e);
+            }
+        }
+
+        [Fact]
+        [PlatformSpecific(TestPlatforms.Windows)] // In Unix is a no-op
+        public void OnStartPrint_InvokeNullDocument_ThrowsNullReferenceException()
+        {
+            var controller = new SubPrintController();
+            Assert.Throws<NullReferenceException>(() => controller.OnStartPrint(null, new PrintEventArgs()));
+        }
+
+        [Theory]
+        [MemberData(nameof(PrintEventArgs_TestData))]
+        public void OnEndPrint_InvokeWithoutStarting_Nop(PrintEventArgs e)
+        {
+            using (var document = new PrintDocument())
+            {
+                var controller = new SubPrintController();
+                controller.OnEndPrint(document, e);
+                controller.OnEndPrint(null, e);
+            }
+        }
+
+        private class SubPrintController : PrintController
+        {
+        }
+    }
+}
index 39a6f9b..4ff2ae2 100644 (file)
     <Compile Include="Imaging\ImageFormatTests.cs" />
     <Compile Include="Imaging\MetaHeaderTests.cs" />
     <Compile Include="Imaging\WmfPlaceableFileHeaderTests.cs" />
+    <Compile Include="Printing\PrintControllerTests.cs" />
     <Compile Include="Printing\PrintDocumentTests.cs" />
     <Compile Include="Printing\PrinterSettingsTests.cs" />
+    <Compile Include="Printing\PreviewPrintControllerTests.cs" />
     <Compile Include="RegionTests.cs" />
     <Compile Include="SolidBrushTests.cs" />
     <Compile Include="StringFormatTests.cs" />