230 lines
8.7 KiB
C#
230 lines
8.7 KiB
C#
using System;
|
|
using System.ComponentModel;
|
|
using System.Printing;
|
|
using System.Runtime.InteropServices;
|
|
|
|
namespace Elwig.Helpers.Printing {
|
|
public partial class Win32Printer {
|
|
|
|
private const int DM_OUT_BUFFER = 2;
|
|
|
|
private const int HORZRES = 8;
|
|
private const int VERTRES = 10;
|
|
private const int LOGPIXELSX = 88;
|
|
private const int LOGPIXELSY = 90;
|
|
|
|
private const int DM_DUPLEX = 0x1000;
|
|
private const short DMDUP_SIMPLEX = 1;
|
|
private const short DMDUP_VERTICAL = 2;
|
|
private const short DMDUP_HORIZONTAL = 3;
|
|
|
|
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
|
|
private struct DOCINFO {
|
|
public int cbSize;
|
|
|
|
[MarshalAs(UnmanagedType.LPWStr)]
|
|
public string? lpszDocName;
|
|
|
|
[MarshalAs(UnmanagedType.LPWStr)]
|
|
public string? lpszOutput;
|
|
|
|
[MarshalAs(UnmanagedType.LPWStr)]
|
|
public string? lpszDatatype;
|
|
}
|
|
|
|
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
|
|
private struct DEVMODE {
|
|
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)]
|
|
public string dmDeviceName;
|
|
|
|
public short dmSpecVersion;
|
|
public short dmDriverVersion;
|
|
public short dmSize;
|
|
public short dmDriverExtra;
|
|
public int dmFields;
|
|
|
|
public short dmOrientation;
|
|
public short dmPaperSize;
|
|
public short dmPaperLength;
|
|
public short dmPaperWidth;
|
|
public short dmScale;
|
|
public short dmCopies;
|
|
public short dmDefaultSource;
|
|
public short dmPrintQuality;
|
|
public short dmColor;
|
|
public short dmDuplex;
|
|
public short dmYResolution;
|
|
public short dmTTOption;
|
|
public short dmCollate;
|
|
|
|
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)]
|
|
public string dmFormName;
|
|
|
|
public short dmLogPixels;
|
|
public int dmBitsPerPel;
|
|
public int dmPelsWidth;
|
|
public int dmPelsHeight;
|
|
public int dmDisplayFlags;
|
|
public int dmDisplayFrequency;
|
|
public int dmICMMethod;
|
|
public int dmICMIntent;
|
|
public int dmMediaType;
|
|
public int dmDitherType;
|
|
public int dmReserved1;
|
|
public int dmReserved2;
|
|
public int dmPanningWidth;
|
|
public int dmPanningHeight;
|
|
}
|
|
|
|
[DllImport("winspool.drv", SetLastError = true, CharSet = CharSet.Unicode)]
|
|
private static extern bool OpenPrinter(string pPrinterName, out IntPtr phPrinter, IntPtr pDefault);
|
|
|
|
[DllImport("winspool.drv", SetLastError = true)]
|
|
private static extern bool ClosePrinter(IntPtr hPrinter);
|
|
|
|
[DllImport("winspool.drv", SetLastError = true, CharSet = CharSet.Unicode)]
|
|
private static extern int DocumentProperties(IntPtr hwnd, IntPtr hPrinter, string pDeviceName, IntPtr pDevModeOutput, IntPtr pDevModeInput, int fMode);
|
|
|
|
[DllImport("gdi32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
|
|
private static extern IntPtr CreateDC(string? pDriver, string? pDevice, string? pOutput, IntPtr pInitData);
|
|
|
|
[DllImport("gdi32.dll", SetLastError = true)]
|
|
private static extern bool DeleteDC(IntPtr hdc);
|
|
|
|
[DllImport("gdi32.dll", SetLastError = true)]
|
|
private static extern int GetDeviceCaps(IntPtr hdc, int index);
|
|
|
|
[DllImport("gdi32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
|
|
private static extern int StartDoc(IntPtr hdc, ref DOCINFO lpdi);
|
|
|
|
[DllImport("gdi32.dll", SetLastError = true)]
|
|
private static extern int EndDoc(IntPtr hdc);
|
|
|
|
[DllImport("gdi32.dll", SetLastError = true)]
|
|
private static extern int AbortDoc(IntPtr hdc);
|
|
|
|
[DllImport("gdi32.dll", SetLastError = true)]
|
|
private static extern int StartPage(IntPtr hdc);
|
|
|
|
[DllImport("gdi32.dll", SetLastError = true)]
|
|
private static extern int EndPage(IntPtr hdc);
|
|
|
|
[DllImport("kernel32.dll", SetLastError = true)]
|
|
private static extern IntPtr GlobalAlloc(uint uFlags, UIntPtr dwBytes);
|
|
|
|
[DllImport("kernel32.dll", SetLastError = true)]
|
|
private static extern IntPtr GlobalFree(IntPtr hMem);
|
|
|
|
[DllImport("kernel32.dll")]
|
|
private static extern IntPtr GlobalLock(IntPtr hMem);
|
|
|
|
[DllImport("kernel32.dll")]
|
|
private static extern bool GlobalUnlock(IntPtr hMem);
|
|
|
|
public class Printer : IDisposable {
|
|
private readonly string _printerName;
|
|
private readonly IntPtr _printerHandle;
|
|
private readonly IntPtr _devMode;
|
|
private readonly IntPtr _hdc;
|
|
|
|
public IntPtr Hdc => _hdc;
|
|
|
|
public int PixelWidth => GetDeviceCaps(_hdc, HORZRES);
|
|
public int PixelHeight => GetDeviceCaps(_hdc, VERTRES);
|
|
public int DpiX => GetDeviceCaps(_hdc, LOGPIXELSX);
|
|
public int DpiY => GetDeviceCaps(_hdc, LOGPIXELSY);
|
|
|
|
public Printer(string printerName) {
|
|
_printerName = printerName;
|
|
|
|
if (!OpenPrinter(_printerName, out _printerHandle, IntPtr.Zero)) {
|
|
throw new Win32Exception(Marshal.GetLastWin32Error(), $"Unable to open printer '{printerName}'.");
|
|
}
|
|
|
|
try {
|
|
_devMode = CreateDevMode();
|
|
_hdc = CreateDC(null, _printerName, null, _devMode);
|
|
if (_hdc == IntPtr.Zero)
|
|
throw new Win32Exception(Marshal.GetLastWin32Error(), $"Unable to create printer DC for '{_printerName}'.");
|
|
} catch {
|
|
if (_devMode != IntPtr.Zero) Marshal.FreeHGlobal(_devMode);
|
|
ClosePrinter(_printerHandle);
|
|
throw;
|
|
}
|
|
}
|
|
|
|
public bool TrySetDuplex(Duplexing duplex) {
|
|
var devMode = Marshal.PtrToStructure<DEVMODE>(_devMode);
|
|
if ((devMode.dmFields & DM_DUPLEX) != 0) {
|
|
switch (duplex) {
|
|
case Duplexing.OneSided: devMode.dmDuplex = DMDUP_SIMPLEX; break;
|
|
case Duplexing.TwoSidedShortEdge: devMode.dmDuplex = DMDUP_HORIZONTAL; break;
|
|
case Duplexing.TwoSidedLongEdge: devMode.dmDuplex = DMDUP_VERTICAL; break;
|
|
}
|
|
Marshal.StructureToPtr(devMode, _devMode, false);
|
|
return true;
|
|
} else {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
private IntPtr CreateDevMode() {
|
|
int size = DocumentProperties(IntPtr.Zero, _printerHandle, _printerName, IntPtr.Zero, IntPtr.Zero, 0);
|
|
if (size <= 0)
|
|
throw new Win32Exception(Marshal.GetLastWin32Error(), "DocumentProperties failed.");
|
|
|
|
IntPtr devMode = Marshal.AllocHGlobal(size);
|
|
int result = DocumentProperties(IntPtr.Zero, _printerHandle, _printerName, devMode, IntPtr.Zero, DM_OUT_BUFFER);
|
|
if (result < 0) {
|
|
Marshal.FreeHGlobal(devMode);
|
|
throw new Win32Exception(Marshal.GetLastWin32Error(), "Unable to obtain printer DEVMODE.");
|
|
}
|
|
|
|
return devMode;
|
|
}
|
|
|
|
public void StartDocument(string documentName) {
|
|
var docInfo = new DOCINFO {
|
|
cbSize = Marshal.SizeOf<DOCINFO>(),
|
|
lpszDocName = documentName,
|
|
lpszDatatype = null,
|
|
lpszOutput = null
|
|
};
|
|
|
|
if (StartDoc(_hdc, ref docInfo) <= 0)
|
|
throw new Win32Exception(Marshal.GetLastWin32Error(), "StartDoc failed.");
|
|
}
|
|
|
|
public void StartPage() {
|
|
if (Win32Printer.StartPage(_hdc) <= 0)
|
|
throw new Win32Exception(Marshal.GetLastWin32Error(), "StartPage failed.");
|
|
}
|
|
|
|
public void EndPage() {
|
|
if (Win32Printer.EndPage(_hdc) <= 0)
|
|
throw new Win32Exception(Marshal.GetLastWin32Error(), "EndPage failed.");
|
|
}
|
|
|
|
public void EndDocument() {
|
|
if (EndDoc(_hdc) <= 0)
|
|
throw new Win32Exception( Marshal.GetLastWin32Error(), "EndDoc failed.");
|
|
}
|
|
|
|
public void AbortDocument() {
|
|
AbortDoc(_hdc);
|
|
}
|
|
|
|
public void Dispose() {
|
|
if (_hdc != IntPtr.Zero)
|
|
DeleteDC(_hdc);
|
|
|
|
if (_devMode != IntPtr.Zero)
|
|
Marshal.FreeHGlobal(_devMode);
|
|
|
|
if (_printerHandle != IntPtr.Zero)
|
|
ClosePrinter(_printerHandle);
|
|
}
|
|
}
|
|
}
|
|
}
|