How to send a pBitmap between scripts?
-
- Posts: 4331
- Joined: 29 Mar 2015, 09:41
- Contact:
Re: How to send a pBitmap between scripts?
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.
Re: How to send a pBitmap between scripts?
Just as curiosity, why does it need to be in b64 string?teadrinker wrote: ↑23 May 2022, 12:19Only 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.
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")
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,"??")
-
- Posts: 4331
- Joined: 29 Mar 2015, 09:41
- Contact:
Re: How to send a pBitmap between scripts?
NumPut is only for numbers, for strings there is StrPut(). If you want to send some text together with image data,
the sender:
the receiver:
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
...
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%
}
...
Re: How to send a pBitmap between scripts?
Thank you teadrinker! helped a lot!
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?
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?
-
- Posts: 31
- Joined: 21 Jan 2020, 22:01
Re: How to send a pBitmap between scripts?
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?
-
- Posts: 4331
- Joined: 29 Mar 2015, 09:41
- Contact:
Re: How to send a pBitmap between scripts?
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.dipstick5000 wrote: ↑Or maybe just copy the image to your clipboard and get it with the second script?
I currently have no such experience, so you just have to test it.
-
- Posts: 31
- Joined: 21 Jan 2020, 22:01
Re: How to send a pBitmap between scripts?
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.
-
- Posts: 31
- Joined: 21 Jan 2020, 22:01
Re: How to send a pBitmap between scripts?
Wait, isn't the clipboard a variable? And you have a problem using variables? Not sure what's going on here.
Re: How to send a pBitmap between scripts?
You can share bitmap with CreateDIBSection with file mapping.
https://docs.microsoft.com/en-us/windows/win32/api/wingdi/nf-wingdi-createdibsection
https://docs.microsoft.com/en-us/windows/win32/api/wingdi/nf-wingdi-createdibsection
-
- Posts: 4331
- Joined: 29 Mar 2015, 09:41
- Contact:
Re: How to send a pBitmap between scripts?
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.dipstick5000 wrote: ↑Not sure what's going on here.
-
- Posts: 4331
- Joined: 29 Mar 2015, 09:41
- Contact:
Re: How to send a pBitmap between scripts?
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.
Re: How to send a pBitmap between scripts?
I am too.I didn't go into it
Here is some thoughts
https://stackoverflow.com/questions/2536331/most-efficient-way-to-send-images-across-processes
Re: How to send a pBitmap between scripts?
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.
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.
Re: How to send a pBitmap between scripts?
I have been able to share strings using file mapping
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
could you point out how to share bitmaps as you described?malcev wrote: ↑23 May 2022, 16:40You can share bitmap with CreateDIBSection with file mapping.
https://docs.microsoft.com/en-us/windows/win32/api/wingdi/nf-wingdi-createdibsection
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)
}
}
Re: How to send a pBitmap between scripts?
https://stackoverflow.com/questions/2493372/how-to-properly-recreate-bitmap-that-was-previously-shared-by-createfilemappingcould you point out how to share bitmaps as you described?
Re: How to send a pBitmap between scripts?
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?
To start, the way im writing the bitmap in the first script is correctly?
Shouldn't be numput? and in the second numget?
Re: How to send a pBitmap between scripts?
I do not have ready code.
So You need search for autohotkey examples of using this functions by Yourself.
So You need search for autohotkey examples of using this functions by Yourself.
Re: How to send a pBitmap between scripts?
Here's something to get you started:
This draws a transparent 100x100 rectangle with some green.
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)
Re: How to send a pBitmap between scripts?
@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
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(©DATASTRUCT)
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)
}
}
-
- Posts: 4331
- Joined: 29 Mar 2015, 09:41
- Contact:
Re: How to send a pBitmap between scripts?
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.