GDI_CaptureScreen to Clipboard

Get help with using AutoHotkey (v1.1 and older) and its commands and hotkeys
GEOVAN
Posts: 178
Joined: 03 Mar 2022, 11:12

GDI_CaptureScreen to Clipboard

01 May 2024, 09:51

Hello,
I have the following code which simply do the following:
By Pressing F2, it copy FullScreen Screenshot to my Clipboard.
The problem i have is the following:

I want to SKIP the save to file at A_ScriptDir . "\Screenshot.bmp"
I want to just "take" the GDI_CaptureScreen( 0, 0, A_ScreenWidth, A_ScreenHeight ) and "copy" it to Clipboard.
I spend many hours, but i can not find a way to do it.
I will appreciate your help please. (Please note that for educational purposes, i want to use the following code - not other methods) - Thanks!

Code: Select all

F2::        ; get FullScreen Screenshot and save it to file and load it to clipboard


Clipboard := 	; Clear the clipboard
DllCall( "DeleteObject", UInt,hBM )
hBM := ""
hBM := GDI_CaptureScreen( 0, 0, A_ScreenWidth, A_ScreenHeight )   

GDI_SaveBitmap( hBM, A_ScriptDir . "\Screenshot.bmp" )
ImageToClipboard( A_ScriptDir . "\Screenshot.bmp")

Return




;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

ImageToClipboard(Filename)
{
    hbm := DllCall("LoadImage","uint",0,"str",Filename,"uint",0,"int",0,"int",0,"uint",0x10)
    if !hbm
      return
    DllCall("OpenClipboard","uint",0)
    DllCall("EmptyClipboard")
    ; Place the data on the clipboard. CF_BITMAP=0x2
    if !DllCall("SetClipboardData","uint",0x2,"uint",hbm)
	  DllCall("DeleteObject","uint",hbm)
    DllCall("CloseClipboard")
}



GDI_CaptureScreen( X, Y, W, H, ByRef Checksum="" ) {
 tDC := DllCall( "CreateCompatibleDC", UInt,0 )
 hBM := DllCall( "CopyImage", UInt,DllCall( "CreateBitmap", Int,W, Int,H, UInt,1, UInt,24
                            , UInt,0 ), UInt,0, Int,0, Int,0, UInt,0x2008, UInt )
 oBM := DllCall( "SelectObject", UInt,tDC, UInt,hBM ), hDC := DllCall( "GetDC", UInt,0 )
 DllCall( "BitBlt"
     , UInt,tDC, UInt,0, UInt,0, Int,W, Int,H, UInt,hDC, UInt,X, UInt,Y, UInt,0x00CC0020 )
 DllCall( "ReleaseDC", UInt,0, UInt,hDC ),   DllCall( "SelectObject", UInt,tDC, UInt,oBM )
 If ( Checksum <> "" )
   VarSetCapacity( BM,24,0 ), DllCall( "GetObject", UInt,hBM, UInt,24, UInt,&BM )
 , DllCall( "shlwapi\HashData", UInt,NumGet(BM,20), UInt,NumGet( BM,12 )*NumGet( BM,8 )
                              , Int64P,Checksum, UInt,7 )
 Return hBM, DllCall( "DeleteDC", UInt,tDC )
}



GDI_SaveBitmap( hBM, File ) {
 DllCall( "GetObject", Int,hBM, Int,VarSetCapacity($,84), UInt,NumPut(0,$,40,"Short")-42 )
 Numput( VarSetCapacity(BFH,14,0)+40, Numput((NumGet($,44)+54),Numput(0x4D42,BFH)-2)+4 )
 If ( hF := DllCall( "CreateFile", Str,File,UInt,2**30,UInt,2,Int,0,UInt,2,Int64,0 ) ) > 0
   DllCall( "WriteFile", UInt,hF, UInt,&BFH,  UInt,14, IntP,0,Int,0 ) ; BITMAPFILEHEADER
 , DllCall( "WriteFile", UInt,hF, UInt,&$+24, UInt,40, IntP,0,Int,0 ) ; BITMAPINFOHEADER
 , DllCall( "WriteFile", UInt,hF, UInt,NumGet($,20), UInt,NumGet($,44), UIntP,BW, Int,0 )
 , DllCall( "CloseHandle", UInt,hF )
Return BW ? 54+BW : 0
}

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;


ShatterCoder
Posts: 84
Joined: 06 Oct 2016, 15:57

Re: GDI_CaptureScreen to Clipboard

01 May 2024, 17:42

I believe the following will work for you. I have not been able to test it yet so I apologize if it does not work. I just copied the function out of the Gdip.ahk lib ( Gdip_SetBitmapToClipboard() ). The only editing needed was to remove the first line of the function which converts from pBitmap to hBitmap as your code below starts out in hBitmap format.

Code: Select all

F2::        ; get FullScreen Screenshot and save it to file and load it to clipboard


Clipboard := 	; Clear the clipboard
DllCall( "DeleteObject", UInt,hBM )
hBM := ""
hBM := GDI_CaptureScreen( 0, 0, A_ScreenWidth, A_ScreenHeight )   

SetBitmapToClipboard(hBM)

Return




;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;


GDI_CaptureScreen( X, Y, W, H, ByRef Checksum="" ) {
 tDC := DllCall( "CreateCompatibleDC", UInt,0 )
 hBM := DllCall( "CopyImage", UInt,DllCall( "CreateBitmap", Int,W, Int,H, UInt,1, UInt,24
                            , UInt,0 ), UInt,0, Int,0, Int,0, UInt,0x2008, UInt )
 oBM := DllCall( "SelectObject", UInt,tDC, UInt,hBM ), hDC := DllCall( "GetDC", UInt,0 )
 DllCall( "BitBlt"
     , UInt,tDC, UInt,0, UInt,0, Int,W, Int,H, UInt,hDC, UInt,X, UInt,Y, UInt,0x00CC0020 )
 DllCall( "ReleaseDC", UInt,0, UInt,hDC ),   DllCall( "SelectObject", UInt,tDC, UInt,oBM )
 If ( Checksum <> "" )
   VarSetCapacity( BM,24,0 ), DllCall( "GetObject", UInt,hBM, UInt,24, UInt,&BM )
 , DllCall( "shlwapi\HashData", UInt,NumGet(BM,20), UInt,NumGet( BM,12 )*NumGet( BM,8 )
                              , Int64P,Checksum, UInt,7 )
 Return hBM, DllCall( "DeleteDC", UInt,tDC )
}


;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

SetBitmapToClipboard(hBitmap )
{
	DllCall("GetObject", "uint", hBitmap, "int", VarSetCapacity(oi, 84, 0), "uint", &oi)
	hdib := DllCall("GlobalAlloc", "uint", 2, "uint", 40+NumGet(oi, 44))
	pdib := DllCall("GlobalLock", "uint", hdib)
	DllCall("RtlMoveMemory", "uint", pdib, "uint", &oi+24, "uint", 40)
	DllCall("RtlMoveMemory", "Uint", pdib+40, "Uint", NumGet(oi, 20), "uint", NumGet(oi, 44))
	DllCall("GlobalUnlock", "uint", hdib)
	DllCall("DeleteObject", "uint", hBitmap)
	DllCall("OpenClipboard", "uint", 0)
	DllCall("EmptyClipboard")
	DllCall("SetClipboardData", "uint", 8, "uint", hdib)
	DllCall("CloseClipboard")
}

This is all assuming that your above code was working, and also assuming that your GDI_CaptureScreen() func does indeed return an hBitmap. I will try to test this later when I get home, but i'll be honest. There is a decent chance I will forget. Hope this gets you at least a bit closer
ShatterCoder
Posts: 84
Joined: 06 Oct 2016, 15:57

Re: GDI_CaptureScreen to Clipboard

02 May 2024, 09:53

So I was finally able to test the code I posted, and found that it does not work. Then again I tried the code you posted and could not make that work either. It's simple enough using the Gdip.ahk or Gdip_all.ahk libs but you said you do not want to go that route. I suspect that you have more to your script and that part of what allows the script you posted to work is missing from the post. Without that bit I'm not sure I can help much more.
User avatar
noname
Posts: 516
Joined: 19 Nov 2013, 09:15

Re: GDI_CaptureScreen to Clipboard

03 May 2024, 12:57

@ShatterCoder I tried your code and it seems to work but there was something strange after a scan but it can be interference from ahk programs that are running on my PC . I only made some small change do not know if it is really necessary ....

Maybe you can try again i am running v1.1.33.11 on win10 pro

Code: Select all

F2::        ; get FullScreen Screenshot and save it to file and load it to clipboard


Clipboard :="" 	; Clear the clipboard

hBM := GDI_CaptureScreen( 0, 0, A_ScreenWidth, A_ScreenHeight )   
SetBitmapToClipboard(hBM)

Return




;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;


GDI_CaptureScreen( X, Y, W, H, ByRef Checksum="" ) {
 tDC := DllCall( "CreateCompatibleDC", UInt,0 )
 hBM := DllCall( "CopyImage", UInt,DllCall( "CreateBitmap", Int,W, Int,H, UInt,1, UInt,24
                            , UInt,0 ), UInt,0, Int,0, Int,0, UInt,0x2008, UInt )
 oBM := DllCall( "SelectObject", UInt,tDC, UInt,hBM ), hDC := DllCall( "GetDC", UInt,0 )
 DllCall( "BitBlt"
     , UInt,tDC, UInt,0, UInt,0, Int,W, Int,H, UInt,hDC, UInt,X, UInt,Y, UInt,0x00CC0020 )
 DllCall( "ReleaseDC", UInt,0, UInt,hDC ),   DllCall( "SelectObject", UInt,tDC, UInt,oBM )
 If ( Checksum <> "" )
   VarSetCapacity( BM,24,0 ), DllCall( "GetObject", UInt,hBM, UInt,24, UInt,&BM )
 , DllCall( "shlwapi\HashData", UInt,NumGet(BM,20), UInt,NumGet( BM,12 )*NumGet( BM,8 )
                              , Int64P,Checksum, UInt,7 )
 Return hBM, DllCall( "DeleteDC", UInt,tDC )
}


;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

SetBitmapToClipboard(hBitmap )
{
	DllCall("GetObject", "uint", hBitmap, "int", VarSetCapacity(oi, 84, 0), "uint", &oi)
	hdib := DllCall("GlobalAlloc", "uint", 2, "uint", 40+NumGet(oi, 44))
	pdib := DllCall("GlobalLock", "uint", hdib)
	DllCall("RtlMoveMemory", "uint", pdib, "uint", &oi+24, "uint", 40)
	DllCall("RtlMoveMemory", "Uint", pdib+40, "Uint", NumGet(oi, 20), "uint", NumGet(oi, 44))
	DllCall("GlobalUnlock", "uint", hdib)
	DllCall("DeleteObject", "uint", hBitmap)
	DllCall("OpenClipboard", "uint", 0)
	DllCall("EmptyClipboard")
	DllCall("SetClipboardData", "uint", 8, "uint", hdib)
	DllCall("CloseClipboard")
}

just me
Posts: 9511
Joined: 02 Oct 2013, 08:51
Location: Germany

Re: GDI_CaptureScreen to Clipboard

04 May 2024, 03:35

@ShatterCoder & @noname,
you're posting ancient AHK v1.0 code only running on 32-bit AHK.

@GEOVAN,
same for you. The following might do what you want:

Code: Select all

#Requires AutoHotkey v1.1.34
#NoEnv

F2::        ; get FullScreen Screenshot and and load it to clipboard
Clipboard := 	; Clear the clipboard
GDI_CaptureScreenToClipboard(0, 0, A_ScreenWidth, A_ScreenHeight)
Return

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
GDI_CaptureScreenToClipboard( X, Y, W, H) {
   Static SizeOfDIB := A_PtrSize = 8 ? 104 : 84 ; DIBSECTION
   Static SizeOfBM  := A_PtrSize = 8 ?  32 : 24 ; BITMAP
   Static SizeOfBIH := 40                       ; BITMAPINFOHEADER
   Static OffBits   := A_PtrSize = 8 ?  24 : 20 ; BITMAP -> bmBits
   Static OffSize   := A_PtrSize = 8 ?  52 : 44 ; BITMAPINFOHEADER -> biSizeImage
   Local
   hDC := DllCall("GetDC", "Ptr", 0, "UPtr")
   cDC := DllCall("CreateCompatibleDC", "Ptr", hDC, "UPtr")
   hBM := DllCall("CreateCompatibleBitmap", "Ptr", hDC, "Int", W, "Int", H, "UPtr")
   hBM := DllCall("CopyImage", "Ptr", hBM, "UInt", 0, "Int", 0, "Int", 0, "UInt", 0x2008, "UPtr")
   oBM := DllCall("SelectObject", "Ptr", cDC, "Ptr", hBM, "UPtr")
   DllCall("BitBlt", "Ptr", cDC, "Int", 0, "Int", 0, "Int", W, "Int", H
                   , "Ptr", hDC, "Int", X, "Int", Y, "UInt", 0x40CC0020)
   DllCall("SelectObject", "Ptr", cDC, "Ptr", oBM)
   DllCall("DeleteDC", "Ptr", tDC)
   DllCall("ReleaseDC", "Ptr", 0, "Ptr", hDC)
   VarSetCapacity(DIB, SizeOfDIB, 0) ; DIBSECTION
   DllCall("GetObject", "Ptr", hBM, "Int", SizeOfDIB, "Ptr", &DIB)
   Size := NumGet(DIB, OffSize, "UInt")
   Bits := NumGet(DIB, OffBits, "UPtr")
   hDIB := DllCall("GlobalAlloc", "UInt", 2, "UInt", SizeOfBIH + Size, "UPtr")
   pDIB := DllCall("GlobalLock", "Ptr", hDIB, "UPtr")
   DllCall("RtlMoveMemory", "Ptr", pDIB, "Ptr", &DIB + SizeOfBM, "UInt", SizeOfBIH)
   DllCall("RtlMoveMemory", "Ptr", pDIB + SizeOfBIH, "Ptr", Bits, "UInt", Size)
   DllCall("GlobalUnlock", "Ptr", hDIB)
   DllCall("DeleteObject", "Ptr", hBM)
   DllCall("OpenClipboard", "Ptr", 0)
   DllCall("EmptyClipboard")
   DllCall("SetClipboardData", "UInt", 8, "Ptr", hDIB)
   DllCall("CloseClipboard")
}
GEOVAN
Posts: 178
Joined: 03 Mar 2022, 11:12

Re: GDI_CaptureScreen to Clipboard

05 May 2024, 23:15

Hello, I try all the suggestions you gave me, and all are working perfect,
Thank you all for your help!
Last edited by GEOVAN on 05 May 2024, 23:19, edited 1 time in total.
GEOVAN
Posts: 178
Joined: 03 Mar 2022, 11:12

Re: GDI_CaptureScreen to Clipboard

07 May 2024, 09:45

Please let me further ask:

Is there a way to go one step further in order also to save the screen capture to an image file, as .png or as .jpg with 100% quality, something like:

Code: Select all

GDI_CaptureScreenToFile( X, Y, W, H, Path_to_File, Quality) {
.......SOME CODE........
}


The following do the job, but i wonder if there is something "smarter":

Code: Select all

F2::        ; get FullScreen Screenshot and and load it to clipboard

Clipboard := 	; Clear the clipboard

hBM := ""
hBM := GDI_CaptureScreen( 0, 0, A_ScreenWidth, A_ScreenHeight ) 

GDIP("Startup")
 SavePicture(hBM, A_ScriptDir . "\Screenshot.png" )
 GDIP("Shutdown")
DllCall( "DeleteObject", "Ptr",hBM )


Return



;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

GDI_CaptureScreen( X, Y, W, H, ByRef Checksum="" )  {
 tDC := DllCall( "CreateCompatibleDC", UInt,0 )
 hBM := DllCall( "CopyImage", UInt,DllCall( "CreateBitmap", Int,W, Int,H, UInt,1, UInt,24
                            , UInt,0 ), UInt,0, Int,0, Int,0, UInt,0x2008, UInt )
 oBM := DllCall( "SelectObject", UInt,tDC, UInt,hBM ), hDC := DllCall( "GetDC", UInt,0 )
 DllCall( "BitBlt"
     , UInt,tDC, UInt,0, UInt,0, Int,W, Int,H, UInt,hDC, UInt,X, UInt,Y, UInt,0x00CC0020 )
 DllCall( "ReleaseDC", UInt,0, UInt,hDC ),   DllCall( "SelectObject", UInt,tDC, UInt,oBM )
 If ( Checksum <> "" )
   VarSetCapacity( BM,24,0 ), DllCall( "GetObject", UInt,hBM, UInt,24, UInt,&BM )
 , DllCall( "shlwapi\HashData", UInt,NumGet(BM,20), UInt,NumGet( BM,12 )*NumGet( BM,8 )
                              , Int64P,Checksum, UInt,7 )
 Return hBM, DllCall( "DeleteDC", UInt,tDC )
}

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;


SavePicture(hBM, sFile) {                                      
Local V,  pBM := VarSetCapacity(V,16,0)>>8,  Ext := LTrim(SubStr(sFile,-3),"."),  E := [0,0,0,0]
Local Enc := 0x557CF400 | Round({"bmp":0, "jpg":1,"jpeg":1,"gif":2,"tif":5,"tiff":5,"png":6}[Ext])
  E[1] := DllCall("gdi32\GetObjectType", "Ptr",hBM ) <> 7
  E[2] := E[1] ? 0 : DllCall("gdiplus\GdipCreateBitmapFromHBITMAP", "Ptr",hBM, "UInt",0, "PtrP",pBM)
  NumPut(0x2EF31EF8,NumPut(0x0000739A,NumPut(0x11D31A04,NumPut(Enc+0,V,"UInt"),"UInt"),"UInt"),"UInt")
  E[3] := pBM ? DllCall("gdiplus\GdipSaveImageToFile", "Ptr",pBM, "WStr",sFile, "Ptr",&V, "UInt",0) : 1
  E[4] := pBM ? DllCall("gdiplus\GdipDisposeImage", "Ptr",pBM) : 1
Return E[1] ? 0 : E[2] ? -1 : E[3] ? -2 : E[4] ? -3 : 1  
} 


GDIP(Var1:="Startup") {                                                
  Static SI:=Chr(!(VarSetCapacity(Si,24,0)>>16)), pToken:=0, hMod:=0, Res:=0, AOK:=0
  If (AOK := (Var1="Startup" and pToken=0) Or (Var1<>"Startup" and pToken<>0))  {
      If (Var1="Startup") {
               hMod := DllCall("LoadLibrary", "Str","gdiplus.dll", "Ptr")
               Res  := DllCall("gdiplus\GdiplusStartup", "PtrP",pToken, "Ptr",&SI, "UInt",0)
      } Else { 
               Res  := DllCall("gdiplus\GdiplusShutdown", "Ptr",pToken )
               DllCall("FreeLibrary", "Ptr",hMod),   hMod:=0,   pToken:=0
   }}  
Return (AOK ? !Res : Res:=0)    
}

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
Is there an easier or smarter code to do it, please?
GEOVAN
Posts: 178
Joined: 03 Mar 2022, 11:12

Re: GDI_CaptureScreen to Clipboard

08 May 2024, 23:07

Hello,
Please you can see the following topic: (many thanks to @teadrinker)
viewtopic.php?f=76&t=94345&p=571071#p571071

Return to “Ask for Help (v1)”

Who is online

Users browsing this forum: Bender and 83 guests