【问题标题】:How to get correct hDevMode values from CPrintDialogEx (PrintDlgEx)?如何从 CPrintDialogEx (PrintDlgEx) 中获取正确的 hDevMode 值?
【发布时间】:2010-10-01 16:58:23
【问题描述】:

我正在显示一个 CPrintDialogEx 对话框来选择打印机并修改设置。我设置了 hDevNames 成员,以便选择默认打印机,但我将 hDevMode 设置为 NULL。成功返回时,我从 hDevMode 中提取返回的 DEVMODE 结构中的一些值,例如纸张大小。

我遇到了问题,因为 hDevMode 似乎是使用我传入的默认打印机的值进行初始化的,而不是最终选择的打印机。如何从实际选择的打印机中获取参数?


根据要求,这里是代码的相关部分。为了空间,我已经删除了一些。 TOwnedHandle 是我编写的一个智能指针,用于持有内存句柄并自动锁定它。
CPrintDialogEx dlg(PD_ALLPAGES | PD_NOCURRENTPAGE | PD_NOPAGENUMS | PD_NOSELECTION, this);
ASSERT(dlg.m_pdex.hDevMode == NULL);
ASSERT(dlg.m_pdex.hDevNames == NULL);
dlg.m_pdex.hDevNames = GlobalAlloc(GHND, sizeof(DEVNAMES) + iSizeName);
DEVNAMES * pDevNames = (DEVNAMES *) GlobalLock(dlg.m_pdex.hDevNames);
// ...
GlobalUnlock(dlg.m_pdex.hDevNames);
if ((dlg.DoModal() == S_OK) && (dlg.m_pdex.dwResultAction == PD_RESULT_PRINT))
{
    TOwnedHandle<DEVMODE> pDevMode = dlg.m_pdex.hDevMode;
    TRACE("Printer config = %dx%d %d\n", (int)pDevMode->dmPaperWidth, (int)pDevMode->dmPaperLength, (int)pDevMode->dmOrientation);
    // ...
}


编辑:我确定如果我不设置 hDevNames 参数就不会出现问题。我想知道我是否发现了一个 Windows 错误?这是在 XP 中,我没有更新版本的 Windows 方便测试。

我已经将代码提炼成一个不使用 MFC 的测试,这严格来说是一个 Windows API 问题。这就是全部内容,除了 pDefaultPrinter 的定义之外没有遗漏任何内容 - 但当然它不再做任何有用的事情了。

    PRINTDLGEX ex = {sizeof(PRINTDLGEX)};
    ex.hwndOwner = m_hWnd;
    ex.Flags = PD_ALLPAGES | PD_NOCURRENTPAGE | PD_NOPAGENUMS | PD_NOSELECTION;
    ex.nStartPage = START_PAGE_GENERAL;
#if 1
    int iSizeName = (strlen(pDefaultPrinter) + 1) * sizeof(char);
    ex.hDevNames = GlobalAlloc(GHND, sizeof(DEVNAMES) + iSizeName);
    DEVNAMES * pDevNames = (DEVNAMES *) GlobalLock(ex.hDevNames);
    ASSERT(pDevNames != NULL);
    pDevNames->wDeviceOffset = sizeof(DEVNAMES);
    strcpy((char *)pDevNames + pDevNames->wDeviceOffset, pDefaultPrinter);
    GlobalUnlock(ex.hDevNames);
#endif
    HRESULT hr = PrintDlgEx(&ex);
    if ((hr == S_OK) && (ex.dwResultAction == PD_RESULT_PRINT))
    {
        DEVMODE * pdm = (DEVMODE *) GlobalLock(ex.hDevMode);
        ASSERT(pdm != NULL);
        TRACE("Printer config = %dx%d %d\n", (int)pdm->dmPaperWidth, (int)pdm->dmPaperLength, (int)pdm->dmOrientation);
        GlobalUnlock(ex.hDevMode);
        DEVNAMES * pdn = (DEVNAMES *) GlobalLock(ex.hDevNames);
        ASSERT(pdn != NULL);
        TRACE(_T("Printer device = %s\n"), (char *)pdn + pdn->wDeviceOffset);
        GlobalUnlock(ex.hDevNames);
    }

如果我无法解决问题,我很想听听解决方法。

【问题讨论】:

  • 很难出错。发布显示 DoModal 和 GetDevMode 的代码。
  • 不错的收获...很难被发现。

标签: windows winapi mfc


【解决方案1】:

经过多次挠头,我想我已经弄明白了。

当对话框最初出现时,hDevMode 成员被填充为最初选择的打印机的默认值。如果您在关闭对话框之前选择了不同的打印机,则该 DEVMODE 结构将呈现给新的打印机驱动程序;如果纸张尺寸对驱动程序没有意义,它可能会更改它,并且驱动程序不一致。

这让我感到困惑的原因是我在三台打印机之间切换:两个标签 具有非常不同特性的打印机,以及使用美国信纸的激光打印机。

  • 激光打印机始终以正确的尺寸响应,但可能会指示错误的纸张尺寸代码。
  • 第一台标签打印机将覆盖激光打印机提供的尺寸,但不会覆盖另一台标签打印机。
  • 第二台标签打印机将接受第一台标签打印机提供的尺寸,因为即使它未加载且未配置,它也能够使用该尺寸。它通过返回最大宽度和 11 英寸的 Letter 尺寸长度来修改激光打印机提供的尺寸。

我确定了两种解决问题的方法。第一种是实现IPrintDialogCallback 并通过为新选择的打印机重新加载默认DEVMODE 来响应SelectionChange 调用。 编辑:我试过了,它不起作用。 CPrintDialogEx 已经实现了一个 IPrintDialogCallback 接口,这很容易。 PrintDlgEx 似乎有自己的内部句柄,用于跟踪当前的 DEVMODE 结构,并且仅使用 PRINTDLGEX 结构中的一个进行输入/输出。对话框启动时无法影响 DEVMODE,当它返回时为时已晚。

第二种解决方案是完全忽略返回的结果,并使用打印机的默认纸张配置。在对话框中对打印机默认值所做的任何更改都将完全丢失,但对于我的应用程序来说这是可以接受的。

bool MyDialog::GetPaperSize(const TCHAR * pPrinterName, double & dPaperWidth, double & dPaperLength)
{
    // you need to open the printer before you can get its properties
    HANDLE hPrinter;
    if (OpenPrinter((TCHAR *)pPrinterName, &hPrinter, NULL))
    {
        // determine how much space is needed for the DEVMODE structure by the printer driver
        int iDevModeSize = DocumentProperties(m_hWnd, hPrinter, (TCHAR *)pPrinterName, NULL, NULL, 0);
        ASSERT(iDevModeSize >= sizeof(DEVMODE);

        // allocate a DEVMODE structure and initialize it to a clean state
        std::vector<char> buffer(iDevModeSize, 0);
        DEVMODE * pdm = (DEVMODE *) &buffer[0];
        pdm->dmSpecVersion = DM_SPECVERSION;

        DocumentProperties(m_hWnd, hPrinter, (TCHAR *)pPrinterName, pdm, NULL, DM_OUT_BUFFER);
        ClosePrinter(hPrinter);

        // convert paper size from tenths of a mm to inches
        dPaperWidth = pdm->dmPaperWidth / 254.;
        dPaperLength = pdm->dmPaperLength / 254.;

        return true;
    }
    return false;
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2014-12-21
    • 1970-01-01
    • 1970-01-01
    • 2019-10-12
    • 1970-01-01
    • 1970-01-01
    • 2015-04-23
    • 2015-05-15
    相关资源
    最近更新 更多