|
//------------------------------------------------------------------------------
// <copyright file="PrintController.cs" company="Microsoft">
// Copyright (c) Microsoft Corporation. All rights reserved.
// </copyright>
//------------------------------------------------------------------------------
namespace System.Drawing.Printing {
using System;
using System.Configuration;
using System.Diagnostics;
using System.Drawing;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using System.Security;
/// <include file='doc\PrintController.uex' path='docs/doc[@for="PrintController"]/*' />
/// <devdoc>
/// <para>Controls how a document is printed.</para>
/// </devdoc>
public abstract 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>
/// <SecurityNote>
/// Critical: base class SafeHandle is critical
/// </SecurityNote>
[SecurityCritical]
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"
{
SetHandle(handle);
}
public override bool IsInvalid
{
get { return handle == IntPtr.Zero; }
}
// 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.
[SecurityCritical]
protected override bool ReleaseHandle()
{
if (!IsInvalid) {
SafeNativeMethods.GlobalFree(new HandleRef(this, handle));
}
handle = IntPtr.Zero;
return true;
}
public static implicit operator IntPtr(SafeDeviceModeHandle handle)
{
return (handle == null) ? IntPtr.Zero : handle.handle;
}
public static explicit operator SafeDeviceModeHandle(IntPtr handle)
{
return new SafeDeviceModeHandle(handle);
}
}
#endregion
internal SafeDeviceModeHandle modeHandle = null;
/// <include file='doc\PrintController.uex' path='docs/doc[@for="PrintController.PrintController"]/*' />
/// <devdoc>
/// <para>
/// Initializes a new instance of the <see cref='System.Drawing.Printing.PrintController'/> class.
/// </para>
/// </devdoc>
protected PrintController() {
IntSecurity.SafePrinting.Demand();
}
/// <include file='doc\PrintController.uex' path='docs/doc[@for="PrintController.IsPreview"]/*' />
/// <devdoc>
/// <para>
/// This is new public property which notifies if this controller is used for PrintPreview.
/// </para>
/// </devdoc>
public virtual bool IsPreview {
get{
return false;
}
}
// WARNING: if you have nested PrintControllers, this method won't get called on the inner one.
// Add initialization code to StartPrint or StartPage instead.
[ResourceExposure(ResourceScope.Process)]
[ResourceConsumption(ResourceScope.Process)]
internal void Print(PrintDocument document) {
IntSecurity.SafePrinting.Demand();
// Most of the printing security is left to the individual print controller
//
// Get the PrintAction for this event
PrintAction printAction;
if (IsPreview)
{
printAction = PrintAction.PrintToPreview;
}
else {
printAction = document.PrinterSettings.PrintToFile ? PrintAction.PrintToFile : PrintAction.PrintToPrinter;
}
// Check that user has permission to print to this particular printer
PrintEventArgs printEvent = new PrintEventArgs(printAction);
document._OnBeginPrint(printEvent);
if (printEvent.Cancel) {
document._OnEndPrint(printEvent);
return;
}
OnStartPrint(document, printEvent);
if (printEvent.Cancel) {
document._OnEndPrint(printEvent);
OnEndPrint(document, printEvent);
return;
}
bool canceled = true;
try {
// To enable optimization of the preview dialog, add the following to the config file:
// <runtime >
// <!-- AppContextSwitchOverrides values are in the form of 'key1=true|false;key2=true|false -->
// <AppContextSwitchOverrides value = "Switch.System.Drawing.Printing.OptimizePrintPreview=true" />
// </runtime >
canceled = LocalAppContextSwitches.OptimizePrintPreview ? PrintLoopOptimized(document) : PrintLoop(document);
}
finally {
try {
try {
document._OnEndPrint(printEvent);
printEvent.Cancel = canceled | printEvent.Cancel;
}
finally {
OnEndPrint(document, printEvent);
}
}
finally {
if (!IntSecurity.HasPermission(IntSecurity.AllPrinting)) {
// Ensure programs with SafePrinting only get to print once for each time they
// throw up the PrintDialog.
IntSecurity.AllPrinting.Assert();
document.PrinterSettings.PrintDialogDisplayed = false;
}
}
}
}
// 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.
private bool PrintLoop(PrintDocument document) {
QueryPageSettingsEventArgs queryEvent = new QueryPageSettingsEventArgs((PageSettings) document.DefaultPageSettings.Clone());
for (;;) {
document._OnQueryPageSettings(queryEvent);
if (queryEvent.Cancel) {
return true;
}
PrintPageEventArgs pageEvent = CreatePrintPageEvent(queryEvent.PageSettings);
Graphics graphics = OnStartPage(document, pageEvent);
pageEvent.SetGraphics(graphics);
try {
document._OnPrintPage(pageEvent);
OnEndPage(document, pageEvent);
}
finally {
pageEvent.Dispose();
}
if (pageEvent.Cancel) {
return true;
}
else if (!pageEvent.HasMorePages) {
return false;
}
else {
// loop
}
}
}
private bool PrintLoopOptimized(PrintDocument document) {
PrintPageEventArgs pageEvent = null;
PageSettings documentPageSettings = (PageSettings) document.DefaultPageSettings.Clone();
QueryPageSettingsEventArgs queryEvent = new QueryPageSettingsEventArgs(documentPageSettings);
for (;;) {
queryEvent.PageSettingsChanged = false;
document._OnQueryPageSettings(queryEvent);
if (queryEvent.Cancel) {
return true;
}
if (!queryEvent.PageSettingsChanged) {
// QueryPageSettings event handler did not change the page settings,
// thus we use default page settings from the document object.
if (pageEvent == null) {
pageEvent = CreatePrintPageEvent(queryEvent.PageSettings);
}
else {
// This is not the first page and the settings had not changed since the previous page,
// thus don't re-apply them.
pageEvent.CopySettingsToDevMode = false;
}
Graphics graphics = OnStartPage(document, pageEvent);
pageEvent.SetGraphics(graphics);
}
else {
// Page settings were customized, so use the customized ones in the start page event.
pageEvent = CreatePrintPageEvent(queryEvent.PageSettings);
Graphics graphics = OnStartPage(document, pageEvent);
pageEvent.SetGraphics(graphics);
}
try {
document._OnPrintPage(pageEvent);
OnEndPage(document, pageEvent);
}
finally {
pageEvent.Graphics.Dispose();
pageEvent.SetGraphics(null);
}
if (pageEvent.Cancel) {
return true;
}
else if (!pageEvent.HasMorePages) {
return false;
}
}
}
private PrintPageEventArgs CreatePrintPageEvent(PageSettings pageSettings) {
IntSecurity.AllPrintingAndUnmanagedCode.Assert();
Debug.Assert((modeHandle != null), "modeHandle is null. Someone must have forgot to call base.StartPrint");
Rectangle pageBounds = pageSettings.GetBounds(modeHandle);
Rectangle marginBounds = new Rectangle(pageSettings.Margins.Left,
pageSettings.Margins.Top,
pageBounds.Width - (pageSettings.Margins.Left + pageSettings.Margins.Right),
pageBounds.Height - (pageSettings.Margins.Top + pageSettings.Margins.Bottom));
PrintPageEventArgs pageEvent = new PrintPageEventArgs(null, marginBounds, pageBounds, pageSettings);
return pageEvent;
}
/// <include file='doc\PrintController.uex' path='docs/doc[@for="PrintController.OnStartPrint"]/*' />
/// <devdoc>
/// <para>When overridden in a derived class, begins the control sequence of when and how to print a document.</para>
/// </devdoc>
[ResourceExposure(ResourceScope.Process)]
[ResourceConsumption(ResourceScope.Process)]
public virtual void OnStartPrint(PrintDocument document, PrintEventArgs e) {
IntSecurity.AllPrintingAndUnmanagedCode.Assert();
modeHandle = (SafeDeviceModeHandle)document.PrinterSettings.GetHdevmode(document.DefaultPageSettings);
}
/// <include file='doc\PrintController.uex' path='docs/doc[@for="PrintController.OnStartPage"]/*' />
/// <devdoc>
/// <para>When overridden in a derived class, begins the control
/// sequence of when and how to print a page in a document.</para>
/// </devdoc>
public virtual Graphics OnStartPage(PrintDocument document, PrintPageEventArgs e) {
return null;
}
/// <include file='doc\PrintController.uex' path='docs/doc[@for="PrintController.OnEndPage"]/*' />
/// <devdoc>
/// <para>When overridden in a derived class, completes the control sequence of when and how
/// to print a page in a document.</para>
/// </devdoc>
public virtual void OnEndPage(PrintDocument document, PrintPageEventArgs e) {
}
/// <include file='doc\PrintController.uex' path='docs/doc[@for="PrintController.OnEndPrint"]/*' />
/// <devdoc>
/// <para>When overridden in a derived class, completes the
/// control sequence of when and how to print a document.</para>
/// </devdoc>
public virtual void OnEndPrint(PrintDocument document, PrintEventArgs e) {
IntSecurity.UnmanagedCode.Assert();
Debug.Assert((modeHandle != null), "modeHandle is null. Someone must have forgot to call base.StartPrint");
if (modeHandle != null) {
modeHandle.Close();
}
}
}
}
|