cbruce Posted July 24, 2017 Share Posted July 24, 2017 I would like to place my PrintWindow() screenshot code in a function (like below). I don't want to create "dirty" code that allocates WinAPI resources inside the function and then requires me to clean up those resources outside the function. But I can't figure out how to pass a handle for my screenshot bitmap back to the calling routine without needing to clean up the WinAPI bitmap resources in the calling routine itself. expandcollapse popupFunc Test_ScreenShot_PrintWindowAPI( $hWnd) Local $winWidth Local $winHeight $winWidth = _WinAPI_GetWindowWidth($hWnd) $winHeight = _WinAPI_GetWindowHeight($hWnd) ; Local $hDC_Window ; The Device Context (DC) of the Window we want a screenshot of. Local $memDC ; A virtual DC to hold the Window image. Local $memBMP_original ; New DC's default 1x1 monochrome bitmap object. Local $hBMP_WinAPI ; WinAPI version of a bitmap. Local $bResult ; $hDC_Window = _WinAPI_GetDC( $hWnd) ; Get the DC for our Window. ; ; Create a blank MemDC just like our Window's DC. $memDC = _WinAPI_CreateCompatibleDC( $hDC_Window) ; Create a blank Bitmap of same size/shape as our Window. $hBMP_WinAPI = _WinAPI_CreateCompatibleBitmap( $hDC_Window, $winWidth, $winHeight) ; Put the blank Bitmap into the blank MemDC. $memBMP_original = _WinAPI_SelectObject( $memDC, $hBMP_WinAPI) ; $bResult = _WinAPI_PrintWindow( $hWnd, $memDC) ; CAPTURE WINDOW IMAGE. ; ; A bitmap image of the Window is now in [ $hBMP_WinAPI ] which is an object ; that belongs to the [ $memDC ] device context. ; ; Cleanup WinAPI resources ... ; ------------------------ ; Delete the WinAPI bitmap. _WinAPI_DeleteObject( $hBMP_WinAPI) ; Restore [ $memDC ]'s original bitmap object before deleting [ $memDC ]. _WinAPI_SelectObject( $memDC, $memBMP_original) ; Now delete [ $memDC ] - and the child bitmap resources will be deleted also. _WinAPI_DeleteDC( $memDC) ; Release the device context that we copied from the target Window. _WinAPI_ReleaseDC( $hWnd, $hDC_Window) ; ; QUESTION: ; Is it possible to pass a handle for my screenshot bitmap back to the calling routine ; without needing to move the... ; ; _WinAPI_DeleteObject( $hBMP_WinAPI) ; ; ...resource cleanup step outside this function? ; ;;; Return(What???) ... I just deleted my WinAPI screenshot bitmap in the resource cleanup! EndFunc Link to comment Share on other sites More sharing options...
InunoTaishou Posted July 24, 2017 Share Posted July 24, 2017 (edited) Did something like this a while back when I was playing with GDI+ expandcollapse popupFunc CaptureWindow($hWnd = WinGetHandle("[Active]"), $sFileName = "", $iLeft = -1, $iTop = -1, $iWidth = -1, $iHeight = -1, $bClientArea = True) If (Not IsHWnd($hWnd)) Then $hWnd = WinGetHandle($hWnd) If (@error) Then Return SetError(1, 0, False) If (BitAND(WinGetState($hWnd), 16) = 16) Then Return SetError(2, 0, False) Local $iSrcWidth = 0 Local $iSrcHeight = 0 If ($hWnd = _WinAPI_GetDesktopWindow() Or $hWnd = WinGetHandle("Program Manager")) Then Local $tDesktop = GetDesktopMetrics() If ($iWidth = -1 Or $iWidth = 0 Or $iWidth = Default Or $iWidth > DllStructGetData($tDesktop, 3)) Then $iWidth = DllStructGetData($tDesktop, 3) If ($iHeight = -1 Or $iHeight = 0 Or $iHeight = Default Or $iWidth > DllStructGetData($tDesktop, 4)) Then $iHeight = DllStructGetData($tDesktop, 4) $iSrcWidth = DllStructGetData($tDesktop, 3) $iSrcHeight = DllStructGetData($tDesktop, 4) Else Local $tRectWindow = _WinAPI_GetWindowRect($hWnd) ; Get the absolute width, using Abs on all of the memembers because the [1] index of the struct may be negative, supports multiple monitors Local $iAbsWidth = Abs(Abs(DllStructGetData($tRectWindow, 3)) - Abs(DllStructGetData($tRectWindow, 1))) ; Get the absolute height, using Abs on all of the memembers because the [1] index of the struct may be negative, supports multiple monitors ; Subtracts the caption bar if $bClientArea only Local $iAbsHeight = Abs(Abs(DllStructGetData($tRectWindow, 4)) - Abs(DllStructGetData($tRectWindow, 2))) - ($bClientArea ? _WinAPI_GetSystemMetrics($SM_CYCAPTION) : 0) If ($iWidth = -1 Or $iWidth = 0 Or $iWidth = Default Or $iWidth > $iAbsWidth) Then $iWidth = $iAbsWidth If ($iHeight = -1 Or $iHeight = 0 Or $iHeight = Default Or $iHeight > $iAbsHeight) Then $iHeight = $iAbsHeight $iSrcWidth = $iAbsWidth $iSrcHeight = $iAbsHeight EndIf If ($iLeft = -1 Or $iLeft = Default) Then $iLeft = 0 If ($iTop = -1 Or $iTop = Default) Then $iTop = 0 Local $hDC = _WinAPI_GetWindowDC($hWnd) Local $hDestDC = _WinAPI_CreateCompatibleDC($hDC) Local $hDestBitmap = _WinAPI_CreateCompatibleBitmap($hDC, $iWidth, $iHeight) Local $hDestSv = _WinAPI_SelectObject($hDestDC, $hDestBitmap) Local $hSrcDC = _WinAPI_CreateCompatibleDC($hDC) Local $hBmp = _WinAPI_CreateCompatibleBitmap($hDC, $iSrcWidth, $iSrcHeight) Local $hSrcSv = _WinAPI_SelectObject($hSrcDC, $hBmp) _WinAPI_PrintWindow($hWnd, $hSrcDC, True) _WinAPI_BitBlt($hDestDC, 0, 0, $iWidth, $iHeight, $hSrcDC, $iLeft, $iTop, $MERGECOPY) _WinAPI_SelectObject($hDestDC, $hDestSv) _WinAPI_SelectObject($hSrcDC, $hSrcSv) _WinAPI_ReleaseDC($hWnd, $hDC) _WinAPI_DeleteDC($hDestDC) _WinAPI_DeleteDC($hSrcDC) _WinAPI_DeleteObject($hBmp) $tPoint = 0 $tRectWindow = 0 $tDesktop = 0 If ($sFileName) Then _GDIPlus_Startup() Local $hBitmap = _GDIPlus_BitmapCreateFromHBITMAP($hDestBitmap) _GDIPlus_ImageSaveToFile($hBitmap, $sFileName) _GDIPlus_BitmapDispose($hBitmap) _GDIPlus_Shutdown() EndIf Return $hDestBitmap EndFunc ;==>CaptureWindow Func GetDesktopMetrics() Return _GDIPlus_RectFCreate(_WinAPI_GetSystemMetrics($SM_XVIRTUALSCREEN), _WinAPI_GetSystemMetrics($SM_YVIRTUALSCREEN), _ _WinAPI_GetSystemMetrics($SM_CXVIRTUALSCREEN), _WinAPI_GetSystemMetrics($SM_CYVIRTUALSCREEN)) EndFunc ;==>GetDesktopMetrics Will return a windows GDI bitmap Example: expandcollapse popup#include <GUIConstants.au3> #include <ScreenCapture.au3> #include <WinAPI.au3> #include <GDIPlus.au3> ;~ #RequireAdmin AutoItSetOption("WinTitleMatchMode", -2) Global $hMain = GUICreate("GDI+ example", 1280, 810, -1, -1, BitOR($WS_SIZEBOX, $WS_MINIMIZEBOX, $WS_MAXIMIZEBOX)) Global $btnCapture = GUICtrlCreateButton("Capture", 10, 10, 100, 25) Global $inpWindow = GUICtrlCreateInput("Program Manager", 125, 10, 200, 25) Global $picCapture = GUICtrlCreatePic("", 0, 40, 1280, 768) GUICtrlSetResizing($picCapture, $GUI_DOCKALL) GUISetState(@SW_SHOW) While (True) Switch (GUIGetMsg()) Case $GUI_EVENT_CLOSE Exit 0 Case $btnCapture Local $hHBitmap = CaptureWindow(GUICtrlRead($inpWindow)) If (Not @Error) Then _WinAPI_DeleteObject(GUICtrlSendMsg($picCapture, $STM_SETIMAGE, $IMAGE_BITMAP, $hHBitmap)) _ScreenCapture_SaveImage(@ScriptDir & "\" & $inpWindow & ".png", $hHBitmap) _WinAPI_DeleteObject($hHBitmap) EndIf EndSwitch WEnd Func CaptureWindow($hWnd = WinGetHandle("[Active]"), $sFileName = "", $iLeft = -1, $iTop = -1, $iWidth = -1, $iHeight = -1, $bClientArea = True) If (Not IsHWnd($hWnd)) Then $hWnd = WinGetHandle($hWnd) If (@error) Then Return SetError(1, 0, False) If (BitAND(WinGetState($hWnd), 16) = 16) Then Return SetError(2, 0, False) Local $iSrcWidth = 0 Local $iSrcHeight = 0 If ($hWnd = _WinAPI_GetDesktopWindow() Or $hWnd = WinGetHandle("Program Manager")) Then Local $tDesktop = GetDesktopMetrics() If ($iWidth = -1 Or $iWidth = 0 Or $iWidth = Default Or $iWidth > DllStructGetData($tDesktop, 3)) Then $iWidth = DllStructGetData($tDesktop, 3) If ($iHeight = -1 Or $iHeight = 0 Or $iHeight = Default Or $iWidth > DllStructGetData($tDesktop, 4)) Then $iHeight = DllStructGetData($tDesktop, 4) $iSrcWidth = DllStructGetData($tDesktop, 3) $iSrcHeight = DllStructGetData($tDesktop, 4) Else Local $tRectWindow = _WinAPI_GetWindowRect($hWnd) ; Get the absolute width, using Abs on all of the memembers because the [1] index of the struct may be negative, supports multiple monitors Local $iAbsWidth = Abs(Abs(DllStructGetData($tRectWindow, 3)) - Abs(DllStructGetData($tRectWindow, 1))) ; Get the absolute height, using Abs on all of the memembers because the [1] index of the struct may be negative, supports multiple monitors ; Subtracts the caption bar if $bClientArea only Local $iAbsHeight = Abs(Abs(DllStructGetData($tRectWindow, 4)) - Abs(DllStructGetData($tRectWindow, 2))) - ($bClientArea ? _WinAPI_GetSystemMetrics($SM_CYCAPTION) : 0) If ($iWidth = -1 Or $iWidth = 0 Or $iWidth = Default Or $iWidth > $iAbsWidth) Then $iWidth = $iAbsWidth If ($iHeight = -1 Or $iHeight = 0 Or $iHeight = Default Or $iHeight > $iAbsHeight) Then $iHeight = $iAbsHeight $iSrcWidth = $iAbsWidth $iSrcHeight = $iAbsHeight EndIf If ($iLeft = -1 Or $iLeft = Default) Then $iLeft = 0 If ($iTop = -1 Or $iTop = Default) Then $iTop = 0 Local $hDC = _WinAPI_GetWindowDC($hWnd) Local $hDestDC = _WinAPI_CreateCompatibleDC($hDC) Local $hDestBitmap = _WinAPI_CreateCompatibleBitmap($hDC, $iWidth, $iHeight) Local $hDestSv = _WinAPI_SelectObject($hDestDC, $hDestBitmap) Local $hSrcDC = _WinAPI_CreateCompatibleDC($hDC) Local $hBmp = _WinAPI_CreateCompatibleBitmap($hDC, $iSrcWidth, $iSrcHeight) Local $hSrcSv = _WinAPI_SelectObject($hSrcDC, $hBmp) _WinAPI_PrintWindow($hWnd, $hSrcDC, True) _WinAPI_BitBlt($hDestDC, 0, 0, $iWidth, $iHeight, $hSrcDC, $iLeft, $iTop, $MERGECOPY) _WinAPI_SelectObject($hDestDC, $hDestSv) _WinAPI_SelectObject($hSrcDC, $hSrcSv) _WinAPI_ReleaseDC($hWnd, $hDC) _WinAPI_DeleteDC($hDestDC) _WinAPI_DeleteDC($hSrcDC) _WinAPI_DeleteObject($hBmp) $tPoint = 0 $tRectWindow = 0 $tDesktop = 0 If ($sFileName) Then _GDIPlus_Startup() Local $hBitmap = _GDIPlus_BitmapCreateFromHBITMAP($hDestBitmap) _GDIPlus_ImageSaveToFile($hBitmap, $sFileName) _GDIPlus_BitmapDispose($hBitmap) _GDIPlus_Shutdown() EndIf Return $hDestBitmap EndFunc ;==>CaptureWindow Func GetDesktopMetrics() Return _GDIPlus_RectFCreate(_WinAPI_GetSystemMetrics($SM_XVIRTUALSCREEN), _WinAPI_GetSystemMetrics($SM_YVIRTUALSCREEN), _ _WinAPI_GetSystemMetrics($SM_CXVIRTUALSCREEN), _WinAPI_GetSystemMetrics($SM_CYVIRTUALSCREEN)) EndFunc ;==>GetDesktopMetrics Edited July 24, 2017 by InunoTaishou Link to comment Share on other sites More sharing options...
UEZ Posted July 24, 2017 Share Posted July 24, 2017 @cbruce if you dispose any bitmap handle than you cannot access it anymore. That means you cannot delete here the bitmap handle and return it afterwards within the function. Thus you have to dispose it outside the function. I didn't test your script but in general you have to comment out / delete the line _WinAPI_DeleteObject( $hBMP_WinAPI) and add ;;; Return(What???) ... I just deleted my WinAPI screenshot bitmap in the resource cleanup! Return $hBMP_WinAPI EndFunc to the end of the function. But be informed that this is a GDI bitmap handle which cannot be accessed directly by GDIPlus (GDI+)! Please don't send me any personal message and ask for support! I will not reply! Selection of finest graphical examples at Codepen.io The own fart smells best! ✌Her 'sikim hıyar' diyene bir avuç tuz alıp koşma!¯\_(ツ)_/¯ ٩(●̮̮̃•̃)۶ ٩(-̮̮̃-̃)۶ૐ Link to comment Share on other sites More sharing options...
cbruce Posted July 24, 2017 Author Share Posted July 24, 2017 Is there a local AutoIt variable or data structure that would hold a bitmap? Such would allow me to make an "AutoIt" copy of the bitmap before disposing of the WinAPI allocated bitmap. I want to handle several bitmaps - passing them to GDIPlus routines... $hBMP_GDIPlus = _GDIPlus_BitmapCreateFromHBITMAP( $hBMP_WinAPI) ..., etc. So saving the bitmaps to files, and then reading them back in, is not really an option that I'm looking for. I know that I can save a copy to the clipboard, but that seems like a kluge that is just waiting to break. Also, it seems that if the clipboard can do it, then my script should be able to also. Anything along that line that is available? Link to comment Share on other sites More sharing options...
UEZ Posted July 24, 2017 Share Posted July 24, 2017 After you have converted the GDI (WinAPI) bitmap to a GDI+ bitmap, just release the GDI bitmap handle using _WinAPI_DeleteObject. After conversation you have already the bitmap in memory, thus you don't need to save it to disk and read it back but don't forget release the GDI+ bitmap when it is not needed anymore. Please don't send me any personal message and ask for support! I will not reply! Selection of finest graphical examples at Codepen.io The own fart smells best! ✌Her 'sikim hıyar' diyene bir avuç tuz alıp koşma!¯\_(ツ)_/¯ ٩(●̮̮̃•̃)۶ ٩(-̮̮̃-̃)۶ૐ Link to comment Share on other sites More sharing options...
cbruce Posted July 24, 2017 Author Share Posted July 24, 2017 Thanks, UEZ and InunoTaishou! I was hoping to keep any bitmap resource allocations/releases in one place, in order to have everything clean and maintainable. But I'll just have to keep track of everything like a good little coder should. [grin] Link to comment Share on other sites More sharing options...
Recommended Posts
Create an account or sign in to comment
You need to be a member in order to leave a comment
Create an account
Sign up for a new account in our community. It's easy!
Register a new accountSign in
Already have an account? Sign in here.
Sign In Now