How to send a pBitmap between scripts?

Get help with using AutoHotkey (v1.1 and older) and its commands and hotkeys
c7aesa7r
Posts: 209
Joined: 02 Jun 2016, 21:09

Re: How to send a pBitmap between scripts?

03 Jun 2022, 07:59

Now the script is able to 'reconstruct' the bitmap
F1 to write the bitmap to the map
F2 to rebuild (F2 only for test as it will dispose of the bitmap, it works correctly as long its the same script)

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:: FM.Read()
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:="") {   
      this.pBuf := data
   }



   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)

      this.CreateBitmapFromData(hHeap, pHeap)
      return

   }



   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)

      ; Just for test
      Gui, Add, Picture, x0 y0, % "HBITMAP:" hbm
      Gui, Show

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

   }



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

}


however, when I start another script with the same code (you need to change F2 to another key, F3:: FM.Read())
and hit F3, the values inside of the function Read size and pdata are 0

Code: Select all

   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)

      this.CreateBitmapFromData(hHeap, pHeap)
      return

   }
maybe need to modify something in the function when its being read from a different script?
fabricio234
Posts: 122
Joined: 06 Mar 2020, 21:48

Re: How to send a pBitmap between scripts?

03 Jun 2022, 08:26

teadrinker wrote:
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.
@c7aesa7r he meant copy the data to pbuf and not assign a new one which is exactly what you did
try in the write function

Code: Select all

   Write(data:="") {   
      NumPut(data, this.pBuf, A_PtrSize)
   }
c7aesa7r
Posts: 209
Joined: 02 Jun 2016, 21:09

Re: How to send a pBitmap between scripts?

03 Jun 2022, 12:36

fabricio, thanks for pointing it, i modified the code as suggested

write:

Code: Select all

   Write(data:="") {   
      NumPut(data, this.pBuf)
   }
read:

Code: Select all

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

But resulted in the same thing, it works as long its written/readen from the same script, when I read it from a different
size and pdata are 0

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)



MsgBox size: %size%
FM.Write(&COPYDATASTRUCT)
Return




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




Class FileMapping {
   
   __New(Name="Global\MyFileMappingObject", BufSize=100000) {    
      
      ; 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:="") {   
      NumPut(data, this.pBuf)
   }



   Read() {

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

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

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

      this.CreateBitmapFromData(hHeap, pHeap)
      return

   }



   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)

      ; Just for test
      Gui, Add, Picture, x0 y0, % "HBITMAP:" hbm
      Gui, Show

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

   }



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

}

fabricio234
Posts: 122
Joined: 06 Mar 2020, 21:48

Re: How to send a pBitmap between scripts?

04 Jun 2022, 14:47

Maybe could help:

MapFile() : Maps a file and returns a memory pointer.
viewtopic.php?t=90383

Return to “Ask for Help (v1)”

Who is online

Users browsing this forum: Draken, oktavimark, Spawnova, william_ahk and 276 guests