using System; using System.Collections.Generic; using System.Windows; using System.Windows.Controls; using System.Windows.Input; using System.Windows.Media; using System.Printing; using System.Drawing.Printing; using System.Text.RegularExpressions; using System.Runtime.InteropServices; /* DLLImport */ using System.Windows.Interop; namespace ghostnet_wpf_example { internal enum fModes { DM_SIZEOF = 0, DM_UPDATE = 1, DM_COPY = 2, DM_PROMPT = 4, DM_MODIFY = 8, DM_OUT_DEFAULT = DM_UPDATE, DM_OUT_BUFFER = DM_COPY, DM_IN_PROMPT = DM_PROMPT, DM_IN_BUFFER = DM_MODIFY, } /* Needed native methods for more advanced printing control */ [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] internal class PRINTER_INFO_9 { /// /// A pointer to a DEVMODE structure that defines the per-user /// default printer data such as the paper orientation and the resolution. /// The DEVMODE is stored in the user's registry. /// public IntPtr pDevMode; } static class print_nativeapi { [DllImport("kernel32.dll")] public static extern IntPtr GlobalLock(IntPtr hMem); [DllImport("kernel32.dll")] [return: MarshalAs(UnmanagedType.Bool)] public static extern bool GlobalUnlock(IntPtr hMem); [DllImport("kernel32.dll")] public static extern IntPtr GlobalFree(IntPtr hMem); [DllImport("winspool.drv", SetLastError = true)] public static extern int OpenPrinter(string pPrinterName, out IntPtr phPrinter, IntPtr pDefault); [DllImport("winspool.drv", SetLastError = true)] public static extern int SetPrinter(IntPtr phPrinter, UInt32 Level , IntPtr pPrinter, UInt32 Command); [DllImport("winspool.Drv", EntryPoint = "DocumentPropertiesW", SetLastError = true, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)] public static extern int DocumentProperties(IntPtr hwnd, IntPtr hPrinter, [MarshalAs(UnmanagedType.LPWStr)] string pDeviceName, IntPtr pDevModeOutput, IntPtr pDevModeInput, int fMode); [DllImport("winspool.drv", CharSet = CharSet.Unicode, SetLastError = true)] public static extern int DocumentProperties(IntPtr hWnd, IntPtr hPrinter, string pDeviceName, IntPtr pDevModeOutput, IntPtr pDevModeInput, fModes fMode); } static class NATIVEWIN { public const int IDOK = 1; public const int IDCANCEL = 2; public const int DM_OUT_BUFFER = 2; public const int DM_IN_BUFFER = 8; public const int DM_IN_PROMPT = 4; public const int DM_ORIENTATION = 1; public const int DM_PAPERSIZE = 2; public const int DM_PAPERLENGTH = 4; public const int DM_WIDTH = 8; public const int DMORIENT_PORTRAIT = 1; public const int DMORIENT_LANDSCAPE = 2; } public enum PrintPages_t { RANGE = 2, CURRENT = 1, ALL = 0 } public enum PageScale_t { NONE = 0, FIT = 1, } public class PrintDiagEventArgs : EventArgs { public int m_page; public PrintDiagEventArgs(int page) { m_page = page; } } public class PrintRanges { public List ToPrint; public bool HasEvens; public bool HasOdds; public int NumberPages; public PrintRanges(int number_pages) { ToPrint = new List(number_pages); NumberPages = 0; HasEvens = false; HasOdds = false; } public void InitRange(Match match) { NumberPages = 0; HasEvens = false; HasOdds = false; for (int k = 0; k < ToPrint.Count; k++) { if (CheckValue(match, k)) { NumberPages = NumberPages + 1; ToPrint[k] = true; if ((k + 1) % 2 != 0) HasOdds = true; else HasEvens = true; } else ToPrint[k] = false; } } private bool CheckValue(Match match, int k) { return false; } } public partial class Print : Window { private LocalPrintServer m_printServer; public PrintQueue m_selectedPrinter = null; String m_status; public PrintPages_t m_pages_setting; public double m_page_scale; int m_numpages; PrintCapabilities m_printcap; public PageSettings m_pagedetails; TranslateTransform m_trans_pap; TranslateTransform m_trans_doc; public bool m_isrotated; PrintRanges m_range_pages; public int m_numcopies; bool m_invalidTo; bool m_invalidFrom; public int m_from; public int m_to; ghostnet_wpf_example.MainWindow main; PrinterSettings m_ps; public Print(ghostnet_wpf_example.MainWindow main_in, int num_pages) { InitializeComponent(); m_ps = new PrinterSettings(); main = main_in; this.Closing += new System.ComponentModel.CancelEventHandler(FakeWindowClosing); InitializeComponent(); m_printServer = new LocalPrintServer(); m_selectedPrinter = LocalPrintServer.GetDefaultPrintQueue(); m_ps.PrinterName = m_selectedPrinter.FullName; m_pagedetails = m_ps.DefaultPageSettings; xaml_rbAll.IsChecked = true; m_pages_setting = PrintPages_t.ALL; xaml_autofit.IsChecked = false; m_numpages = num_pages; m_printcap = m_selectedPrinter.GetPrintCapabilities(); m_trans_pap = new TranslateTransform(0, 0); m_trans_doc = new TranslateTransform(0, 0); m_isrotated = false; /* Data range case */ m_range_pages = new PrintRanges(m_numpages); m_page_scale = 1.0; m_numcopies = 1; m_invalidTo = true; m_invalidFrom = true; m_from = -1; m_to = -1; InitPrinterList(); } void FakeWindowClosing(object sender, System.ComponentModel.CancelEventArgs e) { e.Cancel = true; this.Hide(); } public void RealWindowClosing() { this.Closing -= new System.ComponentModel.CancelEventHandler(FakeWindowClosing); this.Close(); } private void InitPrinterList() { PrintQueueCollection printQueuesOnLocalServer = m_printServer.GetPrintQueues(new[] { EnumeratedPrintQueueTypes.Local, EnumeratedPrintQueueTypes.Connections }); this.xaml_selPrinter.ItemsSource = printQueuesOnLocalServer; if (m_selectedPrinter != null) { foreach (PrintQueue pq in printQueuesOnLocalServer) { if (pq.FullName == m_selectedPrinter.FullName) { this.xaml_selPrinter.SelectedItem = pq; break; } } } } /* Printer Status */ private void GetPrinterStatus() { if (m_selectedPrinter != null) { if (m_selectedPrinter.IsBusy) m_status = "Busy"; else if (m_selectedPrinter.IsNotAvailable) m_status = "Not Available"; else if (m_selectedPrinter.IsOffline) m_status = "Offline"; else if (m_selectedPrinter.IsOutOfMemory) m_status = "Out Of Memory"; else if (m_selectedPrinter.IsOutOfPaper) m_status = "Out Of Paper"; else if (m_selectedPrinter.IsOutputBinFull) m_status = "Output Bin Full"; else if (m_selectedPrinter.IsPaperJammed) m_status = "Paper Jam"; else if (m_selectedPrinter.IsPaused) m_status = "Paused"; else if (m_selectedPrinter.IsPendingDeletion) m_status = "Paused"; else if (m_selectedPrinter.IsPrinting) m_status = "Printing"; else if (m_selectedPrinter.IsProcessing) m_status = "Processing"; else if (m_selectedPrinter.IsWaiting) m_status = "Waiting"; else if (m_selectedPrinter.IsWarmingUp) m_status = "Warming Up"; else m_status = "Ready"; xaml_Status.Text = m_status; } } private void selPrinterChanged(object sender, SelectionChangedEventArgs e) { m_selectedPrinter = this.xaml_selPrinter.SelectedItem as PrintQueue; GetPrinterStatus(); m_ps.PrinterName = m_selectedPrinter.FullName; m_pagedetails = m_ps.DefaultPageSettings; } /* We have to do some calling into native methods to deal with and show * the advanced properties referenced by the DEVMODE struture. Ugly, * but I could not figure out how to do with direct WPF C# methods */ private void ShowProperties(object sender, RoutedEventArgs e) { try { /* First try to open the printer */ IntPtr phPrinter; int result = print_nativeapi.OpenPrinter(m_ps.PrinterName, out phPrinter, IntPtr.Zero); if (result == 0) { return; } /* Get a pointer to the DEVMODE */ IntPtr hDevMode = m_ps.GetHdevmode(m_ps.DefaultPageSettings); IntPtr pDevMode = print_nativeapi.GlobalLock(hDevMode); /* Native method wants a handle to our main window */ IntPtr hwin = new WindowInteropHelper(this).Handle; /* Get size of DEVMODE */ int sizeNeeded = print_nativeapi.DocumentProperties(hwin, IntPtr.Zero, m_ps.PrinterName, IntPtr.Zero, pDevMode, 0); /* Allocate */ IntPtr devModeData = Marshal.AllocHGlobal(sizeNeeded); /* Get devmode and show properties window */ print_nativeapi.DocumentProperties(hwin, IntPtr.Zero, m_ps.PrinterName, devModeData, pDevMode, fModes.DM_IN_PROMPT | fModes.DM_OUT_BUFFER); /* Set the properties, 9 = PRINTER_INFO_9. This was tricky to figure out how to do */ PRINTER_INFO_9 info = new PRINTER_INFO_9(); info.pDevMode = devModeData; IntPtr infoPtr = Marshal.AllocHGlobal(Marshal.SizeOf()); Marshal.StructureToPtr(info, infoPtr, false); result = print_nativeapi.SetPrinter(phPrinter, 9, infoPtr, 0); /* Clean up */ print_nativeapi.GlobalUnlock(hDevMode); print_nativeapi.GlobalFree(hDevMode); Marshal.FreeHGlobal(infoPtr); //Marshal.FreeHGlobal(devModeData); /* NB: Freeing this causes bad things for some reason. */ } catch (Exception except) { main.ShowMessage(NotifyType_t.MESS_ERROR, "Exception in native print interface:" + except.Message); } } private void AllPages(object sender, RoutedEventArgs e) { m_pages_setting = PrintPages_t.ALL; } private void CurrentPage(object sender, RoutedEventArgs e) { m_pages_setting = PrintPages_t.CURRENT; } private void UpdatePageRange() { PrintDiagEventArgs info = new PrintDiagEventArgs(m_from - 1); } public bool RangeOK() { if (m_pages_setting == PrintPages_t.ALL || m_pages_setting == PrintPages_t.CURRENT) return true; if (!m_invalidFrom && !m_invalidTo && m_pages_setting == PrintPages_t.RANGE && m_to >= m_from && m_to > 0 && m_from > 0) return true; return false; } private void PageRange(object sender, RoutedEventArgs e) { m_pages_setting = PrintPages_t.RANGE; if (RangeOK()) { UpdatePageRange(); } } private void PreviewTextInputFrom(object sender, TextCompositionEventArgs e) { Regex regex = new Regex("[^0-9]+"); bool ok = !regex.IsMatch(e.Text); if (!ok) m_invalidFrom = true; else m_invalidFrom = false; } private void PreviewTextInputTo(object sender, TextCompositionEventArgs e) { Regex regex = new Regex("[^0-9]+"); bool ok = !regex.IsMatch(e.Text); if (!ok) m_invalidTo = true; else m_invalidTo = false; } private void FromChanged(object sender, TextChangedEventArgs e) { Regex regex = new Regex("[^0-9]+"); TextBox tbox = (TextBox)sender; if (tbox.Text == "") { e.Handled = true; return; } /* Need to check it again. back space does not cause PreviewTextInputFrom * to fire */ bool ok = !regex.IsMatch(tbox.Text); if (!ok) m_invalidFrom = true; else m_invalidFrom = false; if (m_invalidFrom) { xaml_pagestart.Text = ""; e.Handled = true; m_from = -1; } else { m_from = System.Convert.ToInt32(xaml_pagestart.Text); if (m_from > m_numpages) { m_from = m_numpages; xaml_pagestart.Text = System.Convert.ToString(m_numpages); } if (m_from < 1) { m_from = 1; xaml_pagestart.Text = System.Convert.ToString(m_numpages); } if (!m_invalidFrom && !m_invalidTo && m_pages_setting == PrintPages_t.RANGE && m_to >= m_from) { UpdatePageRange(); } } } private void ToChanged(object sender, TextChangedEventArgs e) { Regex regex = new Regex("[^0-9]+"); TextBox tbox = (TextBox)sender; if (tbox.Text == "") { e.Handled = true; return; } /* Need to check it again. back space does not cause PreviewTextInputTo * to fire */ bool ok = !regex.IsMatch(tbox.Text); if (!ok) m_invalidTo = true; else m_invalidTo = false; if (m_invalidTo) { xaml_pageend.Text = ""; e.Handled = true; m_to = -1; } else { m_to = System.Convert.ToInt32(xaml_pageend.Text); if (m_to > m_numpages) { m_to = m_numpages; xaml_pageend.Text = System.Convert.ToString(m_numpages); } if (m_to < 1) { m_to = 1; xaml_pagestart.Text = System.Convert.ToString(m_numpages); } if (!m_invalidFrom && !m_invalidTo && m_pages_setting == PrintPages_t.RANGE && m_to >= m_from) { UpdatePageRange(); } } } private void xaml_Copies_ValueChanged(object sender, RoutedPropertyChangedEventArgs e) { m_numcopies = (int)e.NewValue; } private void AutoFit_Checked(object sender, RoutedEventArgs e) { } private void ClickOK(object sender, RoutedEventArgs e) { main.PrintDiagPrint(this); this.Hide(); } private void ClickCancel(object sender, RoutedEventArgs e) { this.Hide(); } private void AutoFit_Unchecked(object sender, RoutedEventArgs e) { } } }