Форум программистов «Весельчак У»
  *
Добро пожаловать, Гость. Пожалуйста, войдите или зарегистрируйтесь.
Вам не пришло письмо с кодом активации?

  • Рекомендуем проверить настройки временной зоны в вашем профиле (страница "Внешний вид форума", пункт "Часовой пояс:").
  • У нас больше нет рассылок. Если вам приходят письма от наших бывших рассылок mail.ru и subscribe.ru, то знайте, что это не мы рассылаем.
   Начало  
Наши сайты
Помощь Поиск Календарь Почта Войти Регистрация  
 
Страниц: [1]   Вниз
  Печать  
Автор Тема: TPrinter() как установить размер бумаги  (Прочитано 10853 раз)
0 Пользователей и 1 Гость смотрят эту тему.
AndrewSem
Интересующийся

ru
Offline Offline

« : 30-01-2012 10:57 » 

Всем привет. Столкнулся с проблемой. Есть стандартный принтер для бумаги А4. Нужно на принтере напечатать что-то но на другом формате бумаги (стандартный формат, предусмотренный Windows), например А6. При этом, системные настройки принтера не должны меняться. (Т.е. настройки, что в «устройствах и принтерах->настройка печати») . Кроме того, не должно выводиться пользователю никаких диалогов выбора бумаги. В каких то старых книжках прочитал, что нужно использовать структуру PDEVMODE. На форумах нашел похожий код на Delphi. Правда там не размеры меняют, а число копий и т.д…Мой код никаких ошибок не выдает, но и ничего не делает почему-то. Может кто знает в чем дело или как решить мою задачу?
Код:
Printer()->PrinterIndex = 2;
ShowMessage("Область печати (до изменений): " + IntToStr(Printer()->PageWidth) + "x" + IntToStr(Printer()->PageHeight));   
UnicodeString DeviceName, DriveName, PortName;  //Фиктивная переменная
THandle hPrnDevMode;                            //Фиктивная переменная
PDEVMODE pDevMode;                              //Указатель на структуру DeviceMode
Printer()->GetPrinter(DeviceName.c_str(), DriveName.c_str(), PortName.c_str(), hPrnDevMode);
if (hPrnDevMode == 0)
{
ShowMessage("Не получилось настроить параметры печати!");
return;
}
//Получаем указатель на структуру DEVMODE
pDevMode = (PDEVMODE) GlobalLock((HANDLE)hPrnDevMode);
if (pDevMode == NULL)
{
ShowMessage("Не получилось настроить параметры печати!");
return;
}
//Настраиваем параметры печати
pDevMode->dmFields |= DM_PAPERSIZE;
pDevMode->dmPaperSize = DMPAPER_A6;
//Освобождаем структуру DEVMODE
GlobalUnlock((HANDLE)hPrnDevMode);
ShowMessage("Область печати (после изменений): " + IntToStr(Printer()->PageWidth) + "x" + IntToStr(Printer()->PageHeight)); 
Записан
Джон
просто
Администратор

de
Offline Offline
Пол: Мужской

« Ответ #1 : 30-01-2012 12:32 » 

Ну в принципе всё правильно. Сначала получаем из системы копию DEVMODE структуры для данного принтера, потом изменяем в ней необходимые параметры - в данном случае это размер и тип бумаги. А вот что дальше? Ты говоришь, что твой код ничего не делает. А что он должен делать? Чего ты ждёшь? Ведь никакого вывода на печать не происходит.
В MFC С++ я, например, использую её для получения DC с помощью AfxCreateDC  и уже в него отрисовываю всё необходимое.

Записан

Я вам что? Дурак? По выходным и праздникам на работе работать. По выходным и праздникам я работаю дома.
"Just because the language allows you to do something does not mean that it’s the correct thing to do." Trey Nash
"Physics is like sex: sure, it may give some practical results, but that's not why we do it." Richard P. Feynman
"All science is either physics or stamp collecting." Ernest Rutherford
"Wer will, findet Wege, wer nicht will, findet Gründe."
AndrewSem
Интересующийся

ru
Offline Offline

« Ответ #2 : 30-01-2012 13:02 » 

Потом печатем что-нибудь:
Код:
Printer()->BeginDoc();
...
Printer()->EndDoc();
Проблема в том, что размер бумаги не изменился. Был А4 и остался А4 (а не А6).
Записан
Джон
просто
Администратор

de
Offline Offline
Пол: Мужской

« Ответ #3 : 30-01-2012 18:09 » 

Хм... а для чего тебе тогда код выше? Ты же никак изменённую структуру не используешь для печати. Ты же сам сказал, что:

При этом, системные настройки принтера не должны меняться. (Т.е. настройки, что в «устройствах и принтерах->настройка печати») .

Я думаю (хотя точно не знаю, я с Борландом не работаю), что Printer()->BeginDoc() берёт системные настрйки. Поэтому у тебя ничего не меняется. Мелкософтоским кодом я бы мог тебе помочь, но Борланда у меня нет, даже чтобы попробовать.
Так что сорри. Попробуй посмотри примеры как использовать изменённую DEVMODE структуру. Как я уже сказал, в MFC есть ф-я генерации DC, которая получает эту структуру в качестве параметра. Может у Борланда есть аналог.
Ибо до этого момента код приведённый выше делает всё правильно. Для сравнения, если хочешь, я могу показать MFC-шный код, который делает абсолютно тоже самое - мне в моём  проекте надо менять размер бумаги для этикеток. Ну, с той лишь разницей, что я получаю DC для последущей отрисовки. Так что на уровне API всё корректно.

зы Попробуй сделать эту операцию два раза. Те получить хэндл, изменить параметры, закрыть, снова получить и проверить параметры. Если они не изменились, то ты получаешь копию структуры. Собственно это и правильно. Представь если бы каждая прога меняла системные установки драйвера принтера. Так что я думаю, тебе надо найти способ использовать hPrnDevMode.
Записан

Я вам что? Дурак? По выходным и праздникам на работе работать. По выходным и праздникам я работаю дома.
"Just because the language allows you to do something does not mean that it’s the correct thing to do." Trey Nash
"Physics is like sex: sure, it may give some practical results, but that's not why we do it." Richard P. Feynman
"All science is either physics or stamp collecting." Ernest Rutherford
"Wer will, findet Wege, wer nicht will, findet Gründe."
AndrewSem
Интересующийся

ru
Offline Offline

« Ответ #4 : 30-01-2012 19:39 » 

Джон, давай пример на MFC, может разберусь..
Цитата
Printer()->BeginDoc() берёт системные настрйки
Я проверял. 1) Я в 2-х книгах видел похожий код. И там черным по белому написано, что настройки не сохраняются в этом случае. Достаточно поменять индекс принтера и все сбросится. Но книги были по Билдеру и Делфи 5 версии. Т.е. старье. 2) Я попробовал устанавливать размер бумаги через системный диалог PrinterSetupDialog. При этом, Printer() брал нужные размеры, а в настройках печати принтера ничего не менялось.

В принципе изображение я формирую в битмапе или загружаю из файла. Т.е. на канву принтера мне просто скопировать и растянуть изображение нужно. Тогда может подскажешь как через API напечатать картинку?
Записан
Джон
просто
Администратор

de
Offline Offline
Пол: Мужской

« Ответ #5 : 30-01-2012 21:35 » 

Печать равносильна отрисовке ф-ций GDI в DC. Те точно так как это делается на экране, только в данном случае DC берём принтера.

Вот эта ф-я фозвращает мне DC для выбранного принтера (имя известно)
(click to show)
Код: (C++)
HDC CreatePrinterDC(CString stPrinterName)
{
        HDC hdcPrnOut = NULL;

        HGLOBAL hDevMode = NULL;
        HGLOBAL hDevNames = NULL;

        if(GetPrinterDevice((LPTSTR)stPrinterName.GetBuffer(stPrinterName.GetLength()), &hDevNames, &hDevMode))
        {
                CString stPrinterDriverModelName = GetPrinterDriverName(stPrinterName);

                LPDEVMODE pDevMode = (LPDEVMODE)::GlobalLock(hDevMode);
                short nOrientation = theApp.GetPrinterOrientation(stPrinterName);
                if(nOrientation!=0)
                {
                        pDevMode->dmOrientation = nOrientation;// DMORIENT_PORTRAIT=1; DMORIENT_LANDSCAPE=2;
                }

                int nCfgPageLength = 100000;
                int nCfgPageWidth = 100000;
                pDevMode->dmFields |= (DM_PAPERSIZE|DM_PAPERLENGTH|DM_PAPERWIDTH);
                pDevMode->dmPaperSize = 0;
                pDevMode->dmPaperLength = nCfgPageLength/100;
                pDevMode->dmPaperWidth = nCfgPageWidth/100;
       
                ::GlobalUnlock(hDevMode);
                hdcPrnOut = AfxCreateDC(hDevNames, hDevMode);
                ::GlobalFree(hDevMode);
                ::GlobalFree(hDevNames);
        }
        return hdcPrnOut;
}
я её немного обкарнал и фэйкнул значения ширины и высоты, у меня ещё там разные логики для различных принтеров, к сути отношение не очень имеющие.

GetPrinterDevice делает то же, что и GetPrinter в твоём коде.
(click to show)
Код: (C++)
// returns a DEVMODE and DEVNAMES for the printer name specified
BOOL GetPrinterDevice(LPTSTR pszPrinterName, HGLOBAL* phDevNames, HGLOBAL* phDevMode)
{
        // if NULL is passed, then assume we are setting app object's
        // devmode and devnames
        if (phDevMode == NULL || phDevNames == NULL)
                return FALSE;

        // Open printer
        HANDLE hPrinter;
        if (OpenPrinter(pszPrinterName, &hPrinter, NULL) == FALSE)
                return FALSE;

        // obtain PRINTER_INFO_2 structure and close printer
        DWORD dwBytesReturned = 0;
        DWORD dwBytesNeeded = 0;
        GetPrinter(hPrinter, 2, NULL, 0, &dwBytesNeeded);
        PRINTER_INFO_2* p2 = (PRINTER_INFO_2*)GlobalAlloc(GPTR, dwBytesNeeded);

        if (GetPrinter(hPrinter, 2, (LPBYTE)p2, dwBytesNeeded, &dwBytesReturned) == 0)
        {
                GlobalFree((HGLOBAL)p2);
                ClosePrinter(hPrinter);
                return FALSE;
        }
        ClosePrinter(hPrinter);

        // Allocate a global handle for DEVMODE
        SIZE_T st = sizeof(*p2->pDevMode);
        SIZE_T st1 = sizeof(DEVMODE);
        SIZE_T st2 = p2->pDevMode != NULL ? p2->pDevMode->dmDriverExtra : 0;
        SIZE_T size = max(st1,st) + st2;

        HGLOBAL  hDevMode = GlobalAlloc(GHND, size);
        ASSERT(hDevMode);
        DEVMODE* pDevMode = (DEVMODE*)GlobalLock(hDevMode);
        ASSERT(pDevMode);

        // copy DEVMODE data from PRINTER_INFO_2::pDevMode
        if(p2->pDevMode != NULL)
                memcpy(pDevMode, p2->pDevMode, st1 + st2);
        GlobalUnlock(hDevMode);

        // Compute size of DEVNAMES structure from PRINTER_INFO_2's data
        DWORD drvNameLen = lstrlen(p2->pDriverName)+1;  // driver name
        DWORD ptrNameLen = lstrlen(p2->pPrinterName)+1; // printer name
        DWORD porNameLen = lstrlen(p2->pPortName)+1;    // port name

        // Allocate a global handle big enough to hold DEVNAMES.
        HGLOBAL hDevNames = GlobalAlloc(GHND,sizeof(DEVNAMES) + (drvNameLen + ptrNameLen + porNameLen)*sizeof(TCHAR));
        ASSERT(hDevNames);
        DEVNAMES* pDevNames = (DEVNAMES*)GlobalLock(hDevNames);
        ASSERT(pDevNames);

        // Copy the DEVNAMES information from PRINTER_INFO_2
        // tcOffset = TCHAR Offset into structure
        int tcOffset = sizeof(DEVNAMES)/sizeof(TCHAR);
        ASSERT(sizeof(DEVNAMES) == tcOffset*sizeof(TCHAR));

        pDevNames->wDriverOffset = tcOffset;
        memcpy((LPTSTR)pDevNames + tcOffset, p2->pDriverName,
                drvNameLen*sizeof(TCHAR));
        tcOffset += drvNameLen;

        pDevNames->wDeviceOffset = tcOffset;
        memcpy((LPTSTR)pDevNames + tcOffset, p2->pPrinterName,
                ptrNameLen*sizeof(TCHAR));
        tcOffset += ptrNameLen;

        pDevNames->wOutputOffset = tcOffset;
        memcpy((LPTSTR)pDevNames + tcOffset, p2->pPortName,
                porNameLen*sizeof(TCHAR));
        pDevNames->wDefault = 0;

        GlobalUnlock(hDevNames);
        GlobalFree((HGLOBAL)p2);   // free PRINTER_INFO_2

        // set the new hDevMode and hDevNames
        *phDevMode = hDevMode;
        *phDevNames = hDevNames;

        //AfxDumpStack();

        return TRUE;
}

Ну и собственно использование, я использую gdi+, в примере оставил отрисовку эллипса - FillEllipse:
(click to show)
Код: (C++)
        HDC hdcPrn = CreatePrinterDC(stPrinterName);

        if(hdcPrn==NULL)
        {
                TRACE0("Failed to select custom printer\n");
                return FALSE;
        }

        CDC *pDC = new CDC;
        pDC->Attach(hdcPrn);      // attach a printer DC

        pDC->StartDoc(_T("MyPrnJob"));
   
        // prepare gdi+ object
        Status st;
        Graphics *pgrMem = Graphics::FromHDC(pDC->GetSafeHdc());
        st = pgrMem->SetSmoothingMode(SmoothingModeNone);       // Avoid anti alias
        st = pgrMem->SetPageUnit(UnitMillimeter);
        st = pgrMem->SetPageScale((REAL)0.001);
        st = pgrMem->SetPixelOffsetMode(PixelOffsetModeHalf);    
   
        Brush *pBrush = new SolidBrush(Color(255, 0, 0, 0));
        pgrMem->FillEllipse(pBrush, 239000, 5500, 200, 200);

        delete pBrush;

        pDC->EndDoc();

        ::DeleteDC(pDC->Detach());    // detach the printer DC
        delete pDC;
Записан

Я вам что? Дурак? По выходным и праздникам на работе работать. По выходным и праздникам я работаю дома.
"Just because the language allows you to do something does not mean that it’s the correct thing to do." Trey Nash
"Physics is like sex: sure, it may give some practical results, but that's not why we do it." Richard P. Feynman
"All science is either physics or stamp collecting." Ernest Rutherford
"Wer will, findet Wege, wer nicht will, findet Gründe."
AndrewSem
Интересующийся

ru
Offline Offline

« Ответ #6 : 31-01-2012 07:17 » 

Спасибо. Вроде все понятно. Буду пробовать.
Тогда вот еще вопросик. Настройки принтера хранятся в структуре PDEVMODE. Эту структуру можно получить как в моем коде выше, так и через структуру (как у тебя)  PRINTER_INFO_2. Есть еще структуры PRINTER_INFO_8 и PRINTER_INFO_9 - в них тоже есть PDEVMODE. Со структурой PRINTER_INFO_2 я работал, но тогда меняются настройки принтера по умолчанию. Не скажешь, а 8 и 9 структуры для чего? Там тоже будут меняться настройки по умолчанию?
Записан
Джон
просто
Администратор

de
Offline Offline
Пол: Мужской

« Ответ #7 : 31-01-2012 08:30 » 

Ага, а есть ещё 3,4,5,6 и 7. Ага
Для таких вопросов и существует MSDN. Пересказывать времени нет. Просто для разных целей/потребностей существуют разные структуры.
Записан

Я вам что? Дурак? По выходным и праздникам на работе работать. По выходным и праздникам я работаю дома.
"Just because the language allows you to do something does not mean that it’s the correct thing to do." Trey Nash
"Physics is like sex: sure, it may give some practical results, but that's not why we do it." Richard P. Feynman
"All science is either physics or stamp collecting." Ernest Rutherford
"Wer will, findet Wege, wer nicht will, findet Gründe."
AndrewSem
Интересующийся

ru
Offline Offline

« Ответ #8 : 31-01-2012 18:57 » 

Спасибо Джон. Вроде работает. А можешь еще подсказать.
У нас с тобой в коде используется имя принтера и имя драйвера. Если имя драйвера получать через GetProfileString, имя драйвера для любого из выбранных принтеров - winspool. А если вытаскивать из Print_Info_2, то там наименование драйвера всегда совпадает с наименованием принтера. Почему имена драйверов разные И как наименование драйвера должно вообще выглядеть? При создании DC принтера, что будет, если имя принтера правильное, а имя драйвера не то или ноль?
Записан
Джон
просто
Администратор

de
Offline Offline
Пол: Мужской

« Ответ #9 : 31-01-2012 20:35 » 

Имя принтера может менять пользователь. Имя принтера в драйвере - тн Driver model name - задаётся при установке драйвера и нормальный пользователь его поменять не может. Не понимаю как оно может быть равным NULL. Ты можешь установить несколько принтеров на один драйвер, но с разными именами. Это то, что пользователь использует при обращении к определённому принтеру.

Я для себя сделал ф-ю, которая возвращает мне имя драйвера, по имени принтера. Я не совсем понял зачем тебе нужно явно имя драйвера (у меня просто определённые принтеры имеют свою логику, и мне их необходимо отличать), ИМХО тебе нужно имя принтера, но если надо то вот:

(click to show)
Код: (C++)
CString GetPrinterDriverName(CString stPrinterName)
{
        CString stOut = _T("");
        HANDLE hPrinter;
        if(::OpenPrinter(stPrinterName.GetBuffer(stPrinterName.GetLength()), &hPrinter, NULL))
        {
                // obtain PRINTER_INFO_2 structure and close printer
                DWORD dwBytesReturned, dwBytesNeeded;
                ::GetPrinter(hPrinter, 2, NULL, 0, &dwBytesNeeded);
                PRINTER_INFO_2* p2 = (PRINTER_INFO_2*)GlobalAlloc(GPTR,dwBytesNeeded);
                if(::GetPrinter(hPrinter, 2, (LPBYTE)p2, dwBytesNeeded, &dwBytesReturned)!=0)
                {
                        stOut = p2->pDriverName;
                }
                ::GlobalFree(p2);
                ::ClosePrinter(hPrinter);
        }
        return stOut;
}

ps для ясности пример. Допустим у меня установлен принтер Epson Stylus C62 Series (M), если я ничего не менял, то оба имени после установки совпадают. Теперь я могу переименовать его в, например, MyWorkPrinter. Именно это имя я и использую в ф-ях.
А например ещё один я устанавливаю, чтобы проверять выходные данные и называю его MyWorkPrinterFile и переопределяю его выход в файл. Таким образом у меня в системе два принтера - MyWorkPrinter и MyWorkPrinterFile, но Driver model name у них одинаковое, тк это две инстанции одного драйвера.
« Последнее редактирование: 31-01-2012 20:41 от Джон » Записан

Я вам что? Дурак? По выходным и праздникам на работе работать. По выходным и праздникам я работаю дома.
"Just because the language allows you to do something does not mean that it’s the correct thing to do." Trey Nash
"Physics is like sex: sure, it may give some practical results, but that's not why we do it." Richard P. Feynman
"All science is either physics or stamp collecting." Ernest Rutherford
"Wer will, findet Wege, wer nicht will, findet Gründe."
AndrewSem
Интересующийся

ru
Offline Offline

« Ответ #10 : 02-02-2012 16:47 » new

Я немного не так вопрос задал, т.к. с кодом на тот момент полностью не разобрался. Ты про имя принтера, а я про имя драйвера. Вот смотри. Ты контекст устройства создаешь так:
Код:
hdcPrnOut = AfxCreateDC(hDevNames, hDevMode);
А в Билдере вот так
Код:
HDC HPrinterDC = CreateDC(Driver, Device, NULL, hDevMode);
Т.е. чтобы создать контекст устройства мне нужно имя принтера и имя драйвера принтера. Эти сведения можно получить  несколькими способами:
1) GetProfileString() вернет результат вида "HP LaserJet, winspool, LPT1: " (пример взят из книги). Я попробовал этот код, у меня несколько принтеров. И действительно, имя драйвера всегда возвращает "winspool".
2) Print_Info_2. Из нее получаем структуру PDEVMODE. Вот в ней есть pPrinterName и pDriverName. И тут например, имя принтера "HP LaserJet" и имя драйвера тоже "HP LaserJet".
Вот и вопрос, для создания HDC принтера откуда брать правильные сведения?
Вот меня смущает то, что я беру имя драйвера принтера (так же как и ты!) из Print_Info_2. Но при этом имя драйвера равно имени принтера.
PS Спасибо за информацию. Натолкнул на правильный путь.
« Последнее редактирование: 02-02-2012 16:50 от AndrewSem » Записан
Страниц: [1]   Вверх
  Печать  
 

Powered by SMF 1.1.21 | SMF © 2015, Simple Machines