You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

C# .NET Core 6读写打印机DEVMODE属性保存还原失效问题

问题定位

你遇到的DEVMODE还原后系统配置不生效问题,核心原因是当前代码仅修改了进程内PrinterSettings实例的内存配置,未将配置写入系统持久化存储。
调用SetHdevmode方法只会更新当前.NET运行时中PrinterSettings对象持有的DEVMODE内存句柄,不会触发打印后台服务的配置更新逻辑:应用内唤起的打印机属性对话框读取的是当前进程内存中的DEVMODE数据,因此显示为修改后的值;Windows系统设置页面读取的是打印服务存储的全局/用户级持久化配置,因此显示为未修改的旧值。

现有代码存在的明确问题
  • 句柄释放逻辑错误:GetHdevmode返回的HGLOBAL内存句柄必须通过GlobalFree接口释放,现有代码使用的Marshal.FreeHGlobal对应本地内存释放接口,在64位Windows 11环境下会造成内存泄漏,偶发句柄访问异常。
  • DEVMODE结构截断:现有代码定义的DEVMODE结构体仅包含公共基础字段,未预留打印机驱动私有扩展区域的访问能力。多数厂商打印机会将核心配置(如纸盒选择、色彩模式、装订规则)存储在dmDriverExtra指向的驱动私有内存段,直接按固定结构体长度拷贝会出现配置数据丢失。
  • 缺少系统配置写入逻辑:现有代码完全没有调用打印服务相关的持久化API,仅做了进程内内存赋值,不可能实现系统层面的配置还原。
正确实现逻辑

要实现全局生效的打印机配置备份还原,按以下逻辑调整实现:

  1. 备份配置时,直接通过打印API打开目标打印机句柄,读取打印服务存储的完整DEVMODE结构(包含全部驱动私有扩展数据),按dmSize + dmDriverExtra的实际长度拷贝二进制数据做持久化存储。
  2. 还原配置前,先调用文档属性接口校验备份的DEVMODE数据与当前打印机驱动版本兼容,避免驱动升级后旧配置数据导致崩溃。
  3. 校验通过后,调用打印机配置设置接口将DEVMODE数据写入打印服务的持久化存储,完成后广播系统设置变更消息,通知所有已打开的系统面板、应用刷新打印机配置缓存。
  4. 所有Win32接口获取的全局内存句柄,统一使用GlobalFree释放,禁止混用本地内存释放接口。

权限说明:修改打印机全局默认配置需要对应打印机的管理权限,标准用户账号仅能修改当前用户上下文下的打印机默认配置,无法修改全局配置。

以下是测试代码参考(已标注问题点):

public static class PrinterSettingsExtensions
{
    public static byte[] GetDevModeData(this PrinterSettings settings)
    {
        byte[] devModeData;
        RuntimeHelpers.PrepareConstrainedRegions();
        try
        {
        }
        finally
        {
            var hDevMode = settings.GetHdevmode();
            try
            {
                IntPtr pDevMode = NativeMethods.GlobalLock(hDevMode);
                try
                {
                    var devMode = (NativeMethods.DEVMODE)Marshal.PtrToStructure(
                        pDevMode, typeof(NativeMethods.DEVMODE));

                    var devModeSize = devMode.dmSize + devMode.dmDriverExtra;
                    devModeData = new byte[devModeSize];
                    Marshal.Copy(pDevMode, devModeData, 0, devModeSize);
                }
                finally
                {
                    NativeMethods.GlobalUnlock(hDevMode);
                }
            }
            finally
            {
                // 错误:此处应调用GlobalFree释放hDevMode,而非Marshal.FreeHGlobal
                Marshal.FreeHGlobal(hDevMode);
            }
        }
        return devModeData;
    }

    public static void SetDevModeData(this PrinterSettings settings, byte[] data)
    {
        RuntimeHelpers.PrepareConstrainedRegions();
        try
        {
        }
        finally
        {
            var pDevMode = Marshal.AllocHGlobal(data.Length);
            try
            {
                Marshal.Copy(data, 0, pDevMode, data.Length);
                var devMode = (NativeMethods.DEVMODE)Marshal.PtrToStructure(
                        pDevMode, typeof(NativeMethods.DEVMODE));

                settings.PrinterName = devMode.dmDeviceName;
                // 仅修改进程内PrinterSettings的配置,未写入系统打印服务
                settings.SetHdevmode(pDevMode);
            }
            finally
            {
                Marshal.FreeHGlobal(pDevMode);
            }
        }
    }
}

static class NativeMethods
{
    private const string Kernel32 = "kernel32.dll";

    [DllImport(Kernel32, SetLastError = true, ExactSpelling = true, CharSet = CharSet.Auto)]
    public static extern IntPtr GlobalLock(IntPtr handle);

    [DllImport(Kernel32, SetLastError = true, ExactSpelling = true, CharSet = CharSet.Auto)]
    public static extern bool GlobalUnlock(IntPtr handle);

    [StructLayout(LayoutKind.Sequential, Pack = 1, CharSet = CharSet.Auto)]
    public struct DEVMODE
    {
        private const int CCHDEVICENAME = 32;
        private const int CCHFORMNAME = 32;

        [MarshalAs(UnmanagedType.ByValTStr, SizeConst = CCHDEVICENAME)]
        public string dmDeviceName;
        public short dmSpecVersion;
        public short dmDriverVersion;
        public short dmSize;
        public short dmDriverExtra;
        public int dmFields;

        public int dmPositionX;
        public int dmPositionY;
        public int dmDisplayOrientation;
        public int dmDisplayFixedOutput;

        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 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;
    }
}

运行环境参考:.NET Core 6、Windows 11 22000.795

内容的提问来源于stack exchange,提问作者Leon H

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.26 19:54:18