Sign in to follow this  
Followers 0
cbruce

WinAPI resource cleanup - return bitmap from function

6 posts in this topic

#1 ·  Posted

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.

 

Func 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






 

Share this post


Link to post
Share on other sites



#2 ·  Posted (edited)

Did something like this a while back when I was playing with GDI+

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

Will return a windows GDI bitmap

Example:

#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 by InunoTaishou

Share this post


Link to post
Share on other sites

#3 ·  Posted

@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!
¯\_(ツ)_/¯

Share this post


Link to post
Share on other sites

#4 ·  Posted

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?

Share this post


Link to post
Share on other sites

#5 ·  Posted

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!
¯\_(ツ)_/¯

Share this post


Link to post
Share on other sites

#6 ·  Posted

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]

 

Share this post


Link to post
Share on other sites

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 account

Sign in

Already have an account? Sign in here.


Sign In Now
Sign in to follow this  
Followers 0