Brauche Hilfe bei PrintDlg() und DEVMODE-Struktur (Drucken im Querformat)
-
_matze schrieb:
//erzeugt manchmal (!) einen nicht behandelten Fehler in KERNEL32.DLL (nicht reproduzierbar),
... memset (&lpPrintDlg,0,sizeof(PRINTDLG)); // <- ! lpPrintDlg.lStructSize = (DWORD) sizeof (lpPrintDlg); ...
-
@mitos:
So, sieh dir diesen Code hier mal an. Da wird ein Bild auf den zuvor über einen PrintDialog ausgewählten Drucker (lpPrintDlg.hDC) über die Funktion BitBlt() ausgegeben. Desweiteren ist die Funktion StretchBlt() interessant, da sie dein Bild verkleinern oder vergrößern kann (einfach das Zielrechteck anders bemessen).
Der Code ist zwar ziemlich aus dem Kontext gerissen, aber ich denke, als Beispiel reicht es. Das ist Code, den ich auf der Arbeit geschrieben hab, den kann ich natürlich nicht komplett posten.
if(StartDoc(lpPrintDlg.hDC,&di) > 0) { if(StartPage(lpPrintDlg.hDC) > 0) { // if(!BitBlt( lpPrintDlg.hDC, //Bild wird zentriert auf dem Drucker ausgegeben (iPrinterWidth-pn.x)/2, //Startposition X (Target) 0/*(iPrinterHeight-pn.y)/2*/, //Startposition Y (Target) (pn.x)+((iPrinterWidth-pn.x)/2), //Breite (Target) pn.y/*(pn.y)+((iPrinterHeight-pn.y)/2)*/, //Höhe (Target) memDC, 0, //Startposition X (Source) 0, //Startposition Y (Source) SRCCOPY)) { //hier wird die bereits gestreckte Grafik //if(!BitBlt(lpPrintDlg.hDC,25,0,pn.x,pn.y,memDC,0,0,SRCCOPY)) { //hier wird die bereits gestreckte Grafik //BitBlt ist fehlgeschlagen //in den Drucker-DC geblittet ::SelectObject(memDC, hbmOld); ::DeleteDC(memDC); ::ReleaseDC(hWnd,hdc); ::DeleteObject(hbm); delete[] pbBits; return ERR_BLTWRITEFAILED; } if(EndPage(lpPrintDlg.hDC ) > 0) { EndDoc(lpPrintDlg.hDC); }else { ::SelectObject(memDC, hbmOld); ::DeleteDC(memDC); ::ReleaseDC(hWnd,hdc); ::DeleteObject(hbm); delete[] pbBits; return ERR_ENDPAGEFAILED; } }else { ::SelectObject(memDC, hbmOld); ::DeleteDC(memDC); ::ReleaseDC(hWnd,hdc); ::DeleteObject(hbm); delete[] pbBits; return ERR_STARTPAGEFAILED; } }else { ::SelectObject(memDC, hbmOld); ::DeleteDC(memDC); ::ReleaseDC(hWnd,hdc); ::DeleteObject(hbm); delete[] pbBits; return ERR_STARTDOCFAILED; } // DeleteDC( lpPrintDlg.hDC );Vielleicht interessiert dich auch diese Funktion, da wird ein Screenshot vom gesamten Desktop erzeugt und in eine Datei geschrieben. Die kannst du natürlich entsprechend abändern, um den Screenshot auf dem Drucker deiner Wahl auszugeben.
void ScreenShot(void) { int nWidth = GetSystemMetrics(SM_CXSCREEN); int nHeight = GetSystemMetrics(SM_CYSCREEN); HWND hWnd = ::GetDesktopWindow(); HDC hdc = ::GetDC(hWnd); HDC memDC = ::CreateCompatibleDC(hdc); HBITMAP hbm = ::CreateCompatibleBitmap(hdc, nWidth, nHeight); HBITMAP hbmOld = (HBITMAP)::SelectObject(memDC, hbm); ::BitBlt(memDC, 0, 0, nWidth, nHeight, hdc, 0, 0, SRCCOPY); BITMAPINFO bmi; ZeroMemory(&bmi, sizeof(bmi)); bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER); bmi.bmiHeader.biWidth = nWidth; bmi.bmiHeader.biHeight = nHeight; bmi.bmiHeader.biBitCount = 24; bmi.bmiHeader.biPlanes = 1; bmi.bmiHeader.biCompression = BI_RGB; bmi.bmiHeader.biSizeImage = 32 * nWidth * nHeight / 8; BYTE *pbBits = new BYTE[bmi.bmiHeader.biSizeImage]; ::GetDIBits( memDC, hbm, 0, bmi.bmiHeader.biHeight, pbBits, &bmi, DIB_RGB_COLORS ); BITMAPFILEHEADER bfh; bfh.bfType = ('M' << 8) + 'B'; bfh.bfSize = sizeof(BITMAPFILEHEADER) + bmi.bmiHeader.biSizeImage + sizeof(BITMAPINFOHEADER); bfh.bfReserved1 = 0; bfh.bfReserved2 = 0; bfh.bfOffBits = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER); HANDLE hfile = CreateFile( _T("c:\\temp\\screen.bmp"), GENERIC_WRITE, 0, 0, OPEN_ALWAYS, 0, 0 ); DWORD dwWritten; WriteFile(hfile,&bfh, sizeof(bfh), &dwWritten, NULL); WriteFile(hfile,&bmi.bmiHeader, sizeof(BITMAPINFOHEADER), &dwWritten, NULL); WriteFile(hfile,pbBits, bmi.bmiHeader.biSizeImage, &dwWritten, NULL); CloseHandle(hfile); ::SelectObject(memDC, hbmOld); ::DeleteDC(memDC); ::ReleaseDC(hWnd,hdc); ::DeleteObject(hbm); delete[] pbBits; }@merker:
Danke für den Tipp! Ich muss mich wohl noch dran gewöhnen, dass in C++ nichts von selbst initialisiert wird und immer erstmal Müll in den Variablen steht. Ach, ich sehne mir die alten Clipper-Zeiten herbei. Andererseits, wenn ich daran denke, einen solchen Grafikdruck im Clipper zu realisieren, oh je...
@All:
Übrigens, im oben geposteten Code gibt's diese Zeile hier:
if(!BitBlt( lpPrintDlg.hDC, //Bild wird zentriert auf dem Drucker ausgegeben (iPrinterWidth-pn.x)/2, //Startposition X (Target) 0/*(iPrinterHeight-pn.y)/2*/, //Startposition Y (Target) (pn.x)+((iPrinterWidth-pn.x)/2), //Breite (Target) pn.y/*(pn.y)+((iPrinterHeight-pn.y)/2)*/, //Höhe (Target) memDC, 0, //Startposition X (Source) 0, //Startposition Y (Source) SRCCOPY)) { //hier wird die bereits gestreckte GrafikDas Bild soll zentriert ausgegeben werden. Horizontal funktioniert das auch sehr gut, wenn ich jedoch die gleiche Formel benutze, um vertikal zu zentrieren, kommt nur Müll raus, entweder ein leeres Blatt oder nur schwarze, dicke Streifen, vom Bild keine Spur. Dabei mache ich nichts anderes, als das Bild nach unten zu verschieben. Die Höhe des Bildes bleibt gleich (habe ich nachgerechnet). Wäre klasse, wenn jemand eine Erklärung bzw. einen Lösungsvorschlag hätte.
Gruß Matze
-
@merker:
Dein Tipp hat leider nichts geändert. Hin und wieder tritt einfach diese nicht behandelte Ausnahme in KERNEL32.DLL (trotz memset) auf. Aber wirklich nur hin und wieder, ich musste das Programm gerade mindestens 20 mal starten, um den Fehler zu bekommen.
Nicht abgefangene Ausnahme in testDLL.exe (KERNEL32.DLL): 0x000006BA: (kein Name). Nicht abgefangene Ausnahme in testDLL.exe (KERNEL32.DLL): 0x000006BA: (kein Name).Ist ja auch nicht weiter schlimm, da der Fehler scheinbar keine Auswirkungen hat. Es würde mich eben nur interessieren, wie das zustande kommt.
Gruß Matze
-
super danke!

-
Die Sache mit der Zentrierung hat sich erledigt, da sich soeben 'rausgestellt hat, dass die schwarzen Balken nur gekommen sind, wenn ich einen PDF-Drucker verwendet habe (da aber auch wirklich nur, wenn ich die vertikale Zentrierung eingeschaltet habe, horizontal ist kein Problem).
Tja, man sollte beim Entwickeln von Druck-Funktionen hin und wieder auch mal mit einem echten Drucker testen. Das kann nicht schaden, ganz im Gegenteil...
Die nicht behandelte Ausnahme in KERNEL32.DLL werde ich wohl ignorieren, da es scheinbar keine Auswirkungen gibt, genauso wenig eine Lösung. Im Internet findet man (bzw. ich) dazu nichts.
Trotzdem bräuchte ich noch eine Lösung für das Anfangsproblem (siehe erstes Posting). Hat denn keiner mal ein WinAPI-Beispiel für die korrekte Verwendung von PrintDlg und der DEVMODE-Struktur? Ich will doch nur das Querformat vorauswählen...
Gruß Matze
-
Hat wirklich keiner einen Rat für mich bzgl. der DEVMODE-Struktur (siehe letztes Posting)? Ein funktionierendes Beispiel wäre super. Es geht ja nur darum, das Querformat vorauszuwählen, das klappt bei mir aber leider nicht.
Die Struktur wird so definiert:
memset(&lpDevmode,0,sizeof(DEVMODE)); //Struktur leeren, damit kein Quatsch drinsteht lpDevmode.dmSize =sizeof(DEVMODE); lpDevmode.dmOrientation =DMORIENT_LANDSCAPE; lpDevmode.dmCopies =1; lpDevmode.dmFields =DM_ORIENTATION|DM_COPIES;Die Zuweisung im PrintDialog sieht folgendermaßen aus:
memset(&lpPrintDlg,0,sizeof(PRINTDLG)); //soll die nicht behandelte Ausnahme in KERNEL32.DLL verhindern (tut es aber nicht!) lpPrintDlg.lStructSize = (DWORD)sizeof(lpPrintDlg); lpPrintDlg.hwndOwner = NULL; lpPrintDlg.hDevMode = &lpDevmode; lpPrintDlg.hDevNames = NULL; lpPrintDlg.hDC = NULL; lpPrintDlg.Flags = PD_HIDEPRINTTOFILE|PD_NONETWORKBUTTON|PD_NOPAGENUMS|PD_NOSELECTION|PD_RETURNDC|PD_USEDEVMODECOPIESANDCOLLATE; PrintDlg(&lpPrintDlg); //erzeugt manchmal (!) eine nicht behandelte Ausnahme in KERNEL32.DLL (nicht reproduzierbar),Weder wird meine Voreinstellung (Landscape) auf den PrintDlg übertragen, noch wird der im Dialog ausgewählte Wert in der DEVMODE-Struktur gespeichert. Beim Drucken wird zwar das richtige Format benutzt, trotzdem frage ich mich, wozu ich eine DEVMODE-Struktur anlegen soll, wenn sie gar keine Auswirkungen hat.
Also bitte, was mache ich falsch?!
Gruß Matze
-
_matze schrieb:
Also bitte, was mache ich falsch?!Gruß Matze
Ähem ... PrintDlg() und die DEVMODE-Struktur sind für dein Vorhaben vielleicht nicht ganz die richtige Vorgehensweise ... Hüstel ... für Seitenränder und PAPIERAUSRICHTUNG ist eigentlich die WinAPI-Funktion PageSetupDlg() mit ihrer Structur PAGESETUPDLG zuständig ... Räusper ... aber ich will mich da mal lieber nicht einmischen.
-
@martze: Falsche Vorgehensweise! Besorge Dir zuerst die aktuele DEVMODE Struktur. Du sollst keine neue initialisieren.
@schmidt-webdesign.net: Warum? Es geht doch auch direkt?IMHO must du folgendes machen:
// pd is my PRINTDLG LPDEVMODE pDevMode = (LPDEVMODE)::GlobalLock( pd.hDevMode); if (pDevMode->dmFields & DM_ORIENTATION) pDevMode->dmOrientation = DMORIENT_LANDSCAPE; GlobalUnlock( pd.hDevMode);
-
Danke, Martin. Deine Antwort war irgendwie konstruktiver als die von schmidt-webdesign.net.
Ich werd's testen, sobald ich heute Zeit dafür habe.
Gruß Matze
-
Martin Richter schrieb:
IMHO must du folgendes machen:
// pd is my PRINTDLG LPDEVMODE pDevMode = (LPDEVMODE)::GlobalLock( pd.hDevMode); if (pDevMode->dmFields & DM_ORIENTATION) pDevMode->dmOrientation = DMORIENT_LANDSCAPE; GlobalUnlock( pd.hDevMode);das klappt wunderbar, danke Martin.
Noch ein Hinweis:
Nicht vergessen, die Änderung noch den Drucker Bescheid zusagen.Ich habe es hiermit realisiert.
ResetDC(pd.hDC,pDevMode);