使用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
相关产品推荐
相关产品推荐

