How to send a pBitmap between scripts?

Get help with using AutoHotkey (v1.1 and older) and its commands and hotkeys
teadrinker
Posts: 4326
Joined: 29 Mar 2015, 09:41
Contact:

Re: How to send a pBitmap between scripts?

Post by teadrinker » 23 May 2022, 12:19

Only by converting in the base64 string. I don't think you really need JSON in this case. All the data could be passed in one data block, including text data.

c7aesa7r
Posts: 209
Joined: 02 Jun 2016, 21:09

Re: How to send a pBitmap between scripts?

Post by c7aesa7r » 23 May 2022, 12:29

teadrinker wrote:
23 May 2022, 12:19
Only by converting in the base64 string. I don't think you really need JSON in this case. All the data could be passed in one data block, including text data.
Just as curiosity, why does it need to be in b64 string?

I was reading the docs under dllcall to search for what data type to use for the string

In the sender:

Code: Select all

NumPut(pixelFormat, data)
NumPut(width, data, 4)
NumPut(height, data, 8)
NumPut(stride, data, 12, "UInt")
NumPut("xxxxxxxxxxx", data, 16, "Str")
Is it "str"?

And in the receiver:

Code: Select all

CreateBitmapFromData(hHeap, pImageData) {

   pixelFormat := NumGet(pImageData+0, "UInt")
   width       := NumGet(pImageData+4, "UInt")
   height      := NumGet(pImageData+8, "UInt")
   stride      := NumGet(pImageData+12,"UInt")
   str         := NumGet(pImageData+16,"??")
-edited-

teadrinker
Posts: 4326
Joined: 29 Mar 2015, 09:41
Contact:

Re: How to send a pBitmap between scripts?

Post by teadrinker » 23 May 2022, 13:02

NumPut is only for numbers, for strings there is StrPut(). If you want to send some text together with image data,
the sender:

Code: Select all

...
size := stride * height

str := "Hello, World!"

VarSetCapacity(data, dataSize := size + 16 + StrLen(str)*2 + 2, 0)
StrPut(str, &data + size + 16)
DllCall("RtlCopyMemory", "Ptr", &data + 16, "Ptr", scan0, "Ptr", size)

GDIp.UnlockBits(pBitmap, bitmapData)
GDIp.DisposeImage(pBitmap)
GDIp := ""

NumPut(pixelFormat, data)
NumPut(width, data, 4)
NumPut(height, data, 8)
NumPut(stride, data, 12, "UInt")

if !SendData(hwnd, &data, dataSize)
   MsgBox, Failed to send data
...
the receiver:

Code: Select all

...
CreateBitmapFromData(hHeap, pImageData) {
   pixelFormat := NumGet(pImageData + 0, "UInt")
   width := NumGet(pImageData + 4, "UInt")
   height := NumGet(pImageData + 8, "UInt")
   stride := NumGet(pImageData + 12, "UInt")
   
   GDIp := new GDIplus
   pBitmap := GDIp.CreateBitmapFromScan0(width, height, stride, pixelFormat, pImageData + 16)
   hBitmap := GDIp.CreateHBITMAPFromBitmap(pBitmap)
   GDIp.DisposeImage(pBitmap)
   GDIp := ""
   
   MsgBox, % StrGet(pImageData + 16 + stride*height)
   
   DllCall("HeapFree", "Ptr", hHeap, "UInt", 0, "Ptr", pImageData)

   GuiControl, Move, Static1, x0 y0 w%width% h%height%
   GuiControl,, Static1, HBITMAP: %hBitmap%
   Gui, Show, w%width% h%height%
}
...

c7aesa7r
Posts: 209
Joined: 02 Jun 2016, 21:09

Re: How to send a pBitmap between scripts?

Post by c7aesa7r » 23 May 2022, 15:07

Thank you teadrinker! helped a lot! :salute:

I did a test adding a Sleep, 99999 inside of the function CreateBitmapFromData(hHeap, pImageData) { }
and when I send new wm_copydata msgs everything still works correctly

I read that SetTimer launches a new thread/pseudo thread (which I have no idea what this means)
taking into consideration that the receiver script is constantly receiving a lot of wm_copydata msgs from different senders

is still possible for the receiver script delays a response to the senders causing a drop in their performance?

dipstick5000
Posts: 31
Joined: 21 Jan 2020, 22:01

Re: How to send a pBitmap between scripts?

Post by dipstick5000 » 23 May 2022, 16:11

I wouldn't be a bit surprised if this is the wrong way to go about it, but I might try copying the image into a folder on your computer, then getting it with the second script. You could always have it deleted as well, when you're done with it. Or maybe just copy the image to your clipboard and get it with the second script?

teadrinker
Posts: 4326
Joined: 29 Mar 2015, 09:41
Contact:

Re: How to send a pBitmap between scripts?

Post by teadrinker » 23 May 2022, 16:21

dipstick5000 wrote: Or maybe just copy the image to your clipboard and get it with the second script?
In any case this is much slower way. In addition, the clipboard is a thing, which should be used by a user, but not an application.
c7aesa7r wrote: is still possible for the receiver script delays a response to the senders causing a drop in their performance?
I currently have no such experience, so you just have to test it. :)

dipstick5000
Posts: 31
Joined: 21 Jan 2020, 22:01

Re: How to send a pBitmap between scripts?

Post by dipstick5000 » 23 May 2022, 16:31

Sometimes we settle for the slower way if it still works. Are we talking 2 seconds or 20 minutes out of your day? It's likely 2 seconds, but I don't know you. Yes clipboards are things. And you're sticking with that? The clipboard is a tool, and you can use one that holds many clips. I don't understand, except for the time thing. Programmers solve problems with clipboards all the time. I'm sorry I wasted your time.

dipstick5000
Posts: 31
Joined: 21 Jan 2020, 22:01

Re: How to send a pBitmap between scripts?

Post by dipstick5000 » 23 May 2022, 16:35

Wait, isn't the clipboard a variable? And you have a problem using variables? Not sure what's going on here.

malcev
Posts: 1769
Joined: 12 Aug 2014, 12:37

Re: How to send a pBitmap between scripts?

Post by malcev » 23 May 2022, 16:40

You can share bitmap with CreateDIBSection with file mapping.
https://docs.microsoft.com/en-us/windows/win32/api/wingdi/nf-wingdi-createdibsection

teadrinker
Posts: 4326
Joined: 29 Mar 2015, 09:41
Contact:

Re: How to send a pBitmap between scripts?

Post by teadrinker » 23 May 2022, 16:42

dipstick5000 wrote: Not sure what's going on here.
Nevermind. In some cases the clipboard could be used, but this must be justified by real necessity. If there are other ways, it is better to use them.

teadrinker
Posts: 4326
Joined: 29 Mar 2015, 09:41
Contact:

Re: How to send a pBitmap between scripts?

Post by teadrinker » 23 May 2022, 16:44

malcev wrote: You can share bitmap with CreateDIBSection with file mapping
Already mentioned.
I didn't go into it, but how does the second script know that some object has been shared, and how does the first one know that the information has been read?
Last edited by teadrinker on 23 May 2022, 16:48, edited 1 time in total.


iseahound
Posts: 1444
Joined: 13 Aug 2016, 21:04
Contact:

Re: How to send a pBitmap between scripts?

Post by iseahound » 23 May 2022, 17:10

I think you can create a mutex, which allows you to keep track of the shared memory. See some details here: https://stackoverflow.com/a/4306232

The CreateFileMapping call is paired with OpenFileMapping where the same file mapping shares the same name. I assume Windows will find it for you. You may have to use two mutexes even though it seems one mutex would suffice.

Of course, I would try to avoid all of this in AutoHotkey, especially since it doesn't support threading.

c7aesa7r
Posts: 209
Joined: 02 Jun 2016, 21:09

Re: How to send a pBitmap between scripts?

Post by c7aesa7r » 01 Jun 2022, 09:41

I have been able to share strings using file mapping
malcev wrote:
23 May 2022, 16:40
You can share bitmap with CreateDIBSection with file mapping.
https://docs.microsoft.com/en-us/windows/win32/api/wingdi/nf-wingdi-createdibsection
could you point out how to share bitmaps as you described?

this method would be a lot better as I wouldn't need to use wm_copydata anymore, just keep things saved into the class and read it whenever I need from the second script

Code: Select all

pToken  := Gdip_Startup()
pBitmap := Gdip_BitmapFromScreen(0)


;====== Test script 1 ======
FM := new FileMapping()
;F1::FM.Write(A_Sec " - written by " A_ScriptName)
F1::FM.Write(pBitmap)
F2::MsgBox,,, % FM.Read(), 1
F3::FM.Write()    ; delete all
F4::FM:=""
;F5::MsgBox,,, % IsObject(FM), 1
;Esc::ExitApp



/*
;====== Test script 2 ======
FM := new FileMapping()
1::FM.Write(A_Sec " - written by " A_ScriptName)
2::MsgBox,,, % FM.Read(), 1
3::FM.Write()    ; delete all
4::FM:=""
5::MsgBox,,, % IsObject(FM), 1
Esc::ExitApp
*/



Class FileMapping {
   ; https://www.autohotkey.com/board/topic/93305-filemapping-class/#entry588351
   
   __New(Name="Global\MyFileMappingObject", BufSize=10000) {    
      
      ; Opens existing or creates new file mapping object
      static INVALID_HANDLE_VALUE := -1, PAGE_READWRITE := 0x4, FILE_MAP_ALL_ACCESS := 0xF001F
      
      hMapFile := DllCall("OpenFileMapping", "Ptr", FILE_MAP_ALL_ACCESS, "Int", 0, "Str", Name)
      
      if ( hMapFile == 0 ) {        
         ; OpenFileMapping Failed - file mapping object doesn't exist - that means we have to create it
         hMapFile := DllCall("CreateFileMapping", "Ptr", INVALID_HANDLE_VALUE, "Ptr", 0, "Int", PAGE_READWRITE, "Int", 0, "Int", BufSize, "Str", Name)
         if ( hMapFile == 0 )    ; CreateFileMapping Failed
               return
      }

      pBuf := DllCall("MapViewOfFile", "Ptr", hMapFile, "Int", FILE_MAP_ALL_ACCESS, "Int", 0, "Int", 0, "Ptr", BufSize)
      
      if ( pBuf == 0 ) ; MapViewOfFile Failed
         return

      this.Name     := Name
      this.hMapFile := hMapFile
      this.pBuf     := pBuf
      this.BufSize  := BufSize
      
   }
   

   
   Write(String="") {    
      ; ommiting the String param has "delete all" effect
      if (StrLen(String)*(A_Isunicode ? 2 : 1) <= this.BufSize) {
         Num := StrPut(String, this.pBuf)
         return Num
      }
   }
   
   
   
   Read() {
      return StrGet(this.pBuf)
   }
   
   

   __Delete() {
      DllCall("UnmapViewOfFile", "Ptr", this.pBuf), DllCall("CloseHandle", "Ptr", this.hMapFile)
   }



}



c7aesa7r
Posts: 209
Joined: 02 Jun 2016, 21:09

Re: How to send a pBitmap between scripts?

Post by c7aesa7r » 01 Jun 2022, 19:27

I read the stackoverflow question, but I'm still confuse these concepts are new territory to me

To start, the way im writing the bitmap in the first script is correctly?
Shouldn't be numput? and in the second numget?

malcev
Posts: 1769
Joined: 12 Aug 2014, 12:37

Re: How to send a pBitmap between scripts?

Post by malcev » 01 Jun 2022, 19:31

I do not have ready code.
So You need search for autohotkey examples of using this functions by Yourself.

iseahound
Posts: 1444
Joined: 13 Aug 2016, 21:04
Contact:

Re: How to send a pBitmap between scripts?

Post by iseahound » 01 Jun 2022, 20:01

Here's something to get you started:

Code: Select all

#include *i ImagePut\ImagePut (for v1).ahk
#singleinstance force


width := 100
height := 100
stride := 100 * 4
size := stride * height
rebeccapurple := 0x55663399

   hMap := DllCall("CreateFileMapping", "Ptr", -1, "Ptr", 0, "uInt", 0x04, "uInt", 0, "uInt", size, "Ptr", 0, "Ptr")
   pMap := DllCall("MapViewOfFile", "Ptr", hMap, "uInt", 0x2, "uInt", 0, "uInt", 0, "uPtr", 0, "Ptr")

loop 4
 if y := A_Index
   loop 100
      if x := A_Index
         NumPut(0xFF00FF00, pMap+4*x+y*height, "uint")



      VarSetCapacity(bi, 40, 0)              ; sizeof(bi) = 40
         NumPut(       40, bi,  0,   "uint") ; Size
         NumPut(    width, bi,  4,   "uint") ; Width
         NumPut(  -height, bi,  8,    "int") ; Height - Negative so (0, 0) is top-left.
         NumPut(        1, bi, 12, "ushort") ; Planes
         NumPut(       32, bi, 14, "ushort") ; BitCount / BitsPerPixel

      hbm := DllCall("CreateDIBSection", "ptr", 0, "ptr", &bi, "uint", 0, "ptr*", pBits:=0, "ptr", hMap, "uint", 0, "ptr")

ImagePutWindow(hbm)

   DllCall("UnmapViewOfFile", "Ptr", pMap)
   DllCall("CloseHandle", "Ptr", hMap)
image.png
image.png (455 Bytes) Viewed 1384 times
This draws a transparent 100x100 rectangle with some green.

c7aesa7r
Posts: 209
Joined: 02 Jun 2016, 21:09

Re: How to send a pBitmap between scripts?

Post by c7aesa7r » 03 Jun 2022, 06:55

@teadrinker
what im doing wrong inside of the function Write(data:="")
when I try to rebuild the bitmap from the function Read the value of size and pData doesn't match

Code: Select all

;====== Test script 1 ======
FM := new FileMapping()
Return



F1::
pToken  := Gdip_Startup()

WinGet, hWnd, Id, ahk_exe notepad.exe
pBitmap := Gdip_BitmapFromHWND(hWnd)



DllCall("gdiplus\GdipGetImageDimension", Ptr, pBitmap, "float*", width, "float*", height)
DllCall("gdiplus\GdipGetImagePixelFormat", "Ptr", pBitmap, "PtrP", pixelFormat)
Gdip_LockBits(pBitmap, 0, 0, width, height, stride, scan0, bitmapData,, pixelFormat)

size := stride * height
str  := "testtest" ;CoCoJson.Dump(Object)



VarSetCapacity(data, dataSize := size + 16 + StrLen(str)*2 + 2, 0)
StrPut(str, &data + size + 16)

DllCall("RtlCopyMemory", "Ptr", &data + 16, "Ptr", scan0, "Ptr", size)
DllCall("Gdiplus\GdipBitmapUnlockBits", "UPtr", pBitmap, "UPtr", &BitmapData) 



NumPut(pixelFormat, data)
NumPut(width, data, 4)
NumPut(height, data, 8)
NumPut(stride, data, 12, "UInt")



VarSetCapacity(COPYDATASTRUCT, A_PtrSize*3, 0)
NumPut(dataSize, COPYDATASTRUCT, A_PtrSize)
NumPut(&data, COPYDATASTRUCT, A_PtrSize*2)



FileAppend, size: %size%`n,*
FM.Write(&COPYDATASTRUCT)
Return




F2::MsgBox,,, % FM.Read(), 1
;F3::FM.Write()    ; delete all
;F4::FM:=""
;F5::MsgBox,,, % IsObject(FM), 1
;Esc::ExitApp




Class FileMapping {
   
   __New(Name="Global\MyFileMappingObject", BufSize=10000) {    
      
      ; Opens existing or creates new file mapping object
      static INVALID_HANDLE_VALUE := -1, PAGE_READWRITE := 0x4, FILE_MAP_ALL_ACCESS := 0xF001F
      
      hMapFile := DllCall("OpenFileMapping", "Ptr", FILE_MAP_ALL_ACCESS, "Int", 0, "Str", Name)
      
      if ( hMapFile == 0 ) {        
         ; OpenFileMapping Failed - file mapping object doesn't exist - that means we have to create it
         hMapFile := DllCall("CreateFileMapping", "Ptr", INVALID_HANDLE_VALUE, "Ptr", 0, "Int", PAGE_READWRITE, "Int", 0, "Int", BufSize, "Str", Name)
         if ( hMapFile == 0 )    ; CreateFileMapping Failed
               return
      }

      pBuf := DllCall("MapViewOfFile", "Ptr", hMapFile, "Int", FILE_MAP_ALL_ACCESS, "Int", 0, "Int", 0, "Ptr", BufSize)
      
      if ( pBuf == 0 ) ; MapViewOfFile Failed
         return

      this.Name     := Name
      this.hMapFile := hMapFile
      this.pBuf     := pBuf
      this.BufSize  := BufSize
      
   }
   

   
   Write(data:="") {   

      ;n := NumPut(&data, this.pBuf) 
      this.pBuf := &data
      return ;n

      /*
      ; ommiting the String param has "delete all" effect
      if (StrLen(String)*(A_Isunicode ? 2 : 1) <= this.BufSize) {
         Num := StrPut(String, this.pBuf)
         return Num
      }
      */
   }



   Read() {

      static flags := HEAP_ZERO_MEMORY := 0x00000008
         , hHeap := DllCall("GetProcessHeap", "Ptr")

      size  := NumGet(this.pBuf + A_PtrSize, "UInt")
      pData := NumGet(this.pBuf + A_PtrSize*2)

      pHeap := DllCall("HeapAlloc", "Ptr", hHeap, "UInt", flags, "UPtr", size, "Ptr")
      DllCall("RtlCopyMemory", "Ptr", pHeap, "Ptr", pData, "Ptr", size)

      return ;StrGet(this.pBuf)

   }



   CreateBitmapFromData(hHeap, pImageData) {

      pixelFormat := NumGet(pImageData+0, "UInt")
      width       := NumGet(pImageData+4, "UInt")
      height      := NumGet(pImageData+8, "UInt")
      stride      := NumGet(pImageData+12,"UInt")
      str         := StrGet(pImageData + 16 + stride*height)


      
      DllCall("gdiplus\GdipCreateBitmapFromScan0", "Int", width, "Int", height, "Int", stride, "Int", pixelFormat, "Ptr", pImageData + 16, "PtrP", pBitmap)

      DllCall("gdiplus\GdipCreateHBITMAPFromBitmap", "Ptr", pBitmap, "PtrP", hbm, "Int", 0xffffffff)

      DllCall("HeapFree", "Ptr", hHeap, "UInt", 0, "Ptr", pImageData)
      DllCall("gdiplus\GdipDisposeImage", "Ptr", pBitmap)

   }



   __Delete() {
      DllCall("UnmapViewOfFile", "Ptr", this.pBuf), DllCall("CloseHandle", "Ptr", this.hMapFile)
   }

}


teadrinker
Posts: 4326
Joined: 29 Mar 2015, 09:41
Contact:

Re: How to send a pBitmap between scripts?

Post by teadrinker » 03 Jun 2022, 07:23

I think you have to copy data (RtlMoveMemory) to pBuf, but not change the pBuf to a new one. However, I have no experience with file mapping, so I'm not sure that what you are doing is correct.

Post Reply

Return to “Ask for Help (v1)”