Как получить правильные значения hDevMode из CPrintDialogEx (PrintDlgEx)? - PullRequest
1 голос
/ 01 октября 2010

Я отображаю диалоговое окно CPrintDialogEx, чтобы выбрать принтер и изменить настройки.Я установил член hDevNames так, чтобы был выбран принтер по умолчанию, но я оставил для hDevMode значение NULL.При успешном возврате я извлекаю некоторые значения, такие как размер бумаги, из возвращенной структуры DEVMODE из hDevMode.

У меня проблема, потому что кажется, что 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);
    }

Если я не могу получить исправление, я бы с удовольствиемуслышать об обходном пути.

1 Ответ

0 голосов
/ 07 октября 2010

После долгих царапин на голове, я думаю, я понял это.

Когда диалоговое окно появляется изначально, элемент hDevMode заполняется значениями по умолчанию для принтера, который был выбран изначально. Если вы выберете другой принтер перед закрытием диалогового окна, эта структура DEVMODE будет представлена ​​новому драйверу принтера; если размер бумаги не имеет смысла для драйвера, он может изменить его, и драйверы не согласованы.

Причина, по которой меня это сбило с толку, заключается в том, что я переключался между тремя принтерами: двумя этикетками принтеры с очень разными характеристиками и лазерный принтер с бумагой US Letter.

  • Лазерный принтер всегда реагирует с правильными размерами, но может указывать неправильный код формата бумаги.
  • Первый принтер этикеток заменит размер, предоставленный лазерным принтером, но не другой принтер этикеток.
  • Второй принтер этикеток примет размер, предоставленный первым принтером этикеток, поскольку он может использовать этот размер, даже если он не загружен и не настроен. Он изменяет размер, предоставляемый лазерным принтером, возвращая максимальную ширину и длину буквы 11 дюймов.

Я определил два способа обойти эту проблему. Первый - реализовать IPrintDialogCallback и ответить на вызовы SelectionChange, перезагрузив DEVMODE по умолчанию для вновь выбранного принтера. РЕДАКТИРОВАТЬ: Я пробовал это и это не работает . 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;
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...