![]() |
Delphi-Version: 10 Seattle
Wie mache ich einen screenshot in Z-Order mit PrintWindow-API?
Ich versuche, einen C-Code zu übersetzen, der Screenshots in Z-Reihenfolge aufnehmen kann. Wenn ich den Delphi-Code teste, wird nur das Desktopfenster gedruckt. Wie kann das behoben werden?
C code:
Code:
#include "stdafx.h"
#include <Windows.h> #include <gdiplus.h> #include <conio.h> #pragma warning(disable : 4996) #pragma comment (lib,"Gdiplus.lib") using namespace Gdiplus; int GetEncoderClsid(const WCHAR* format, CLSID* pClsid) { UINT num = 0; UINT size = 0; ImageCodecInfo* pImageCodecInfo = NULL; GetImageEncodersSize(&num, &size); if (size == 0) return -1; pImageCodecInfo = (ImageCodecInfo*)(malloc(size)); if (pImageCodecInfo == NULL) return -1; GetImageEncoders(num, size, pImageCodecInfo); for (UINT j = 0; j < num; ++j) { if (wcscmp(pImageCodecInfo[j].MimeType, format) == 0) { *pClsid = pImageCodecInfo[j].Clsid; free(pImageCodecInfo); return j; } } free(pImageCodecInfo); return -1; } //======================================================================================================== BOOL xPrintWindow(HWND hWnd, HDC hDc, HDC hDcScreen) { BOOL ret = FALSE; RECT rect; GetWindowRect(hWnd, &rect); HDC hDcWindow = CreateCompatibleDC(hDc); HBITMAP hBmpWindow = CreateCompatibleBitmap(hDc, rect.right - rect.left, rect.bottom - rect.top); SelectObject(hDcWindow, hBmpWindow); if (PrintWindow(hWnd, hDcWindow, 0)) { BitBlt(hDcScreen, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, hDcWindow, 0, 0, SRCCOPY); ret = TRUE; } DeleteObject(hBmpWindow); DeleteDC(hDcWindow); return ret; } void EnumWindowsTopToDown(HWND owner, WNDENUMPROC proc, LPARAM param) { HWND currentWindow = GetTopWindow(owner); if (currentWindow == NULL) return; if ((currentWindow = GetWindow(currentWindow, GW_HWNDLAST)) == NULL) return; while (proc(currentWindow, param) && (currentWindow = GetWindow(currentWindow, GW_HWNDPREV)) != NULL); } struct EnumHwndsPrintData { HDC hDc; HDC hDcScreen; }; BOOL CALLBACK EnumHwndsPrint(HWND hWnd, LPARAM lParam) { EnumHwndsPrintData *data = (EnumHwndsPrintData *)lParam; if (!IsWindowVisible(hWnd)) return TRUE; xPrintWindow(hWnd, data->hDc, data->hDcScreen); DWORD style = GetWindowLongA(hWnd, GWL_EXSTYLE); SetWindowLongA(hWnd, GWL_EXSTYLE, style | WS_EX_COMPOSITED); /*OSVERSIONINFOA versionInfo; ZeroMemory(&versionInfo, sizeof(OSVERSIONINFO)); versionInfo.dwOSVersionInfoSize = sizeof(versionInfo); GetVersionExA(&versionInfo); if (versionInfo.dwMajorVersion < 6) EnumWindowsTopToDown(hWnd, EnumHwndsPrint, (LPARAM)data);*/ return TRUE; } void testPrintWindow(int serverWidth, int serverHeight) { RECT rect; HWND hWndDesktop = GetDesktopWindow(); GetWindowRect(hWndDesktop, &rect); HDC hDc = GetDC(NULL); HDC hDcScreen = CreateCompatibleDC(hDc); HBITMAP hBmpScreen = CreateCompatibleBitmap(hDc, rect.right, rect.bottom); SelectObject(hDcScreen, hBmpScreen); EnumHwndsPrintData data; data.hDc = hDc; data.hDcScreen = hDcScreen; EnumWindowsTopToDown(NULL, EnumHwndsPrint, (LPARAM)&data); if (serverWidth > rect.right) serverWidth = rect.right; if (serverHeight > rect.bottom) serverHeight = rect.bottom; if (serverWidth != rect.right || serverHeight != rect.bottom) { HBITMAP hBmpScreenResized = CreateCompatibleBitmap(hDc, serverWidth, serverHeight); HDC hDcScreenResized = CreateCompatibleDC(hDc); SelectObject(hDcScreenResized, hBmpScreenResized); SetStretchBltMode(hDcScreenResized, HALFTONE); StretchBlt(hDcScreenResized, 0, 0, serverWidth, serverHeight, hDcScreen, 0, 0, rect.right, rect.bottom, SRCCOPY); DeleteObject(hBmpScreen); DeleteDC(hDcScreen); hBmpScreen = hBmpScreenResized; hDcScreen = hDcScreenResized; } //======================================================================================================= // ========== HBITMAP to bmp file =========== GdiplusStartupInput gdiplusStartupInput; ULONG_PTR gdiplusToken; GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL); Bitmap *image = new Bitmap(hBmpScreen, NULL); CLSID myClsId; int retVal = GetEncoderClsid(L"image/bmp", &myClsId); image->Save(L"output.bmp", &myClsId, NULL); delete image; GdiplusShutdown(gdiplusToken); //======================================================================= } int _tmain(int argc, _TCHAR* argv[]) { testPrintWindow(800, 600); _getch(); return 0; }
Delphi-Quellcode:
program Project1;
{$APPTYPE CONSOLE} {$R *.res} uses Windows, Vcl.Graphics, SysUtils; type PEnumHwndsPrintData = ^TEnumHwndsPrintData; TEnumHwndsPrintData = record _hDc: HDC; hDcScreen: HDC; end; type TFNWndEnumProc = function(_hwnd: HWND; _lParam: LPARAM): BOOL; stdcall; function xPrintWindow(_hwnd: HWND; _hDc, hDcScreen: HDC): BOOL; const sPrintWindow = 'PrintWindow'; var PrintWindowAPI: function(sourceHandle: HWND; destinationHandle: HDC; nFlags: UINT): BOOL; stdcall; User32DLLHandle: THandle; bPrint: Boolean; Ret: BOOL; R: TRect; hDcWindow: HDC; hBmpWindow: HBITMAP; begin Ret := False; User32DLLHandle := GetModuleHandle(user32); if User32DLLHandle <> 0 then begin @PrintWindowAPI := GetProcAddress(User32DLLHandle, sPrintWindow); if @PrintWindowAPI <> nil then begin GetWindowRect(_hwnd, R); hDcWindow := CreateCompatibleDC(_hDc); hBmpWindow := CreateCompatibleBitmap(_hDc, R.Right - R.Left, R.Bottom - R.Top); SelectObject(hDcWindow, hBmpWindow); bPrint := PrintWindowAPI(_hwnd, hDcWindow, 0); if bPrint then begin BitBlt(hDcScreen, R.Left, R.Top, R.Right - R.Left, R.Bottom - R.Top, hDcWindow, 0, 0, SRCCOPY); Ret := True; end; DeleteObject(hBmpWindow); DeleteDC(hDcWindow); end; end; Result := Ret; end; function GetPrevHwnd(hWindow: HWND): HWND; begin hWindow := GetWindow(hWindow, GW_HWNDPREV); Result := hWindow; end; procedure EnumWindowsTopToDown(Owner: HWND; Proc: TFNWndEnumProc; _Param: LPARAM); var CurrentWindow, _CurrentWindow: HWND; begin repeat CurrentWindow := GetTopWindow(Owner); if CurrentWindow = 0 then Exit; CurrentWindow := GetWindow(CurrentWindow, GW_HWNDLAST); if CurrentWindow = 0 then Exit; _CurrentWindow := GetPrevHwnd(CurrentWindow); until Proc(CurrentWindow, _Param) and (_CurrentWindow <> 0); end; function EnumHwndsPrint(wHandle: HWND; _lParam: LPARAM): BOOL; stdcall; var VersionInfo: TOSVersionInfo; Data: PEnumHwndsPrintData; Style: DWORD; begin Result := True; if not IsWindowVisible(wHandle) then Exit; Data := PEnumHwndsPrintData(_lParam); Writeln('oi'); xPrintWindow(wHandle, Data._hDc, Data.hDcScreen); Style := GetWindowLong(wHandle, GWL_EXSTYLE); SetWindowLong(wHandle, GWL_EXSTYLE, Style or WS_EX_COMPOSITED); VersionInfo.dwOSVersionInfoSize := SizeOf(VersionInfo); GetVersionEx(VersionInfo); if (VersionInfo.dwMajorVersion < 6) then EnumWindowsTopToDown(wHandle, EnumHwndsPrint, LPARAM(Data)); end; procedure testPrintWindow(Width, Height: Integer); var hWndDesktop: HWND; Data: TEnumHwndsPrintData; _hDcScreen, hDc_, hDcScreenResized: HDC; hBmpScreen, hBmpScreenResized: HBITMAP; Rect: TRect; bmp: TBitmap; begin hWndDesktop := GetDesktopWindow; GetWindowRect(hWndDesktop, Rect); hDc_ := GetDC(0); _hDcScreen := CreateCompatibleDC(hDc_); hBmpScreen := CreateCompatibleBitmap(hDc_, Rect.Right, Rect.Bottom); SelectObject(_hDcScreen, hBmpScreen); with Data do begin _hDc := hDc_; hDcScreen := _hDcScreen; end; EnumWindowsTopToDown(0, EnumHwndsPrint, LPARAM(@Data)); if (Width > Rect.Right) then Width := Rect.Right; if (Height > Rect.Bottom) then Height := Rect.Bottom; if (Width <> Rect.Right) or (Height <> Rect.Bottom) then begin hBmpScreenResized := CreateCompatibleBitmap(hDc_, Width, Height); hDcScreenResized := CreateCompatibleDC(hDc_); SelectObject(hDcScreenResized, hBmpScreenResized); SetStretchBltMode(hDcScreenResized, HALFTONE); StretchBlt(hDcScreenResized, 0, 0, Width, Height, _hDcScreen, 0, 0, Rect.Right, Rect.Bottom, SRCCOPY); DeleteObject(hBmpScreen); DeleteDC(_hDcScreen); hBmpScreen := hBmpScreenResized; _hDcScreen := hDcScreenResized; end; bmp := TBitmap.Create; bmp.Handle := hBmpScreen; bmp.SaveToFile('output.bmp'); bmp.Free; end; begin try testPrintWindow(800, 600); except on E: Exception do Writeln(E.ClassName, ': ', E.Message); end; Readln; end. |
AW: Wie mache ich einen screenshot in Z-Order mit PrintWindow-API?
na ja..
Wenn du das Handle des Desktop verwendest was denkst du dann was du als ScreenShot bekommst. Überlege dir das mal.. Richtig! Einen ScreenShot des Desktop nix anderes. gruss |
AW: Wie mache ich einen screenshot in Z-Order mit PrintWindow-API?
Zitat:
Delphi-Quellcode:
Dann denke ich, dass mein Problem hier ist, aber ich weiß nicht, wie ich es lösen soll.
function GetPrevHwnd(hWindow: HWND): HWND;
begin hWindow := GetWindow(hWindow, GW_HWNDPREV); Result := hWindow; end; procedure EnumWindowsTopToDown(Owner: HWND; Proc: TFNWndEnumProc; _Param: LPARAM); var CurrentWindow, _CurrentWindow: HWND; begin repeat CurrentWindow := GetTopWindow(Owner); if CurrentWindow = 0 then Exit; CurrentWindow := GetWindow(CurrentWindow, GW_HWNDLAST); if CurrentWindow = 0 then Exit; _CurrentWindow := GetPrevHwnd(CurrentWindow); until Proc(CurrentWindow, _Param) and (_CurrentWindow <> 0); end; |
AW: Wie mache ich einen screenshot in Z-Order mit PrintWindow-API?
Zitat:
Meine Vermutung ist das dein Problem die Funktion GetTopWindow ist die ist nicht zuverlässig. Das hier ist aber schon mal unnötig auch wenn es jetzt direkt nichts mit dem Problem zu tun hat.
Delphi-Quellcode:
du löschst das _hDcScreen
DeleteObject(hBmpScreen);
DeleteDC(_hDcScreen); hBmpScreen := hBmpScreenResized; _hDcScreen := hDcScreenResized; end; bmp := TBitmap.Create; bmp.Handle := hBmpScreen; bmp.SaveToFile('output.bmp'); bmp.Free; weist diesen ein neues zu und zwar das des hDcScreenResized Und anschließend wird das _hDcScreen nicht mehr verwendet. Das sieht mir sehr nach einem Speicherleck aus.. Google mal nach GetTopWindow gruss |
AW: Wie mache ich einen screenshot in Z-Order mit PrintWindow-API?
Zitat:
![]() Da war noch ein Frage, aber die wurde wohl gelöscht. |
AW: Wie mache ich einen screenshot in Z-Order mit PrintWindow-API?
Zitat:
Nebenbei auch deine Lösung funktioniert nicht, anschließend kannst du dein System neu starten. Durch den Code zerstört sich der Explorer der IE geht nicht mehr, die Gadges laufen mit einer Auslastung von 50% usw.. Ich kann nur davon abraten. gruss |
AW: Wie mache ich einen screenshot in Z-Order mit PrintWindow-API?
Zitat:
Meine Version. Keine Ahnung ob die macht, was du möchtest. Ich weiß nämlich nicht genau, was du möchtest.
Delphi-Quellcode:
program PrintInZOrder;
{$APPTYPE CONSOLE} {$R *.res} uses System.SysUtils, Winapi.Windows, Vcl.Graphics; function PrintWindow(hwnd: HWND; hdcBlt: HDC; nFlags: UInt32): BOOL; stdcall; external 'user32.dll' name 'PrintWindow'; function xPrintWindow(hWnd: HWND; hdc, hdcScreen: HDC): Boolean; var ret: Boolean; rect: TRect; hdcWindow: Winapi.Windows.HDC; hbmpWindow: HBITMAP; begin ret := False; GetWindowRect(hWnd, rect); hdcWindow := CreateCompatibleDC(hDC); hbmpWindow := CreateCompatibleBitmap(hDC, rect.Width, rect.Height); SelectObject(hdcWindow, hbmpWindow); if PrintWindow(hWnd, hdcWindow, 0) then begin BitBlt(hdcScreen, rect.Left, rect.Top, rect.Width, rect.Height, hdcWindow, 0, 0, SRCCOPY); ret := True; end; DeleteObject(hbmpWindow); DeleteDC(hdcWindow); Result := ret; end; // https://stackoverflow.com/a/55885143/95954 type WNDENUMPROC = function(hwnd: HWND; lParam: LPARAM): BOOL stdcall; procedure EnumWindowsTopToDown(owner: HWND; proc: WNDENUMPROC; param: LPARAM); var currentWindow: HWND; begin currentWindow := GetTopWindow(owner); if currentWindow = 0 then Exit; currentWindow := GetWindow(currentWindow, GW_HWNDLAST); while (currentWindow <> 0) and proc(currentWindow, param) do currentWindow := GetWindow(currentWindow, GW_HWNDPREV); end; type PEnumHwndsPrintData = ^TEnumHwndsPrintData; TEnumHwndsPrintData = record hdc, hdcScreen: Winapi.Windows.HDC; end; function EnumHwndsPrint(hwnd: HWND; lParam: LPARAM): BOOL; stdcall; var data: PEnumHwndsPrintData; style: Integer; begin data := PEnumHwndsPrintData(LPARAM); if not IsWindowVisible(hwnd) then Exit(True); xPrintWindow(hwnd, data^.hdc, data^.hdcScreen); // Folgende Zeilen machen die IDE total banane (Endlos-Repaint-Schleife), und sind wohl nicht nötig. Wenn doch, dann nur ohne IDE starten. // style := GetWindowLongA(hwnd, GWL_EXSTYLE); // SetWindowLongA(hwnd, GWL_EXSTYLE, style or WS_EX_COMPOSITED); Result := True; end; procedure testPrintWindow(serverWidth, serverHeight: Integer); var rect: TRect; hwndDesktop: HWND; hdc, hdcScreen: Winapi.Windows.HDC; hbmpScreen: HBITMAP; data: TEnumHwndsPrintData; hbmpScreenResized: HBITMAP; hdcScreenResized: Winapi.Windows.HDC; image: TBitmap; begin hwndDesktop := GetDesktopWindow; GetWindowRect(hwndDesktop, rect); hdc := GetDC(0); hdcScreen := CreateCompatibleDC(hdc); hbmpScreen := CreateCompatibleBitmap(hdc, rect.Right, rect.Bottom); SelectObject(hdcScreen, hbmpScreen); data.hdc := hdc; data.hdcScreen := hdcScreen; EnumWindowsTopToDown(0, EnumHwndsPrint, Winapi.Windows.LPARAM(@data)); if serverWidth > rect.Right then serverWidth := rect.Right; if serverHeight > rect.Bottom then serverHeight := rect.Bottom; if (serverWidth <> rect.Right) or (serverHeight <> rect.Bottom) then begin // Diesen Block kann man wahrscheinlich viel einfacher direkt mit einer Vcl.Graphics.TBitmap ausführen. hbmpScreenResized := CreateCompatibleBitmap(hdc, serverWidth, serverHeight); hdcScreenResized := CreateCompatibleDC(hdc); SelectObject(hdcScreenResized, hbmpScreenResized); SetStretchBltMode(hdcScreenResized, HALFTONE); StretchBlt(hdcScreenResized, 0, 0, serverWidth, serverHeight, hdcScreen, 0, 0, rect.Right, rect.Bottom, SRCCOPY); DeleteObject(hbmpScreen); DeleteDC(hdcScreen); hbmpScreen := hbmpScreenResized; hdcScreen := hdcScreenResized; end; image := TBitmap.Create; try image.Handle := hbmpScreen; image.SaveToFile('output.bmp'); finally image.Free; end; // Nicht im Original, aber hier notwendig. DeleteDC(hdcScreen); end; procedure Main; begin testPrintWindow(800, 600); Writeln('Gespeichert, bitte Enter-Taste drücken...'); end; begin try Main; except on E: Exception do Writeln(E.ClassName, ': ', E.Message); end; Readln; end. |
AW: Wie mache ich einen screenshot in Z-Order mit PrintWindow-API?
Delphi-Quellcode:
hdcScreen := hdcScreenResized;
den gleichen Fehler übernommen den ich schon gemeldet habe. Zitat:
Und wenn schon dann SetWindowLongPtrA.. gruss |
AW: Wie mache ich einen screenshot in Z-Order mit PrintWindow-API?
Zitat:
Delphi-Quellcode:
nicht mehr gebraucht wird. Das Leck ist dowieso da, denn auch wenn da nicht gestaucht werden soll wird hdcScreen nicht mit DeleteObject gelöscht. Ich weiß auch, dass man den ganzen Block wohl besser direkt mit einer
hdcScreen
Delphi-Quellcode:
macht. Aber Visual C++ kennt keine Delphi-TBitmaps. <g>
TBitmap
Das
Delphi-Quellcode:
Problem tritt bei mir nur in der IDE auf, sonst nicht.
SetWindowLongA(...EX_STYLE...)
|
AW: Wie mache ich einen screenshot in Z-Order mit PrintWindow-API?
Zitat:
Nur ein Neustart korrigiert das wieder. Zitat:
gruss |
Alle Zeitangaben in WEZ +1. Es ist jetzt 05:20 Uhr. |
Powered by vBulletin® Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024-2025 by Thomas Breitkreuz