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

使用Win32 API创建打印对话框时DEVMODE无法设置默认参数问题

问题分析与修复方案

你的代码存在几个关键问题,导致DEVMODE的初始设置无法生效:

1. PRINTDLG的hDevMode类型赋值错误

PRINTDLGA的hDevMode成员是HGLOBAL全局内存句柄,但你直接把GlobalLock返回的指针(PDEVMODEA)赋值给它,系统无法正确识别这块全局内存,自然读不到你设置的初始值。

2. 缺少DEVNAMES结构的初始化

要指定初始打印机,不能只提供DEVMODE,必须同时配置DEVNAMES结构——它包含打印机的驱动名、设备名、端口名信息,PrintDlg需要靠它定位到指定打印机。

3. 内存操作不规范

修改全局内存后未调用GlobalUnlock,会导致内存状态异常,影响后续操作。


修正后的代码

inline int OpenPrintSetupDlg(DEVMODEA n_in_info, CPrintInfo* n_out_info, BOOL is_inly_set) {
    int result = PRINT_CN;
    PRINTDLGA printDlg = { 0 };
    printDlg.lStructSize = sizeof(PRINTDLGA);
    printDlg.Flags = is_inly_set ? PD_RETURNDC | PD_PRINTSETUP : PD_RETURNDC;
    
    // 申请DEVMODE和DEVNAMES的全局内存
    HGLOBAL hDevMode = GlobalAlloc(GMEM_MOVEABLE | GMEM_ZEROINIT, sizeof(DEVMODEA));
    HGLOBAL hDevNames = nullptr;

    if (!hDevMode) {
        return result;
    }

    // 初始化DEVMODE
    PDEVMODEA pDevMode = (PDEVMODEA)GlobalLock(hDevMode);
    if (pDevMode) {
        memcpy(pDevMode, &n_in_info, sizeof(DEVMODEA));
        // 必须设置dmSize,确保系统识别DEVMODE的有效大小
        pDevMode->dmSize = sizeof(DEVMODEA);
        GlobalUnlock(hDevMode);
        printDlg.hDevMode = hDevMode;
    } else {
        GlobalFree(hDevMode);
        return result;
    }

    // 初始化DEVNAMES:需要目标打印机的驱动、设备、端口信息
    // 示例中假设驱动名/设备名与dmDeviceName一致,端口为实际打印机端口(需自行替换)
    const char* driverName = n_in_info.dmDeviceName;
    const char* deviceName = n_in_info.dmDeviceName;
    const char* portName = "LPT1:"; // 替换为目标打印机的真实端口

    int driverLen = strlen(driverName) + 1;
    int deviceLen = strlen(deviceName) + 1;
    int portLen = strlen(portName) + 1;
    int devNamesSize = sizeof(DEVNAMES) + driverLen + deviceLen + portLen;

    hDevNames = GlobalAlloc(GMEM_MOVEABLE | GMEM_ZEROINIT, devNamesSize);
    if (hDevNames) {
        LPDEVNAMES pDevNames = (LPDEVNAMES)GlobalLock(hDevNames);
        if (pDevNames) {
            pDevNames->wDriverOffset = sizeof(DEVNAMES);
            pDevNames->wDeviceOffset = pDevNames->wDriverOffset + driverLen;
            pDevNames->wOutputOffset = pDevNames->wDeviceOffset + deviceLen;
            pDevNames->wDefault = 0;

            memcpy((char*)pDevNames + pDevNames->wDriverOffset, driverName, driverLen);
            memcpy((char*)pDevNames + pDevNames->wDeviceOffset, deviceName, deviceLen);
            memcpy((char*)pDevNames + pDevNames->wOutputOffset, portName, portLen);

            GlobalUnlock(hDevNames);
            printDlg.hDevNames = hDevNames;
        } else {
            GlobalFree(hDevNames);
            hDevNames = nullptr;
        }
    }

    if (PrintDlgA(&printDlg)) {
        if (n_out_info) {
            // 读取选中的打印机名称
            if (printDlg.hDevNames) {
                LPDEVNAMES lpDevNames = (LPDEVNAMES)GlobalLock(printDlg.hDevNames);
                if (lpDevNames) {
                    LPSTR lpszDevice = (LPSTR)lpDevNames + lpDevNames->wDeviceOffset;
                    n_out_info->m_DeviceName = CloneTextData(lpszDevice);
                    GlobalUnlock(lpDevNames);
                }
            }
            // 读取打印设置信息
            if (printDlg.hDevMode) {
                PDEVMODEA devmode = (PDEVMODEA)GlobalLock(printDlg.hDevMode);
                if (devmode) {
                    n_out_info->set_info(devmode);
                    GlobalUnlock(devmode);
                }
            }
        }
        result = PRINT_OK;
    }

    // 正确释放全局内存
    if (printDlg.hDevMode) {
        GlobalFree(printDlg.hDevMode);
    }
    if (printDlg.hDevNames) {
        GlobalFree(printDlg.hDevNames);
    }

    return result;
}

额外注意事项

  • 如果你不知道目标打印机的驱动名和端口名,需调用EnumPrinters枚举系统打印机,通过dmDeviceName匹配到对应的PRINTER_INFO_2结构,从中获取真实的驱动和端口信息。
  • 确保n_in_info.dmDeviceName是系统中已存在的打印机名称,否则系统会自动 fallback 到默认打印机。
  • DEVMODE的dmSize必须正确设置为sizeof(DEVMODEA),否则系统可能忽略你配置的参数。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.01 06:45:30