C# System.Drawing.Printing.PrintDocument设置打印机额外项的踩坑点

C#设置打印首选项

查看PrintDocument的API
查看DocumentProperties()
查看DEVMODE

注意:如果有非DEVMODE标准的属性需要配置,请跳过这段直接看到踩坑

[StructLayout(LayoutKind.Sequential)]
public struct DEVMODE
{
    const int CCHDEVICENAME  = (32 * 2);
    const int CCHFORMNAME = (32 * 2);
 
    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = CCHDEVICENAME)]
    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 = CCHFORMNAME)]
    public string dmFormName;
    public short dmUnusedPadding;
    public short 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 dmICCManufacturer;
    public int dmICCModel;
    public int dmPanningWidth;
    public int dmPanningHeight;
}

public class FunctionClass
{
	[DllImport("winspool.Drv",
    EntryPoint = "DocumentPropertiesA",
    SetLastError = true,
    ExactSpelling = true,
    CallingConvention = CallingConvention.StdCall)]
	public static extern int DocumentProperties(IntPtr hwnd, IntPtr hPrinter, [MarshalAs(UnmanagedType.LPStr)] string pDeviceNameg, IntPtr pDevModeOutput, ref IntPtr pDevModeInput, int fMode);
}

public class GCFunction 
{
    [DllImport("kernel32.dll")]
    public static extern IntPtr GlobalAlloc(short uFlags, int dwBytes);

    [DllImport("kernel32.dll")]
    public static extern IntPtr GlobalLock(IntPtr hMem);

    [DllImport("kernel32.dll")]
    public static extern int GlobalFree(IntPtr hMem); 
    
    [DllImport("kernel32.dll", ExactSpelling = true)]
    public static extern IntPtr GlobalUnlock(IntPtr handle);

    [DllImport("kernel32.dll",
        EntryPoint = "RtlMoveMemory")]
    public static extern void Mem2DevCopy(out DEVMODE hpvDest,IntPtr hpvSource,int cbCopy);
}
 
// 全局变量
private DEVMODE dm;
private IntPtr pDevMode;
private IntPtr devModePtr;

 /// <summary>
 /// 读取初始配置
 /// </summary>
 private void GetDevMode()
 {
     IntPtr printerSlef = IntPtr.Zero;
     int n = 0;

     // 空默认不使用
     IntPtr temp = new IntPtr();

     try
     {
         
         if (!FunctionClass.OpenPrinter(printName, out printerSlef, 0))
         {
             throw new Exception("Cannot open printer");
         }

         // 正式获取结构体
         pDevMode = pd.PrinterSettings.GetHdevmode(pd.PrinterSettings.DefaultPageSettings);
         devModePtr = GCFunction.GlobalLock(pDevMode);
         GetDevMode(printerSlef);

         // 获取额外配置项的起始位
         while (true)
         {
             if (dm.ExDevMode[n] == DevModeCode.EX_DEV_SIGN)
             {
                 DevModeCode.ExDevTop = n - 1;
                 break;
             }
             if (n >= DevModeCode.EX_DEV_SIZE)
             {
                 break;
             }
             n++;
         }
     }
     finally
     {
         FunctionClass.ClosePrinter(printerSlef);
     }
 }

 /// <summary>
 /// 设置打印机
 /// </summary>
 public void SettingPrint(PrinterSet printerSet)
 {
     try
     {
         dm = Marshal.PtrToStructure<DEVMODE>(devModePtr);

         // 修改DEVMODE

         // 将修改之后dm同步内存
         Marshal.StructureToPtr(dm, devModePtr, true);
         pd.PrinterSettings.SetHdevmode(devModePtr);
     }
     finally
     {
         GCFunction.GlobalUnlock(devModePtr);
         GCFunction.GlobalFree(pDevMode);
     }
 }

 /// <summary>
 /// 手动获取DevMod内容
 /// </summary>
 /// <param name="devModePtr"> 已被分配内存的指针 </param>
 /// <param name="dm"> 回传结构体 </param>
 /// <exception cref="Exception"></exception>
 public void GetDevMode(IntPtr printerSlef)
 {
     IntPtr tempNull = IntPtr.Zero;
     int resultCode;

     // 再次访问获取实际数据
     resultCode = FunctionClass.DocumentProperties(0, printerSlef, printName, devModePtr, ref tempNull, DevModeCode.DM_OUT_BUFFER);
     if (resultCode < 0)
     {
         GCFunction.GlobalUnlock(devModePtr);
         GCFunction.GlobalFree(pDevMode);
         devModePtr = pDevMode = IntPtr.Zero;
         throw new Exception("Cannot get DevMode"); ;
     }

     dm = Marshal.PtrToStructure<DEVMODE>(devModePtr);

     if (string.IsNullOrEmpty(dm.dmDeviceName))
     {
         GCFunction.GlobalUnlock(devModePtr);
         GCFunction.GlobalFree(pDevMode);
         devModePtr = pDevMode = IntPtr.Zero;
         throw new Exception("Cannot make intptr to DevMode"); ;
     }
 }

如果出错了,记得外部方法出错了记得使用 Marshal.GetLastWin32Error()查看错误

错误代码列表

配置额外的配置项

当我在配置打印机提供的额外配置项时老是不生效,导致抓耳挠腮,后面调试后发现SetHdevmode()使用的时Gdi.DEVMODE的标准类,导致额外添加的属性没法生效……
所以我用VB写了个DLL提供C#调用解决,因为VB的生效且方便C#调用

Public Function SettingPrinter(hPrinter As IntPtr)
    Dim ResultCode As Integer
    Dim DevModeSize As Integer
    Dim n, ExDevTop As Integer
    Dim dmw As DEVMODEW = Nothing

    Dim hDevMode As Long
    Dim lpDevModeDmW As IntPtr

    DevModeSize = DocumentProperties(0, hPrinter, printerName, IntPtr.Zero, IntPtr.Zero, 0)
    If DevModeSize = 0 Then
        Exit Function
    End If

    hDevMode = GlobalAlloc(GHND, DevModeSize)
    lpDevModeDmW = GlobalLock(hDevMode)
    ResultCode = DocumentProperties(0, hPrinter, printerName, lpDevModeDmW, 0, DM_OUT_BUFFER)
    If ResultCode < 0 Then
        GlobalFree(lpDevModeDmW)
        Exit Function
    End If

    Mem2DevCopy(dmw, lpDevModeDmW, DevModeSize)
    
    '配置DEVMODE
    Call Dev2MemCopy(lpDevModeDmW, dmw, DevModeSize)
    printerDocuent.PrinterSettings.SetHdevmode(lpDevModeDmW)
    ResultCode = GlobalFree(lpDevModeDmW)
End Function

Declare Function DocumentProperties Lib "winspool.drv" Alias "DocumentPropertiesW" _
    (ByVal hwnd As Integer,
     ByVal hPrinter As IntPtr,
     ByVal pDeviceName As String,
     ByVal pDevModeOutput As IntPtr,
     ByVal pDevModeInput As IntPtr,
     ByVal fMode As Integer) As Integer

Public Declare Function GlobalAlloc Lib "kernel32" _
    (ByVal uFlags As Short,
     ByVal dwBytes As Long) As Long

Public Declare Function GlobalFree Lib "kernel32" _
    (ByVal hMem As Long) As Long

Public Declare Function GlobalLock Lib "kernel32" _
    (ByVal hMem As Long) As Long

Private Declare Sub Mem2DevCopy Lib "kernel32" Alias "RtlMoveMemory" _
    (ByRef hpvDest As DEVMODEW,
     ByVal hpvSource As IntPtr,
     ByVal cbCopy As Integer)

Private Declare Sub Dev2MemCopy Lib "kernel32" Alias "RtlMoveMemory" _
    (ByVal hpvDest As IntPtr,
     ByRef hpvSource As DEVMODEW,
     ByVal cbCopy As Integer)
     
  Public Const EX_DEV_SIZE As Short = 1024

 Public Const GMEM_FIXED As Integer = &H0
 Public Const GMEM_MOVEABLE As Integer = &H2
 Public Const GMEM_ZEROINIT As Integer = &H40

 Public Const GHND As Integer = (GMEM_MOVEABLE Or GMEM_ZEROINIT)

 Public Const DM_UPDATE As Integer = 1
 Public Const DM_COPY As Integer = 2
 Public Const DM_PROMPT As Integer = 4
 Public Const DM_MODIFY As Integer = 8

 Public Const DM_IN_BUFFER As Integer = DM_MODIFY
 Public Const DM_IN_PROMPT As Integer = DM_PROMPT
 Public Const DM_OUT_BUFFER As Integer = DM_COPY
 Public Const DM_OUT_DEFAULT As Integer = DM_UPDATE
 
<StructLayout(LayoutKind.Sequential)>
Public Structure DEVMODEW
    Const CCHDEVICENAME As Integer = (32 * 2)
    Const CCHFORMNAME As Integer = (32 * 2)

    <VBFixedArray(CCHDEVICENAME - 1),
        MarshalAs(UnmanagedType.ByValArray,
                  SizeConst:=CCHDEVICENAME)> Public dmDeviceName As Byte()

    Dim dmSpecVersion As Short
    Dim dmDriverVersion As Short
    Dim dmSize As Short
    Dim dmDriverExtra As Short
    Dim dmFields As Integer
    Dim dmOrientation As Short
    Dim dmPaperSize As Short
    Dim dmPaperLength As Short
    Dim dmPaperWidth As Short
    Dim dmScale As Short
    Dim dmCopies As Short
    Dim dmDefaultSource As Short
    Dim dmPrintQuality As Short
    Dim dmColor As Short
    Dim dmDuplex As Short
    Dim dmYResolution As Short
    Dim dmTTOption As Short
    Dim dmCollate As Short

    <VBFixedArray(CCHFORMNAME - 1),
        MarshalAs(UnmanagedType.ByValArray,
                  SizeConst:=CCHFORMNAME)> Public dmFormName As Byte()

    Dim dmUnusedPadding As Short
    Dim dmBitsPerPel As Integer
    Dim dmPelsWidth As Integer
    Dim dmPelsHeight As Integer
    Dim dmDisplayFlags As Integer
    Dim dmDisplayFrequency As Integer
    Dim dmICMMethod As Integer
    Dim dmICMIntent As Integer
    Dim dmMediaType As Integer
    Dim dmDitherType As Integer
    Dim dmICCManufacturer As Integer
    Dim dmICCModel As Integer
    Dim dmPanningWidth As Integer
    Dim dmPanningHeight As Integer

    '额外属性

End Structure
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值