I'm looking for a way to accomplish something roughly equivalent to LoadPicture(), but instead of loading a bitmap from disk, I want to load one already read into a variable (via FileRead, .RawRead(), or equivalent). I have searched around the forum and read a dozen or so MSDN docs, but haven't been able to come up with anything that works. Any ideas?
Note: When I try to read/write bytes to the variablespace HBITMAP points to after using LoadPicture, AHK crashes. Am I doing it wrong or is it pointing to memory space I'm not allowed to access this way?
TIA,
Sam.
LoadPicture() from variable Topic is solved
Re: LoadPicture() from variable Topic is solved
hBitmap is a handle to a bitmap, it doesn't give you a pointer, to put data into. Are you trying to edit the binary data after loading the image?
This script loads a file into the memory, and then creates an hBitmap based on binary data from a variable. It uses a function by SKAN, which is linked to at the bottom.
Btw if anyone can help: does a pBitmap actually point to data? And has anyone written a pBitmapFromBuffer function? Cheers.
This script loads a file into the memory, and then creates an hBitmap based on binary data from a variable. It uses a function by SKAN, which is linked to at the bottom.
Code: Select all
q:: ;data to hBitmap
RegRead, vPath, HKEY_CURRENT_USER\Control Panel\Desktop, Wallpaper
;==============================
;from file
;hBitmap := LoadPicture(vPath, "", vType)
;==============================
;==============================
;from data
oFile := FileOpen(vPath, "r")
oFile.Pos := 0
vSize := oFile.Length
oFile.RawRead(vData, oFile.Length)
oFile.Close()
;gdip startup
if !DllCall("kernel32\GetModuleHandle", Str,"gdiplus", Ptr)
DllCall("kernel32\LoadLibrary", Str,"gdiplus", Ptr)
VarSetCapacity(GdiplusStartupInput, 16, 0), GdiplusStartupInput := Chr(1)
DllCall("gdiplus\GdiplusStartup", UPtrP,pToken, Ptr,&GdiplusStartupInput, Ptr,0)
hBitmap := GDIPlus_hBitmapFromBuffer(vData, vSize)
;gdip shutdown
DllCall("gdiplus\GdiplusShutdown", UPtr,pToken)
if hModule := DllCall("kernel32\GetModuleHandle", Str,"gdiplus", Ptr)
DllCall("kernel32\FreeLibrary", Ptr,hModule)
;==============================
Loop, 3
{
;SplashImage destroys the hBitmap each time,
;so we need to copy the hBitmap each time
hBitmap2 := DllCall("user32\CopyImage", Ptr,hBitmap, UInt,0, Int,0, Int,0, UInt,0, Ptr)
SplashImage, % "HBITMAP:" hBitmap2, B ;B: borderless
Sleep, 1500
SplashImage, Off
Sleep, 1500
}
return
;==================================================
;GdiPlus_SaveImageToBuffer() - Scripts and Functions - AutoHotkey Community
;https://autohotkey.com/board/topic/85523-gdiplus-saveimagetobuffer/
GDIPlus_hBitmapFromBuffer( ByRef Buffer, nSize ) { ; Last Modifed : 21-Jun-2011
; Adapted version by SKAN www.autohotkey.com/forum/viewtopic.php?p=383863#383863
; Original code by Sean www.autohotkey.com/forum/viewtopic.php?p=147029#147029
hData := DllCall("GlobalAlloc", UInt,2, UInt,nSize )
pData := DllCall("GlobalLock", UInt,hData )
DllCall( "RtlMoveMemory", UInt,pData, UInt,&Buffer, UInt,nSize )
DllCall( "GlobalUnlock" , UInt,hData )
DllCall( "ole32\CreateStreamOnHGlobal", UInt,hData, Int,True, UIntP,pStream )
DllCall( "gdiplus\GdipCreateBitmapFromStream", UInt,pStream, UIntP,pBitmap )
DllCall( "gdiplus\GdipCreateHBITMAPFromBitmap", UInt,pBitmap, UIntP,hBitmap, UInt
,DllCall( "ntdll\RtlUlongByteSwap",UInt
,DllCall( "GetSysColor", Int,15 ) <<8 ) | 0xFF000000 )
DllCall( "gdiplus\GdipDisposeImage", UInt,pBitmap )
DllCall( NumGet( NumGet(1*pStream)+8 ), UInt,pStream ) ; IStream::Release
Return hBitmap
}
homepage | tutorials | wish list | fun threads | donate
WARNING: copy your posts/messages before hitting Submit as you may lose them due to CAPTCHA
WARNING: copy your posts/messages before hitting Submit as you may lose them due to CAPTCHA
Re: LoadPicture() from variable
Hello. Disclaimer: I know very little about gdi(+).
So I would expect a PBITMAP to point to such a stucture. Now, in AHK code, we don't need (can't rather) specify the type of the variables we use, so if you see pBitmap in AHK, don't count on it being a pointer to such a struct . For example, from your code,
Here, pBitmap is not a pointer to the above struct. Instead, it is a pointer to a Bitmap class object. It might have been more appropriate to implement a gdi+ wrapper in an object oriented way, then there might have been less confusion. nnnik has at least started work on it, we'll see what happens.
Source: github and AHK forum. The pointer to the bits is in BITMAP.bits.
Please remember the above disclaimer, I'd be happy to have my statments and my understanding corrected.
Cheers
It depends, I'd say. According to msdn, the BITMAP structure looks like this,does a pBitmap actually point to data?
Code: Select all
typedef struct tagBITMAP {
LONG bmType;
LONG bmWidth;
LONG bmHeight;
LONG bmWidthBytes;
WORD bmPlanes;
WORD bmBitsPixel;
LPVOID bmBits;
} BITMAP, *PBITMAP;
Code: Select all
DllCall( "gdiplus\GdipCreateBitmapFromStream", UInt,pStream, UIntP,pBitmap ) ; Maybe use "Ptr" and "PtrP" instead
A HBITMAP is to be consider an opaque pointer, you can't use so freely, you need to use its associated functions/methods. To get the image's bit array from a hBitmap from loadPicture, I have used this function,Note: When I try to read/write bytes to the variablespace HBITMAP points to after using LoadPicture, AHK crashes. Am I doing it wrong or is it pointing to memory space I'm not allowed to access this way?
Code: Select all
_getBitmap(hBitmap){
; Url:
; - https://msdn.microsoft.com/en-us/library/windows/desktop/dd144904%28v=vs.85%29.aspx (GetObject function)
; - https://msdn.microsoft.com/en-us/library/windows/desktop/dd183371(v=vs.85).aspx (BITMAP structure)
/*
typedef struct tagBITMAP {
LONG bmType;
LONG bmWidth;
LONG bmHeight;
LONG bmWidthBytes;
WORD bmPlanes;
WORD bmBitsPixel;
LPVOID bmBits;
} BITMAP, *PBITMAP;
*/
static pBits:=A_PtrSize==4?20:24
local BITMAP,cbBuffer,nBytes
if !(cbBuffer:=DllCall("Gdi32.dll\GetObject", "Ptr", hBitmap, "Int", cbBuffer, "Ptr",0)) ; Get the requiered size of the buffer (BITMAP)
throw Exception("Failed to get the requiered buffer size, ErrorLevel: " ErrorLevel " Last error: " A_LastError ".",-1)
VarSetCapacity(BITMAP,cbBuffer,0)
if !(nBytes:=DllCall("Gdi32.dll\GetObject", "Ptr", hBitmap, "Int", cbBuffer, "Ptr", &BITMAP) == cbBuffer) ; Get the BITMAP
throw Exception("Failed to get the bitmap object, ErrorLevel: " ErrorLevel " Last error: " A_LastError ".",-1)
BITMAP := {type: NumGet(&BITMAP, 0, "Int") ; bmType
,w: NumGet(&BITMAP, 4, "Int") ; bmWidth
,h: NumGet(&BITMAP, 8, "Int") ; bmHeight
,widthBytes: NumGet(&BITMAP, 12, "Int") ; bmWidthBytes
,planes: NumGet(&BITMAP, 16, "UShort") ; bmPlanes
,bitsPixel: NumGet(&BITMAP, 18, "UShort") ; bmBitsPixel
,bits: NumGet(&BITMAP, pBits, "Ptr")} ; bmBits
return BITMAP
}
Please remember the above disclaimer, I'd be happy to have my statments and my understanding corrected.
Cheers
- Brazolek123
- Posts: 187
- Joined: 06 Jun 2016, 16:02
Re: LoadPicture() from variable
This is exactly what I was looking for, thank you! I have noticed that when wrapped in a try-catch statement, RtlUlongByteSwap in the code above throws an exception and returns A4: it needs "CDecl" at the end.jeeswg wrote:This script loads a file into the memory, and then creates an hBitmap based on binary data from a variable. It uses a function by SKAN, which is linked to at the bottom.Code: Select all
q:: ;data to hBitmap RegRead, vPath, HKEY_CURRENT_USER\Control Panel\Desktop, Wallpaper ;============================== ;from file ;hBitmap := LoadPicture(vPath, "", vType) ;============================== ;============================== ;from data oFile := FileOpen(vPath, "r") oFile.Pos := 0 vSize := oFile.Length oFile.RawRead(vData, oFile.Length) oFile.Close() ;gdip startup if !DllCall("kernel32\GetModuleHandle", Str,"gdiplus", Ptr) DllCall("kernel32\LoadLibrary", Str,"gdiplus", Ptr) VarSetCapacity(GdiplusStartupInput, 16, 0), GdiplusStartupInput := Chr(1) DllCall("gdiplus\GdiplusStartup", UPtrP,pToken, Ptr,&GdiplusStartupInput, Ptr,0) hBitmap := GDIPlus_hBitmapFromBuffer(vData, vSize) ;gdip shutdown DllCall("gdiplus\GdiplusShutdown", UPtr,pToken) if hModule := DllCall("kernel32\GetModuleHandle", Str,"gdiplus", Ptr) DllCall("kernel32\FreeLibrary", Ptr,hModule) ;============================== Loop, 3 { ;SplashImage destroys the hBitmap each time, ;so we need to copy the hBitmap each time hBitmap2 := DllCall("user32\CopyImage", Ptr,hBitmap, UInt,0, Int,0, Int,0, UInt,0, Ptr) SplashImage, % "HBITMAP:" hBitmap2, B ;B: borderless Sleep, 1500 SplashImage, Off Sleep, 1500 } return ;================================================== ;GdiPlus_SaveImageToBuffer() - Scripts and Functions - AutoHotkey Community ;https://autohotkey.com/board/topic/85523-gdiplus-saveimagetobuffer/ GDIPlus_hBitmapFromBuffer( ByRef Buffer, nSize ) { ; Last Modifed : 21-Jun-2011 ; Adapted version by SKAN www.autohotkey.com/forum/viewtopic.php?p=383863#383863 ; Original code by Sean www.autohotkey.com/forum/viewtopic.php?p=147029#147029 hData := DllCall("GlobalAlloc", UInt,2, UInt,nSize ) pData := DllCall("GlobalLock", UInt,hData ) DllCall( "RtlMoveMemory", UInt,pData, UInt,&Buffer, UInt,nSize ) DllCall( "GlobalUnlock" , UInt,hData ) DllCall( "ole32\CreateStreamOnHGlobal", UInt,hData, Int,True, UIntP,pStream ) DllCall( "gdiplus\GdipCreateBitmapFromStream", UInt,pStream, UIntP,pBitmap ) DllCall( "gdiplus\GdipCreateHBITMAPFromBitmap", UInt,pBitmap, UIntP,hBitmap, UInt ,DllCall( "ntdll\RtlUlongByteSwap",UInt ,DllCall( "GetSysColor", Int,15 ) <<8 ) | 0xFF000000 ) DllCall( "gdiplus\GdipDisposeImage", UInt,pBitmap ) DllCall( NumGet( NumGet(1*pStream)+8 ), UInt,pStream ) ; IStream::Release Return hBitmap }
That makes more sense. Thank you for the explanation.Helgef wrote:A HBITMAP is to be consider an opaque pointer, you can't use so freely, you need to use its associated functions/methods. To get the image's bit array from a hBitmap from loadPicture, I have used this function,Note: When I try to read/write bytes to the variablespace HBITMAP points to after using LoadPicture, AHK crashes. Am I doing it wrong or is it pointing to memory space I'm not allowed to access this way?Source: github and AHK forum. The pointer to the bits is in BITMAP.bits.Code: Select all
_getBitmap(hBitmap){ ; Url: ; - https://msdn.microsoft.com/en-us/library/windows/desktop/dd144904%28v=vs.85%29.aspx (GetObject function) ; - https://msdn.microsoft.com/en-us/library/windows/desktop/dd183371(v=vs.85).aspx (BITMAP structure) /* typedef struct tagBITMAP { LONG bmType; LONG bmWidth; LONG bmHeight; LONG bmWidthBytes; WORD bmPlanes; WORD bmBitsPixel; LPVOID bmBits; } BITMAP, *PBITMAP; */ static pBits:=A_PtrSize==4?20:24 local BITMAP,cbBuffer,nBytes if !(cbBuffer:=DllCall("Gdi32.dll\GetObject", "Ptr", hBitmap, "Int", cbBuffer, "Ptr",0)) ; Get the requiered size of the buffer (BITMAP) throw Exception("Failed to get the requiered buffer size, ErrorLevel: " ErrorLevel " Last error: " A_LastError ".",-1) VarSetCapacity(BITMAP,cbBuffer,0) if !(nBytes:=DllCall("Gdi32.dll\GetObject", "Ptr", hBitmap, "Int", cbBuffer, "Ptr", &BITMAP) == cbBuffer) ; Get the BITMAP throw Exception("Failed to get the bitmap object, ErrorLevel: " ErrorLevel " Last error: " A_LastError ".",-1) BITMAP := {type: NumGet(&BITMAP, 0, "Int") ; bmType ,w: NumGet(&BITMAP, 4, "Int") ; bmWidth ,h: NumGet(&BITMAP, 8, "Int") ; bmHeight ,widthBytes: NumGet(&BITMAP, 12, "Int") ; bmWidthBytes ,planes: NumGet(&BITMAP, 16, "UShort") ; bmPlanes ,bitsPixel: NumGet(&BITMAP, 18, "UShort") ; bmBitsPixel ,bits: NumGet(&BITMAP, pBits, "Ptr")} ; bmBits return BITMAP }
Re: LoadPicture() from variable
You need to specify the calling convention, DllCall( "ntdll\RtlUlongByteSwap",UInt, DllCall( "GetSysColor", Int,15 ) <<8 , "cdecl"). See dllcall
Re: LoadPicture() from variable
That is what I did. I meant my comment as an observation not a question.Helgef wrote:You need to specify the calling convention, DllCall( "ntdll\RtlUlongByteSwap",UInt, DllCall( "GetSysColor", Int,15 ) <<8 , "cdecl"). See dllcall
Re: LoadPicture() from variable
It seems RtlUlongByteSwap from ntdll in the above DllCall does not play nicely with x64 AHK. I have implemented a workaround to manually swap the bytes with a series of NumGets and NumPuts, but is there a better solution?Helgef wrote:You need to specify the calling convention, DllCall( "ntdll\RtlUlongByteSwap",UInt, DllCall( "GetSysColor", Int,15 ) <<8 , "cdecl"). See dllcall
Re: LoadPicture() from variable
OK, the problem seems to be that the function exists in the x32 version of the dll but not the x64 version. So a solution would be some bit wizardry using & and | to swap the bytes, classic Stack Overflow fare, and one call to NumPut, or to use a dll function, if anyone knows of one.
Code: Select all
;DllListExports() - List of Function exports of a DLL - AutoHotkey Community
;https://autohotkey.com/boards/viewtopic.php?f=6&t=4563
q:: ;list dll functions
if A_Is64bitOS
DllCall("kernel32\Wow64DisableWow64FsRedirection", "Ptr*",0)
else
return
vDllName := "ntdll"
vPath32 := A_Desktop "\z " vDllName " x32.txt"
vPath64 := A_Desktop "\z " vDllName " x64.txt"
vDll32 := "C:\Windows\SysWOW64\" vDllName ".dll" ;SysWOW64 *is* 32-bit
vDll64 := "C:\Windows\System32\" vDllName ".dll"
vText32 := StrReplace(DllListExports(vDll32), "`n", "`r`n")
vText64 := StrReplace(DllListExports(vDll64), "`n", "`r`n")
if !(FileExist, vPath32)
FileAppend, % vText32 "`r`n", % "*" vPath32, UTF-8
if !(FileExist, vPath64)
FileAppend, % vText64 "`r`n", % "*" vPath64, UTF-8
return
Last edited by jeeswg on 20 Aug 2019, 18:52, edited 2 times in total.
homepage | tutorials | wish list | fun threads | donate
WARNING: copy your posts/messages before hitting Submit as you may lose them due to CAPTCHA
WARNING: copy your posts/messages before hitting Submit as you may lose them due to CAPTCHA
Re: LoadPicture() from variable
Hello
ahk v1,
Also,DllCall( "ntdll\RtlUlongByteSwap",UInt, DllCall( "GetSysColor", Int,15 ) <<8 , "cdecl") should probably be, DllCall( "ntdll\RtlUlongByteSwap",UInt, DllCall( "GetSysColor", Int,15 ) <<8 , "cdecl uint").
@ jeeswg, nice script
Cheers
There is another, ahk v2is there a better solution?
Code: Select all
RtlUlongByteSwap64(num){
; Url:
; - https://msdn.microsoft.com/en-us/library/windows/hardware/ff562886(v=vs.85).aspx (RtlUlongByteSwap routine)
; - https://msdn.microsoft.com/en-us/library/e8cxb8tk.aspx (_swab function)
; For example, if the Source parameter value is 0x12345678, the routine returns 0x78563412.
; works on both 32 and 64 bit.
static dest, i := varsetcapacity(dest,4)
DllCall("MSVCRT.dll\_swab", "ptr", &num, "ptr", &dest+2, "int", 2, "cdecl")
,DllCall("MSVCRT.dll\_swab", "ptr", &num+2, "ptr", &dest, "int", 2, "cdecl")
return numget(dest,"uint")
}
; Tested only on these examples,
msgbox(format("0x{:08x}", RtlUlongByteSwap64(0x12345678)))
msgbox(format("0x{:08x}", RtlUlongByteSwap64(0x78563412)))
Code: Select all
RtlUlongByteSwap64(num){
; Url:
; - https://msdn.microsoft.com/en-us/library/windows/hardware/ff562886(v=vs.85).aspx (RtlUlongByteSwap routine)
; - https://msdn.microsoft.com/en-us/library/e8cxb8tk.aspx (_swab function)
; For example, if the Source parameter value is 0x12345678, the routine returns 0x78563412.
; works on both 32 and 64 bit.
; v1 version
static dest, src
static i := varsetcapacity(dest,4) + varsetcapacity(src,4)
numput(num,src,"uint")
,DllCall("MSVCRT.dll\_swab", "ptr", &src, "ptr", &dest+2, "int", 2, "cdecl")
,DllCall("MSVCRT.dll\_swab", "ptr", &src+2, "ptr", &dest, "int", 2, "cdecl")
return numget(dest,"uint")
}
; Tested only on these examples,
msgbox % format("0x{:08x}", RtlUlongByteSwap64(0x12345678))
msgbox % format("0x{:08x}", RtlUlongByteSwap64(0x78563412))
@ jeeswg, nice script
Cheers
Re: LoadPicture() from variable
Cheers Helgef.
Is NumGet and NumPut so bad though, compared to using DllCall?
Is NumGet and NumPut so bad though, compared to using DllCall?
Code: Select all
q:: ;swap bytes (UInt)
vNum := 0x11223344
vNum := (0xFF000000&vNum)>>24 | (0xFF0000&vNum)>>8 | (0xFF00&vNum)<<8 | (0xFF&vNum)<<24
MsgBox, % Format("0x{:X}", vNum)
return
homepage | tutorials | wish list | fun threads | donate
WARNING: copy your posts/messages before hitting Submit as you may lose them due to CAPTCHA
WARNING: copy your posts/messages before hitting Submit as you may lose them due to CAPTCHA
-
- Posts: 77
- Joined: 25 Dec 2018, 10:58
Re: LoadPicture() from variable
[Mod edit: This message by @kazhafeizhale ended up in the reports for this post, so that nobody could see it. Now it has been transformed into a post:]
kazhafeizhale wrote:HI, you may need free memory
Dllcall("GlobalFree", "Ptr", hData)
Re: LoadPicture() from variable
nokazhafeizhale wrote: ↑01 Jan 2022, 05:10[Mod edit: This message by @kazhafeizhale ended up in the reports for this post, so that nobody could see it. Now it has been transformed into a post:]
kazhafeizhale wrote:HI, you may need free memory
Dllcall("GlobalFree", "Ptr", hData)
DllCall( "ole32\CreateStreamOnHGlobal", UInt,hData, Int,True, UIntP,pStream )
DllCall( NumGet( NumGet(1*pStream)+8 ), UInt,pStream ) ; IStream::ReleaseA value that indicates whether the underlying handle for this stream object should be automatically freed when the stream object is released. If set to FALSE, the caller must free the hGlobal after the final release. If set to TRUE, the final release will automatically free the underlying handle.