我有一个打印多页文档的程序。第一页是预打印纸,因此应打印在第一面,其余页面应双面打印。
我最初的解决方案是在第一页之后打印一张空白纸,但是很多(或可能所有)打印机将使用预打印纸的另一面作为正面(非常糟糕),具体取决于它是否设置为是否双工。
所以我一直试图说服打印机在打印作业中间更改双面打印。我一直在努力解决这个问题,并且使用各种代码示例,理论上这应该可以工作,但事实并非如此。
在每次 EndPage 调用之后,如果双工需要改变,它从打印机获取设备上下文(使用内部类的私有字段上的反射,yuck),它使用 Marshal 填充 DEVMODE 结构上的值,然后调用ResetDC() 与设备上下文和 DEVMODE 指针。
我从指针中检索 DEVMODE 结构仅用于验证目的,我想确保我设置了正确的字段。DEVMODE 被正确填充并发送到 ResetDC() 和 PrinterSettings.SetHdevmode(),但是当我重新检索 PrinterSettings.GetHdevmode() 时,我刚刚所做的更改消失了。打印机保留旧的双面设置。
编辑:我发现我之前发布的代码存在一些问题,例如 OnStartPage 调用 ResetDC 的事实。所以我需要修改 StandardPrintController 持有的 DEVMODE 结构。但它仍然无法处理这些更改:
public class PrinterDuplexController : StandardPrintController
{
public PrinterDuplexController()
{
}
private static FieldInfo dcField = typeof(StandardPrintController)
.GetField("dc",
BindingFlags.GetField | BindingFlags.Instance | BindingFlags.NonPublic);
private static FieldInfo modeHandleField = typeof(StandardPrintController)
.GetField("modeHandle",
BindingFlags.GetField | BindingFlags.Instance | BindingFlags.NonPublic);
protected object dc
{
get
{
return dcField.GetValue(this);
}
}
protected IntPtr Hdc
{
get
{
var dc = this.dc;
return (IntPtr)(dc.GetType().GetProperty("Hdc").GetValue(dc, null));
}
}
protected IntPtr modeHandle
{
get
{
object result = modeHandleField.GetValue(this);
var field = result.GetType().GetField("handle", BindingFlags.GetField | BindingFlags.Instance | BindingFlags.NonPublic);
return (IntPtr)field.GetValue(result);
}
}
public override void OnEndPage(PrintDocument document, PrintPageEventArgs e)
{
base.OnEndPage(document, e);
IntPtr pDEVMODE = GlobalLock(modeHandle);
try
{
int[] flags = new int[1];
Marshal.Copy(
new IntPtr(40 + pDEVMODE.ToInt64()),
flags,
0,
1);
flags[0] |= (int)DM.Duplex;
Marshal.Copy(
flags,
0,
new IntPtr(40 + pDEVMODE.ToInt64()),
1);
Marshal.Copy(
new short[] { (short)e.PageSettings.PrinterSettings.Duplex },
0,
new IntPtr(62 + pDEVMODE.ToInt64()),
1);
var debugDevMode = (DEVMODE)Marshal.PtrToStructure(pDEVMODE, typeof(DEVMODE));
ResetDC(Hdc, pDEVMODE);
}
finally
{
GlobalUnlock(modeHandle);
}
}
[DllImport("gdi32.dll")]
//private static extern IntPtr ResetDC(IntPtr hdc, [In] ref DEVMODE lpInitData);
private static extern int ResetDC(IntPtr hdc, IntPtr DevMode);
[DllImport("gdi32.dll")]
public static extern int StartPage(IntPtr hdc);
[DllImport("gdi32.dll")]
public static extern int EndPage(IntPtr hdc);
[DllImport("kernel32.dll", ExactSpelling = true)]
private static extern IntPtr GlobalFree(IntPtr handle);
[DllImport("kernel32.dll", ExactSpelling = true)]
private static extern IntPtr GlobalLock(IntPtr handle);
[DllImport("kernel32.dll", ExactSpelling = true)]
private static extern IntPtr GlobalUnlock(IntPtr handle);
[Flags()]
internal enum DM : int
{
Orientation = 0x1,
PaperSize = 0x2,
PaperLength = 0x4,
PaperWidth = 0x8,
Scale = 0x10,
Position = 0x20,
NUP = 0x40,
DisplayOrientation = 0x80,
Copies = 0x100,
DefaultSource = 0x200,
PrintQuality = 0x400,
Color = 0x800,
Duplex = 0x1000,
YResolution = 0x2000,
TTOption = 0x4000,
Collate = 0x8000,
FormName = 0x10000,
LogPixels = 0x20000,
BitsPerPixel = 0x40000,
PelsWidth = 0x80000,
PelsHeight = 0x100000,
DisplayFlags = 0x200000,
DisplayFrequency = 0x400000,
ICMMethod = 0x800000,
ICMIntent = 0x1000000,
MediaType = 0x2000000,
DitherType = 0x4000000,
PanningWidth = 0x8000000,
PanningHeight = 0x10000000,
DisplayFixedOutput = 0x20000000
}
internal struct POINTL
{
public int x;
public int y;
}
[StructLayout(LayoutKind.Explicit, CharSet = CharSet.Unicode)]
internal struct DEVMODE
{
public const int CCHDEVICENAME = 32;
public const int CCHFORMNAME = 32;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = CCHDEVICENAME)]
[FieldOffset(0)]
public string dmDeviceName;
[FieldOffset(32)]
public Int16 dmSpecVersion;
[FieldOffset(34)]
public Int16 dmDriverVersion;
[FieldOffset(36)]
public Int16 dmSize;
[FieldOffset(38)]
public Int16 dmDriverExtra;
[FieldOffset(40)]
public DM dmFields;
[FieldOffset(44)]
Int16 dmOrientation;
[FieldOffset(46)]
Int16 dmPaperSize;
[FieldOffset(48)]
Int16 dmPaperLength;
[FieldOffset(50)]
Int16 dmPaperWidth;
[FieldOffset(52)]
Int16 dmScale;
[FieldOffset(54)]
Int16 dmCopies;
[FieldOffset(56)]
Int16 dmDefaultSource;
[FieldOffset(58)]
Int16 dmPrintQuality;
[FieldOffset(44)]
public POINTL dmPosition;
[FieldOffset(52)]
public Int32 dmDisplayOrientation;
[FieldOffset(56)]
public Int32 dmDisplayFixedOutput;
[FieldOffset(60)]
public short dmColor;
[FieldOffset(62)]
public short dmDuplex;
[FieldOffset(64)]
public short dmYResolution;
[FieldOffset(66)]
public short dmTTOption;
[FieldOffset(68)]
public short dmCollate;
[FieldOffset(72)]
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = CCHFORMNAME)]
public string dmFormName;
[FieldOffset(102)]
public Int16 dmLogPixels;
[FieldOffset(104)]
public Int32 dmBitsPerPel;
[FieldOffset(108)]
public Int32 dmPelsWidth;
[FieldOffset(112)]
public Int32 dmPelsHeight;
[FieldOffset(116)]
public Int32 dmDisplayFlags;
[FieldOffset(116)]
public Int32 dmNup;
[FieldOffset(120)]
public Int32 dmDisplayFrequency;
}
}