How to save image in clipboard.

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

Re: How to save image in clipboard.

Post by GEOVAN » 17 May 2024, 12:43

Dear teadrinker, i want to say a BIG THANK YOU to you!
You help me so much.
THANK YOU!!!
:superhappy:

I know that this may be little different topic, but i kindly request a last point regarding the "Ptr" and "UPtr" and "Int" and "Uint" used in the "DllCall("DeleteObject" ....", because i think you are the best person to advice.
From my reading i understand that the best to use is the "Ptr", because it covers all the rest ("UPtr" and "Int" and "Uint")

For example purposes, lets consider the following function:

Code: Select all

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")
}
Now, lets focus in the following line in the above code:

Code: Select all

DllCall("DeleteObject","uint",hbm)
This line (and ONLY this line without modify anything else from the other lines of the code) can be substituted with the following: [ because if i am correct --> "Ptr" covers all the rest ("UPtr" and "Int" and "Uint") ]

Code: Select all

DllCall( "DeleteObject", "Ptr", hbm )
So the code can be as follow:

Code: Select all

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", "Ptr", hbm )
    DllCall("CloseClipboard")
}
Do i understand this matter correct please?
teadrinker
Posts: 4389
Joined: 29 Mar 2015, 09:41
Contact:

Re: How to save image in clipboard.

Post by teadrinker » 17 May 2024, 18:35

Whether "Ptr" or "Int" is used depends on the size of the data that is specified after it.

Code: Select all

DllCall("DeleteObject","uint",hbm)
Here hbm refers to the "handle" data type. In 32-bit processes this type occupies 32 bits and in 64-bit processes it occupies 64 bits. That's why it is always better to specify "Ptr" in this case to make the code work with an interpreter of any bitness because "Ptr" also corresponds to 32 bits in 32-bit processes and 64 bits in 64-bit processes.
The use of the prefix "U" is described here.
GEOVAN
Posts: 185
Joined: 03 Mar 2022, 11:12

Re: How to save image in clipboard.

Post by GEOVAN » 18 May 2024, 05:23

Thank you for your comments regarding "Ptr" and "UPtr" and "Int" and "Uint", and for the link:
https://www.autohotkey.com/docs/v1/lib/DllCall.htm#unsigned

From that link, it is documented the following:
"Ptr" is ..... equivalent to Int or Int64 depending on whether the exe running the script is 32-bit or 64-bit.
Ptr should be used for pointers to arrays or structures (such as RECT* or LPPOINT) and almost all handles (such as HWND, HBRUSH or HBITMAP).
UPtr is also valid, but is only unsigned in 32-bit builds as AutoHotkey does not support unsigned 64-bit integers.
So, as a conclusion:
"Ptr" is equivalent to "Int" and "Int64"
and
"UPtr" is equivalent to "Uint"


But, i note also from the following page:
viewtopic.php?t=71028
"Just use "ptr" for all handles and forgot about "uint" and "uptr"."


So if the above are correct concluded, then:

PART A: DllCall("DeleteObject"..........) INSIDE the FUNCTION:
Example:

Code: Select all

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")
}
WITHOUT modify anything else in the code, we can ONLY modify the line:
DllCall("DeleteObject","uint",hbm) ---> to be ---> DllCall( "DeleteObject", "UPtr", hbm ) ---> OR also can be ---> DllCall( "DeleteObject", "Ptr", hbm )

So the function it can be modified as follow:

Code: Select all

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", "UPtr", hbm )
    DllCall("CloseClipboard")
}
OR, also can be as follow:

Code: Select all

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", "Ptr", hbm )
    DllCall("CloseClipboard")
}
Please, are all the above correct regarding PART A: DllCall("DeleteObject"..........) INSIDE the FUNCTION ?




PART B: DllCall("DeleteObject"..........) OUTSIDE the FUNCTION:

Code: Select all

F2::  ; press F2 to save screenshot

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

SaveToImageFile(hBM, A_ScriptDir . "\ClipboardImage.jpg", 100)

DllCall("DeleteObject", "Ptr", hbm)  ; Clean-up

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 )
}


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


SaveToImageFile(hBM, destImageFilePath, jpegQuality := 75) {
   gdip := new GDIplus()
   pBM := gdip.CreateBitmapFromHBITMAP(hBM)
   gdip.SaveBitmap(pBM, destImageFilePath, jpegQuality)
   gdip.DisposeImage(pBM)
}

class GDIplus  {
   __New() {
      this.hLib := DllCall("LoadLibrary", "Str", "gdiplus", "Ptr")
      VarSetCapacity(si, 8 + A_PtrSize*2, 0), si := Chr(1)
      DllCall("gdiplus\GdiplusStartup", "PtrP", pToken, "Ptr", &si, "Ptr", 0)
      this.token := pToken
   }
   
   __Delete() {
      DllCall("gdiplus\GdiplusShutdown", "Ptr", this.token)
      DllCall("FreeLibrary", "Ptr", this.hLib)
   }

   CreateBitmapFromHBITMAP(hBM, Palette=0)  {
      DllCall("gdiplus\GdipCreateBitmapFromHBITMAP", "Ptr", hBM, "Ptr", Palette, "PtrP", pBM)
      return pBM
   }

   SaveBitmap(pBM, ByRef info, Quality := 75, tobuff := "")
   {
      ; info — if copying to buffer, then file extension, if copying to file, then file path
      if tobuff
         Extension := info
      else
         SplitPath, info,,, Extension
      if Extension not in BMP,DIB,RLE,JPG,JPEG,JPE,JFIF,GIF,TIF,TIFF,PNG
         return -1

      DllCall("gdiplus\GdipGetImageEncodersSize", "UintP", nCount, "UintP", nSize)
      VarSetCapacity(ci, nSize)
      DllCall("gdiplus\GdipGetImageEncoders", "UInt", nCount, "UInt", nSize, "Ptr", &ci)
      if !(nCount && nSize)
         return -2
      
      Loop, % nCount  {
         sString := StrGet(NumGet(ci, (idx := (48+7*A_PtrSize)*(A_Index-1))+32+3*A_PtrSize), "UTF-16")
         if !InStr(sString, "*." Extension)
            continue
         
         pCodec := &ci+idx
         break
      }
      
      if !pCodec
         return -3
      
      if RegExMatch(Extension, "i)^J(PG|PEG|PE|FIF)$") && Quality != 75  {
         DllCall("gdiplus\GdipGetEncoderParameterListSize", "Ptr", pBM, "Ptr", pCodec, "UintP", nSize)
         VarSetCapacity(EncoderParameters, nSize, 0)
         DllCall("gdiplus\GdipGetEncoderParameterList", "Ptr", pBM, "Ptr", pCodec, "UInt", nSize, "Ptr", &EncoderParameters)
         Loop, % NumGet(EncoderParameters, "UInt")
         {
            elem := (24+A_PtrSize)*(A_Index-1) + 4 + (pad := A_PtrSize = 8 ? 4 : 0)
            if (NumGet(EncoderParameters, elem+16, "UInt") = 1) && (NumGet(EncoderParameters, elem+20, "UInt") = 6)
            {
               p := elem+&EncoderParameters-pad-4
               NumPut(Quality, NumGet(NumPut(4, NumPut(1, p+0)+20, "UInt")), "UInt")
               break
            }
         }      
      }
      if !tobuff
         E := DllCall("gdiplus\GdipSaveImageToFile", "Ptr", pBM, "WStr", info, "Ptr", pCodec, "UInt", p ? p : 0)
      else  {
         DllCall( "ole32\CreateStreamOnHGlobal", "UInt", 0, "Int", 1, "PtrP", pStream )
         if !E := DllCall( "gdiplus\GdipSaveImageToStream", "Ptr", pBM, "Ptr", pStream, "Ptr", pCodec, "UInt", p ? p : 0 )  {
            DllCall( "ole32\GetHGlobalFromStream", "Ptr", pStream, "PtrP", hData )
            pData := DllCall( "GlobalLock", "Ptr", hData, "Ptr" )
            nSize := DllCall( "GlobalSize", "Ptr", hData, "Ptr" )
            VarSetCapacity( info, 0), VarSetCapacity( info, nSize, 0 )
            DllCall( "RtlMoveMemory", "Ptr", &info, "Ptr", pData, "UInt", nSize )
            DllCall( "GlobalUnlock", "Ptr", hData )
            DllCall( "GlobalFree", "Ptr", hData )
         }
         ObjRelease(pStream)
      }
      return E ? -4 : tobuff ? nSize : 0
   }
   
   DisposeImage(pBM)  {
      return DllCall("gdiplus\GdipDisposeImage", "Ptr", pBM)
   }
}

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
I understand that the above function GDI_CaptureScreen() has incorrect data types, but only for educational purposes, let me ask:
Under F2:: (WHICH IS OUTSIDE THE FUNTION) ---> What is the correct to use, please?
DllCall( "DeleteObject", "UPtr", hbm )
or
DllCall( "DeleteObject", "Ptr", hbm )
or
DllCall( "DeleteObject", "UInt", hbm )
or
DllCall( "DeleteObject", "Int", hbm )


What is the correct to use, please? (The reason i ask, it is because inside the function GDI_CaptureScreen(), there is no Ptr or UPtr, but may be we can use them?)

Your answers regarding PART A and PART B above, will help me clarify all my worries / questions regarding Ptr , UPtr Int, Uint.
Once again many many thanks for your help.
teadrinker
Posts: 4389
Joined: 29 Mar 2015, 09:41
Contact:

Re: How to save image in clipboard.

Post by teadrinker » 21 May 2024, 16:40

There is no difference in using "Ptr" inside or outside functions. With handles and pointers you must always use "Ptr". So the correct option is to always use "Ptr":
DllCall("DeleteObject", "Ptr", hbm)
as much as
DllCall("SetClipboardData","uint",0x2,"Ptr",hbm)
Also, if the return value of DllCall() is a handle or pointer, you must specify "Ptr" as the last parameter:
hbm := DllCall("LoadImage","uint",0,"str",Filename,"uint",0,"int",0,"int",0,"uint",0x10, "Ptr")
tDC := DllCall("CreateCompatibleDC", "Ptr", 0, "Ptr")
hBM := DllCall("CopyImage", "Ptr", DllCall("CreateBitmap", Int,W, Int,H, UInt,1, UInt,24, UInt,0, "Ptr"), UInt,0, Int,0, Int,0, UInt,0x2008, "Ptr")
oBM := DllCall("SelectObject", "Ptr",tDC, "Ptr",hBM, "Ptr")
And so on...
GEOVAN
Posts: 185
Joined: 03 Mar 2022, 11:12

Re: How to save image in clipboard.

Post by GEOVAN » 23 May 2024, 13:57

Thank you very much!

Please let me clarify a last point regarding your comment :
teadrinker wrote:
21 May 2024, 16:40
There is no difference in using "Ptr" inside or outside functions. With handles and pointers you must always use "Ptr". So the correct option is to always use "Ptr":
DllCall("DeleteObject", "Ptr", hbm)
Does your above comment valid in any case?
What i mean:
Is your comment also valid in cases where the reference function has no other Ptr inside it?
OR, do i must also edit the function in such a way to contain Ptr ?

To explain better what i mean, lets consider the following code - example which contains THREE Functions, the following:
(1) GDI_CaptureScreen
(2) ImageToClipboard
(3) SaveToImageFile

Example - Code:

Code: Select all

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

F2::  ; press F2 to save screenshot as .bmp file and also load it to clipboard

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

SaveToImageFile(hBM, A_ScriptDir . "\ClipboardImage.bmp")
sleep 1000
ImageToClipboard(A_ScriptDir . "\ClipboardImage.bmp")

DllCall("DeleteObject", "Ptr", hbm)  ; Clean-up

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 )
}


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

ImageToClipboard(Filename)  ; Copies image data from file to the clipboard - supports .bmp format
{
    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", "Ptr", hbm )
    DllCall("CloseClipboard")
}

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

SaveToImageFile(hBM, destImageFilePath, jpegQuality := 75) {
   gdip := new GDIplus()
   pBM := gdip.CreateBitmapFromHBITMAP(hBM)
   gdip.SaveBitmap(pBM, destImageFilePath, jpegQuality)
   gdip.DisposeImage(pBM)
}

class GDIplus  {
   __New() {
      this.hLib := DllCall("LoadLibrary", "Str", "gdiplus", "Ptr")
      VarSetCapacity(si, 8 + A_PtrSize*2, 0), si := Chr(1)
      DllCall("gdiplus\GdiplusStartup", "PtrP", pToken, "Ptr", &si, "Ptr", 0)
      this.token := pToken
   }
   
   __Delete() {
      DllCall("gdiplus\GdiplusShutdown", "Ptr", this.token)
      DllCall("FreeLibrary", "Ptr", this.hLib)
   }

   CreateBitmapFromHBITMAP(hBM, Palette=0)  {
      DllCall("gdiplus\GdipCreateBitmapFromHBITMAP", "Ptr", hBM, "Ptr", Palette, "PtrP", pBM)
      return pBM
   }

   SaveBitmap(pBM, ByRef info, Quality := 75, tobuff := "")
   {
      ; info — if copying to buffer, then file extension, if copying to file, then file path
      if tobuff
         Extension := info
      else
         SplitPath, info,,, Extension
      if Extension not in BMP,DIB,RLE,JPG,JPEG,JPE,JFIF,GIF,TIF,TIFF,PNG
         return -1

      DllCall("gdiplus\GdipGetImageEncodersSize", "UintP", nCount, "UintP", nSize)
      VarSetCapacity(ci, nSize)
      DllCall("gdiplus\GdipGetImageEncoders", "UInt", nCount, "UInt", nSize, "Ptr", &ci)
      if !(nCount && nSize)
         return -2
      
      Loop, % nCount  {
         sString := StrGet(NumGet(ci, (idx := (48+7*A_PtrSize)*(A_Index-1))+32+3*A_PtrSize), "UTF-16")
         if !InStr(sString, "*." Extension)
            continue
         
         pCodec := &ci+idx
         break
      }
      
      if !pCodec
         return -3
      
      if RegExMatch(Extension, "i)^J(PG|PEG|PE|FIF)$") && Quality != 75  {
         DllCall("gdiplus\GdipGetEncoderParameterListSize", "Ptr", pBM, "Ptr", pCodec, "UintP", nSize)
         VarSetCapacity(EncoderParameters, nSize, 0)
         DllCall("gdiplus\GdipGetEncoderParameterList", "Ptr", pBM, "Ptr", pCodec, "UInt", nSize, "Ptr", &EncoderParameters)
         Loop, % NumGet(EncoderParameters, "UInt")
         {
            elem := (24+A_PtrSize)*(A_Index-1) + 4 + (pad := A_PtrSize = 8 ? 4 : 0)
            if (NumGet(EncoderParameters, elem+16, "UInt") = 1) && (NumGet(EncoderParameters, elem+20, "UInt") = 6)
            {
               p := elem+&EncoderParameters-pad-4
               NumPut(Quality, NumGet(NumPut(4, NumPut(1, p+0)+20, "UInt")), "UInt")
               break
            }
         }      
      }
      if !tobuff
         E := DllCall("gdiplus\GdipSaveImageToFile", "Ptr", pBM, "WStr", info, "Ptr", pCodec, "UInt", p ? p : 0)
      else  {
         DllCall( "ole32\CreateStreamOnHGlobal", "UInt", 0, "Int", 1, "PtrP", pStream )
         if !E := DllCall( "gdiplus\GdipSaveImageToStream", "Ptr", pBM, "Ptr", pStream, "Ptr", pCodec, "UInt", p ? p : 0 )  {
            DllCall( "ole32\GetHGlobalFromStream", "Ptr", pStream, "PtrP", hData )
            pData := DllCall( "GlobalLock", "Ptr", hData, "Ptr" )
            nSize := DllCall( "GlobalSize", "Ptr", hData, "Ptr" )
            VarSetCapacity( info, 0), VarSetCapacity( info, nSize, 0 )
            DllCall( "RtlMoveMemory", "Ptr", &info, "Ptr", pData, "UInt", nSize )
            DllCall( "GlobalUnlock", "Ptr", hData )
            DllCall( "GlobalFree", "Ptr", hData )
         }
         ObjRelease(pStream)
      }
      return E ? -4 : tobuff ? nSize : 0
   }
   
   DisposeImage(pBM)  {
      return DllCall("gdiplus\GdipDisposeImage", "Ptr", pBM)
   }
}

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

As you can see, inside the two functions:
(1) GDI_CaptureScreen, and (2) ImageToClipboard
there is "NO TRACE" of Ptr, and in the final i use "DllCall("DeleteObject", "Ptr", hbm)", as shown in following screenshots:

image.png
image.png (123.57 KiB) Viewed 544 times
image.png
image.png (166.51 KiB) Viewed 544 times

Is this OK, or do i must also edit the two functions (1) GDI_CaptureScreen, and (2) ImageToClipboard, in such a way to contain Ptr ?
teadrinker
Posts: 4389
Joined: 29 Mar 2015, 09:41
Contact:

Re: How to save image in clipboard.

Post by teadrinker » 23 May 2024, 17:03

GEOVAN wrote: Does your above comment valid in any case?
Yes, the correct data types must always be specified when calling DllCall(), regardless of where that call is located.
GEOVAN
Posts: 185
Joined: 03 Mar 2022, 11:12

Re: How to save image in clipboard.

Post by GEOVAN » 23 May 2024, 23:50

teadrinker wrote:
23 May 2024, 17:03
GEOVAN wrote: Does your above comment valid in any case?
Yes, the correct data types must always be specified when calling DllCall(), regardless of where that call is located.

So, regarding the Example - Code in my previous comment, where its TWO functions (1) GDI_CaptureScreen, and (2) ImageToClipboard have "NO TRACE" of Ptr (but they use only UInt and Int),
the question is:

The Example - Code as shown below is completely correct?
OR
Since i use Ptr in the "DllCall("DeleteObject", "Ptr", hbm)" in the two locations (as shown in the following screenshots) do i MUST also need to MODIFY the WHOLE two functions (1) GDI_CaptureScreen, and (2) ImageToClipboard in such a way in order to contain Ptr INSTEAD of Int and UInt?

If i understand correct, I think that the answer is that the Example - Code as shown below is completely correct and is not need any modifications inside its two functions (1) GDI_CaptureScreen, and (2) ImageToClipboard.
But, i am not 100% sure, so i will kindly ask your clarification, please.




Screenshots:
image.png
image.png (291.04 KiB) Viewed 458 times
Example - Code:

Code: Select all

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

F2::  ; press F2 to save screenshot as .bmp file and also load it to clipboard

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

SaveToImageFile(hBM, A_ScriptDir . "\ClipboardImage.bmp")
sleep 1000
ImageToClipboard(A_ScriptDir . "\ClipboardImage.bmp")

DllCall("DeleteObject", "Ptr", hbm)  ; Clean-up

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 )
}


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

ImageToClipboard(Filename)  ; Copies image data from file to the clipboard - supports .bmp format
{
    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", "Ptr", hbm )
    DllCall("CloseClipboard")
}

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

SaveToImageFile(hBM, destImageFilePath, jpegQuality := 75) {
   gdip := new GDIplus()
   pBM := gdip.CreateBitmapFromHBITMAP(hBM)
   gdip.SaveBitmap(pBM, destImageFilePath, jpegQuality)
   gdip.DisposeImage(pBM)
}

class GDIplus  {
   __New() {
      this.hLib := DllCall("LoadLibrary", "Str", "gdiplus", "Ptr")
      VarSetCapacity(si, 8 + A_PtrSize*2, 0), si := Chr(1)
      DllCall("gdiplus\GdiplusStartup", "PtrP", pToken, "Ptr", &si, "Ptr", 0)
      this.token := pToken
   }
   
   __Delete() {
      DllCall("gdiplus\GdiplusShutdown", "Ptr", this.token)
      DllCall("FreeLibrary", "Ptr", this.hLib)
   }

   CreateBitmapFromHBITMAP(hBM, Palette=0)  {
      DllCall("gdiplus\GdipCreateBitmapFromHBITMAP", "Ptr", hBM, "Ptr", Palette, "PtrP", pBM)
      return pBM
   }

   SaveBitmap(pBM, ByRef info, Quality := 75, tobuff := "")
   {
      ; info — if copying to buffer, then file extension, if copying to file, then file path
      if tobuff
         Extension := info
      else
         SplitPath, info,,, Extension
      if Extension not in BMP,DIB,RLE,JPG,JPEG,JPE,JFIF,GIF,TIF,TIFF,PNG
         return -1

      DllCall("gdiplus\GdipGetImageEncodersSize", "UintP", nCount, "UintP", nSize)
      VarSetCapacity(ci, nSize)
      DllCall("gdiplus\GdipGetImageEncoders", "UInt", nCount, "UInt", nSize, "Ptr", &ci)
      if !(nCount && nSize)
         return -2
      
      Loop, % nCount  {
         sString := StrGet(NumGet(ci, (idx := (48+7*A_PtrSize)*(A_Index-1))+32+3*A_PtrSize), "UTF-16")
         if !InStr(sString, "*." Extension)
            continue
         
         pCodec := &ci+idx
         break
      }
      
      if !pCodec
         return -3
      
      if RegExMatch(Extension, "i)^J(PG|PEG|PE|FIF)$") && Quality != 75  {
         DllCall("gdiplus\GdipGetEncoderParameterListSize", "Ptr", pBM, "Ptr", pCodec, "UintP", nSize)
         VarSetCapacity(EncoderParameters, nSize, 0)
         DllCall("gdiplus\GdipGetEncoderParameterList", "Ptr", pBM, "Ptr", pCodec, "UInt", nSize, "Ptr", &EncoderParameters)
         Loop, % NumGet(EncoderParameters, "UInt")
         {
            elem := (24+A_PtrSize)*(A_Index-1) + 4 + (pad := A_PtrSize = 8 ? 4 : 0)
            if (NumGet(EncoderParameters, elem+16, "UInt") = 1) && (NumGet(EncoderParameters, elem+20, "UInt") = 6)
            {
               p := elem+&EncoderParameters-pad-4
               NumPut(Quality, NumGet(NumPut(4, NumPut(1, p+0)+20, "UInt")), "UInt")
               break
            }
         }      
      }
      if !tobuff
         E := DllCall("gdiplus\GdipSaveImageToFile", "Ptr", pBM, "WStr", info, "Ptr", pCodec, "UInt", p ? p : 0)
      else  {
         DllCall( "ole32\CreateStreamOnHGlobal", "UInt", 0, "Int", 1, "PtrP", pStream )
         if !E := DllCall( "gdiplus\GdipSaveImageToStream", "Ptr", pBM, "Ptr", pStream, "Ptr", pCodec, "UInt", p ? p : 0 )  {
            DllCall( "ole32\GetHGlobalFromStream", "Ptr", pStream, "PtrP", hData )
            pData := DllCall( "GlobalLock", "Ptr", hData, "Ptr" )
            nSize := DllCall( "GlobalSize", "Ptr", hData, "Ptr" )
            VarSetCapacity( info, 0), VarSetCapacity( info, nSize, 0 )
            DllCall( "RtlMoveMemory", "Ptr", &info, "Ptr", pData, "UInt", nSize )
            DllCall( "GlobalUnlock", "Ptr", hData )
            DllCall( "GlobalFree", "Ptr", hData )
         }
         ObjRelease(pStream)
      }
      return E ? -4 : tobuff ? nSize : 0
   }
   
   DisposeImage(pBM)  {
      return DllCall("gdiplus\GdipDisposeImage", "Ptr", pBM)
   }
}

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
teadrinker
Posts: 4389
Joined: 29 Mar 2015, 09:41
Contact:

Re: How to save image in clipboard.

Post by teadrinker » 24 May 2024, 03:30

GEOVAN wrote: the Example - Code as shown below is completely correct
No, both of GDI_CaptureScreen() and ImageToClipboard() are completely incorrect.
GEOVAN
Posts: 185
Joined: 03 Mar 2022, 11:12

Re: How to save image in clipboard.

Post by GEOVAN » 24 May 2024, 04:09

I see.... Thank you very much!

With your help i finally i end up with the following final code:
Is this final code ALL completely correct, please?

Final code:

Code: Select all

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

F2::  ; press F2 to save screenshot as .bmp file and also load it to clipboard

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

SaveToImageFile(hBM, A_ScriptDir . "\ClipboardImage.bmp")
GDI_CaptureScreenToClipboard(0, 0, A_ScreenWidth, A_ScreenHeight)

DllCall("DeleteObject", "Ptr", hbm)  ; Clean-up

Return




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

GDI_CaptureScreen(X, Y, W, H) {
   static flags := (SRCCOPY := 0x00CC0020) | (CAPTUREBLT := 0x40000000)
   hDC := DllCall("GetDC", "Ptr", 0, "Ptr")
   hBM := DllCall("CreateCompatibleBitmap", "Ptr", hDC, "Int", W, "Int", H, "Ptr")
   hMDC := DllCall("CreateCompatibleDC", "Ptr", hDC, "Ptr")
   hObj := DllCall("SelectObject", "Ptr", hMDC, "Ptr", hBM, "Ptr")
   DllCall("BitBlt", "Ptr", hMDC, "Int", 0, "Int", 0, "Int", W, "Int", H
                   , "Ptr", hDC, "Int", X, "Int", Y, "UInt", flags)
   DllCall("SelectObject", "Ptr", hMDC, "Ptr", hObj, "Ptr")
   DllCall("DeleteDC", "Ptr", hMDC)
   DllCall("ReleaseDC", "Ptr", 0, "Ptr", hDC)
   Return hBM
}



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


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")
}


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

SaveToImageFile(hBM, destImageFilePath, jpegQuality := 75) {
   gdip := new GDIplus()
   pBM := gdip.CreateBitmapFromHBITMAP(hBM)
   gdip.SaveBitmap(pBM, destImageFilePath, jpegQuality)
   gdip.DisposeImage(pBM)
}

class GDIplus  {
   __New() {
      this.hLib := DllCall("LoadLibrary", "Str", "gdiplus", "Ptr")
      VarSetCapacity(si, 8 + A_PtrSize*2, 0), si := Chr(1)
      DllCall("gdiplus\GdiplusStartup", "PtrP", pToken, "Ptr", &si, "Ptr", 0)
      this.token := pToken
   }
   
   __Delete() {
      DllCall("gdiplus\GdiplusShutdown", "Ptr", this.token)
      DllCall("FreeLibrary", "Ptr", this.hLib)
   }

   CreateBitmapFromHBITMAP(hBM, Palette=0)  {
      DllCall("gdiplus\GdipCreateBitmapFromHBITMAP", "Ptr", hBM, "Ptr", Palette, "PtrP", pBM)
      return pBM
   }

   SaveBitmap(pBM, ByRef info, Quality := 75, tobuff := "")
   {
      ; info — if copying to buffer, then file extension, if copying to file, then file path
      if tobuff
         Extension := info
      else
         SplitPath, info,,, Extension
      if Extension not in BMP,DIB,RLE,JPG,JPEG,JPE,JFIF,GIF,TIF,TIFF,PNG
         return -1

      DllCall("gdiplus\GdipGetImageEncodersSize", "UintP", nCount, "UintP", nSize)
      VarSetCapacity(ci, nSize)
      DllCall("gdiplus\GdipGetImageEncoders", "UInt", nCount, "UInt", nSize, "Ptr", &ci)
      if !(nCount && nSize)
         return -2
      
      Loop, % nCount  {
         sString := StrGet(NumGet(ci, (idx := (48+7*A_PtrSize)*(A_Index-1))+32+3*A_PtrSize), "UTF-16")
         if !InStr(sString, "*." Extension)
            continue
         
         pCodec := &ci+idx
         break
      }
      
      if !pCodec
         return -3
      
      if RegExMatch(Extension, "i)^J(PG|PEG|PE|FIF)$") && Quality != 75  {
         DllCall("gdiplus\GdipGetEncoderParameterListSize", "Ptr", pBM, "Ptr", pCodec, "UintP", nSize)
         VarSetCapacity(EncoderParameters, nSize, 0)
         DllCall("gdiplus\GdipGetEncoderParameterList", "Ptr", pBM, "Ptr", pCodec, "UInt", nSize, "Ptr", &EncoderParameters)
         Loop, % NumGet(EncoderParameters, "UInt")
         {
            elem := (24+A_PtrSize)*(A_Index-1) + 4 + (pad := A_PtrSize = 8 ? 4 : 0)
            if (NumGet(EncoderParameters, elem+16, "UInt") = 1) && (NumGet(EncoderParameters, elem+20, "UInt") = 6)
            {
               p := elem+&EncoderParameters-pad-4
               NumPut(Quality, NumGet(NumPut(4, NumPut(1, p+0)+20, "UInt")), "UInt")
               break
            }
         }      
      }
      if !tobuff
         E := DllCall("gdiplus\GdipSaveImageToFile", "Ptr", pBM, "WStr", info, "Ptr", pCodec, "UInt", p ? p : 0)
      else  {
         DllCall( "ole32\CreateStreamOnHGlobal", "UInt", 0, "Int", 1, "PtrP", pStream )
         if !E := DllCall( "gdiplus\GdipSaveImageToStream", "Ptr", pBM, "Ptr", pStream, "Ptr", pCodec, "UInt", p ? p : 0 )  {
            DllCall( "ole32\GetHGlobalFromStream", "Ptr", pStream, "PtrP", hData )
            pData := DllCall( "GlobalLock", "Ptr", hData, "Ptr" )
            nSize := DllCall( "GlobalSize", "Ptr", hData, "Ptr" )
            VarSetCapacity( info, 0), VarSetCapacity( info, nSize, 0 )
            DllCall( "RtlMoveMemory", "Ptr", &info, "Ptr", pData, "UInt", nSize )
            DllCall( "GlobalUnlock", "Ptr", hData )
            DllCall( "GlobalFree", "Ptr", hData )
         }
         ObjRelease(pStream)
      }
      return E ? -4 : tobuff ? nSize : 0
   }
   
   DisposeImage(pBM)  {
      return DllCall("gdiplus\GdipDisposeImage", "Ptr", pBM)
   }
}

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
teadrinker
Posts: 4389
Joined: 29 Mar 2015, 09:41
Contact:

Re: How to save image in clipboard.

Post by teadrinker » 24 May 2024, 08:56

I didn't notice any errors.
GEOVAN
Posts: 185
Joined: 03 Mar 2022, 11:12

Re: How to save image in clipboard.

Post by GEOVAN » Today, 10:25

Thank you very very much!!!
Post Reply

Return to “Ask for Help (v1)”