Windows下如何通过程序为特定打印机添加永久自定义纸张尺寸
在Windows中为特定打印机添加永久自定义纸张尺寸的无管理员权限方案
一、使用Win32 API(C/C++)
Windows打印后台处理程序API可直接管理打印机纸张配置,无需手动修改注册表,针对用户本地打印机场景通常无需管理员权限。
核心步骤:
- 调用
OpenPrinter获取目标打印机句柄,请求PRINTER_ACCESS_USE权限即可满足多数场景。 - 填充
DEVMODE结构体,指定自定义纸张的尺寸(单位为0.01毫米)和类型(DMPAPER_USER)。 - 通过
SetPrinter函数传入PRINTER_INFO_9结构体,将自定义尺寸添加到打印机支持列表。 - 调用
ResetPrinter刷新打印机配置,确保其他程序能识别新尺寸。
示例代码片段:
#include <windows.h> #include <winspool.h> BOOL AddCustomPaperSize(LPCWSTR printerName, LPCWSTR paperName, int widthMm, int heightMm) { HANDLE hPrinter = NULL; BOOL result = FALSE; if (!OpenPrinterW((LPWSTR)printerName, &hPrinter, NULL)) { return FALSE; } DEVMODEW dm = {0}; dm.dmSize = sizeof(DEVMODEW); dm.dmFields = DM_PAPERSIZE | DM_PAPERWIDTH | DM_PAPERLENGTH; dm.dmPaperSize = DMPAPER_USER; dm.dmPaperWidth = widthMm * 10; // 转换为0.01毫米单位 dm.dmPaperLength = heightMm * 10; PRINTER_INFO_9W pi9 = {0}; pi9.pDevMode = &dm; result = SetPrinterW(hPrinter, 9, (LPBYTE)&pi9, 0); ResetPrinterW(hPrinter, &dm); ClosePrinter(hPrinter); return result; }
二、使用PowerShell脚本
通过封装Win32 API,实现无编译的脚本化操作,适合批量部署场景。
示例脚本:
$printerName = "你的目标打印机名称" $paperName = "自定义小票纸" $widthMm = 80 $heightMm = 200 # 导入Win32 API定义 Add-Type @" using System; using System.Runtime.InteropServices; public class PrinterHelper { [DllImport("winspool.drv", CharSet = CharSet.Unicode, SetLastError = true)] public static extern bool OpenPrinter(string pPrinterName, out IntPtr phPrinter, IntPtr pDefault); [DllImport("winspool.drv", CharSet = CharSet.Unicode, SetLastError = true)] public static extern bool ClosePrinter(IntPtr hPrinter); [DllImport("winspool.drv", CharSet = CharSet.Unicode, SetLastError = true)] public static extern bool SetPrinter(IntPtr hPrinter, int Level, IntPtr pPrinter, int Command); [DllImport("winspool.drv", CharSet = CharSet.Unicode, SetLastError = true)] public static extern bool ResetPrinter(IntPtr hPrinter, IntPtr pDevMode); [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] public struct DEVMODE { public int dmSize; public int dmDriverExtra; public int dmFields; public short dmPaperSize; public short dmPaperWidth; public short dmPaperLength; } [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] public struct PRINTER_INFO_9 { public IntPtr pDevMode; } } "@ # 打开打印机 $hPrinter = [IntPtr]::Zero if (-not [PrinterHelper]::OpenPrinter($printerName, [ref]$hPrinter, [IntPtr]::Zero)) { Write-Error "打开打印机失败: $([System.ComponentModel.Win32Exception][System.Runtime.InteropServices.Marshal]::GetLastWin32Error())" exit 1 } try { # 初始化DEVMODE $dm = New-Object PrinterHelper+DEVMODE $dm.dmSize = [System.Runtime.InteropServices.Marshal]::SizeOf($dm) $dm.dmFields = 0x00000001 | 0x00000002 | 0x00000004 $dm.dmPaperSize = 256 # DMPAPER_USER $dm.dmPaperWidth = $widthMm * 10 $dm.dmPaperLength = $heightMm * 10 # 分配内存并复制结构体 $dmPtr = [System.Runtime.InteropServices.Marshal]::AllocHGlobal($dm.dmSize) [System.Runtime.InteropServices.Marshal]::StructureToPtr($dm, $dmPtr, $false) $pi9 = New-Object PrinterHelper+PRINTER_INFO_9 $pi9.pDevMode = $dmPtr $pi9Ptr = [System.Runtime.InteropServices.Marshal]::AllocHGlobal([System.Runtime.InteropServices.Marshal]::SizeOf($pi9)) [System.Runtime.InteropServices.Marshal]::StructureToPtr($pi9, $pi9Ptr, $false) # 添加自定义纸张并刷新 if ([PrinterHelper]::SetPrinter($hPrinter, 9, $pi9Ptr, 0)) { [PrinterHelper]::ResetPrinter($hPrinter, $dmPtr) Write-Host "自定义纸张添加成功" } else { Write-Error "添加纸张失败: $([System.ComponentModel.Win32Exception][System.Runtime.InteropServices.Marshal]::GetLastWin32Error())" } # 释放内存 [System.Runtime.InteropServices.Marshal]::FreeHGlobal($pi9Ptr) [System.Runtime.InteropServices.Marshal]::FreeHGlobal($dmPtr) } finally { [PrinterHelper]::ClosePrinter($hPrinter) }
三、使用.NET(C#)
通过封装Win32 API实现简洁的高层代码,适合集成到.NET应用中。
示例代码:
using System; using System.Runtime.InteropServices; using System.ComponentModel; public class CustomPaperManager { [DllImport("winspool.drv", CharSet = CharSet.Unicode, SetLastError = true)] private static extern bool OpenPrinter(string pPrinterName, out IntPtr phPrinter, IntPtr pDefault); [DllImport("winspool.drv", CharSet = CharSet.Unicode, SetLastError = true)] private static extern bool ClosePrinter(IntPtr hPrinter); [DllImport("winspool.drv", CharSet = CharSet.Unicode, SetLastError = true)] private static extern bool SetPrinter(IntPtr hPrinter, int Level, IntPtr pPrinter, int Command); [DllImport("winspool.drv", CharSet = CharSet.Unicode, SetLastError = true)] private static extern bool ResetPrinter(IntPtr hPrinter, IntPtr pDevMode); [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] private struct DEVMODE { public int dmSize; public int dmDriverExtra; public int dmFields; public short dmPaperSize; public short dmPaperWidth; public short dmPaperLength; } [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] private struct PRINTER_INFO_9 { public IntPtr pDevMode; } public static bool AddCustomPaper(string printerName, int widthMm, int heightMm) { IntPtr hPrinter = IntPtr.Zero; try { if (!OpenPrinter(printerName, out hPrinter, IntPtr.Zero)) throw new Win32Exception(); DEVMODE dm = new DEVMODE(); dm.dmSize = Marshal.SizeOf(typeof(DEVMODE)); dm.dmFields = 0x00000001 | 0x00000002 | 0x00000004; dm.dmPaperSize = 256; dm.dmPaperWidth = (short)(widthMm * 10); dm.dmPaperLength = (short)(heightMm * 10); IntPtr dmPtr = Marshal.AllocHGlobal(dm.dmSize); Marshal.StructureToPtr(dm, dmPtr, false); PRINTER_INFO_9 pi9 = new PRINTER_INFO_9(); pi9.pDevMode = dmPtr; IntPtr pi9Ptr = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(PRINTER_INFO_9))); Marshal.StructureToPtr(pi9, pi9Ptr, false); if (!SetPrinter(hPrinter, 9, pi9Ptr, 0)) throw new Win32Exception(); ResetPrinter(hPrinter, dmPtr); return true; } catch { return false; } finally { if (hPrinter != IntPtr.Zero) ClosePrinter(hPrinter); } } }
关键注意事项
- 权限说明:针对用户本地安装的打印机,上述方案仅需普通用户权限;若为共享/系统级打印机,仍可能需要管理员权限。
- 单位转换:所有API的纸张尺寸单位为0.01毫米,需手动转换输入的毫米值(如80mm → 800)。
- 生效验证:添加完成后,可通过打印机属性面板或其他程序的打印设置确认自定义尺寸是否存在。
内容的提问来源于stack exchange,提问作者Alireza
相关产品推荐
相关产品推荐

