[DllCall / NumGet] How to read the value of a pointer to pointer?

Get help with using AutoHotkey (v1.1 and older) and its commands and hotkeys
swagfag
Posts: 6222
Joined: 11 Jan 2017, 17:59

[DllCall / NumGet] How to read the value of a pointer to pointer?

15 Nov 2018, 06:14

the GDIP lib has this here function:

Code: Select all

; Function				CreateDIBSection
; Description			The CreateDIBSection function creates a DIB (Device Independent Bitmap) that applications can write to directly
;
; w						width of the bitmap to create
; h						height of the bitmap to create
; hdc					a handle to the device context to use the palette from
; bpp					bits per pixel (32 = ARGB)
; ppvBits				A pointer to a variable that receives a pointer to the location of the DIB bit values
;
; return				returns a DIB. A gdi bitmap
;
; notes					ppvBits will receive the location of the pixels in the DIB

CreateDIBSection(w, h, hdc:="", bpp:=32, ByRef ppvBits:=0) {
	Ptr := A_PtrSize ? "UPtr" : "UInt"

	hdc2 := hdc ? hdc : GetDC()
	VarSetCapacity(bi, 40, 0)

	NumPut(w, bi, 4, "uint")
	, NumPut(h, bi, 8, "uint")
	, NumPut(40, bi, 0, "uint")
	, NumPut(1, bi, 12, "ushort")
	, NumPut(0, bi, 16, "uInt")
	, NumPut(bpp, bi, 14, "ushort")

	hbm := DllCall("CreateDIBSection"
					, Ptr, hdc2
					, Ptr, &bi
					, "uint", 0
					, A_PtrSize ? "UPtr*" : "uint*", ppvBits
					, Ptr, 0
					, "uint", 0, Ptr)

	if !hdc
		ReleaseDC(hdc2)
	return hbm
}
from msdn:
ppvBits - A pointer to a variable that receives a pointer to the location of the DIB bit values.
question is, how to read those bit values?
example, nonworking code ive tried:

Code: Select all

CreateDIBSection(A_ScreenWidth, A_ScreenHeight, 0, 32, ppvBits)
MsgBox % ppvBits ; this should be the address of the pointer to the bits, if i understood this right
MsgBox % ptrToBits := NumGet(ppvBits) ; get the address of the bits?
MsgBox % NumGet(ptrToBits, "UInt") ; get the bits?
oif2003
Posts: 214
Joined: 17 Oct 2018, 11:43
Contact:

Re: [DllCall / NumGet] How to read the value of a pointer to pointer?

15 Nov 2018, 10:25

I think you need to do NumGet one more time. I get confused by these, too. This is what I understand:

Code: Select all

;"A pointer to a variable that receives a pointer to the location of the DIB bit values"
bitValue := 1							;the bit value
bitValueLocation := &bitValue			;location of bit value
ptr1 := &bitValueLocation				;pointer to location of bit value
variable := ptr1						;a variable that stores this ptr
ptr2 := &variable						;a pointer to the variable
_bitValue := NumGet(NumGet(NumGet(ptr2, 0, "Ptr"), 0, "Ptr"), 0, "UInt")
swagfag
Posts: 6222
Joined: 11 Jan 2017, 17:59

Re: [DllCall / NumGet] How to read the value of a pointer to pointer?

15 Nov 2018, 17:07

i cant run the example uve provided, in fact, i think it crashes unexpectedly
ive tried to make sense of it, using this diagram. someone more knowledgeable correct it if its wrong
Image
i dont get how NumGet() is supposed to work. even this simple snippet cant get to the original number:

Code: Select all

var := 123
MsgBox % ptr_var := &var ; prints something resembling an address
MsgBox % NumGet(ptr_var, 0, "Int") ; prints something else
MsgBox % NumGet(ptr_var+0, 0, "Int") ; prints something else entirely
this is on ahk x64 unicode, if it makes a difference
User avatar
jeeswg
Posts: 6902
Joined: 19 Dec 2016, 01:58
Location: UK

Re: [DllCall / NumGet] How to read the value of a pointer to pointer?

15 Nov 2018, 17:46

CreateDIBSection function | Microsoft Docs
https://docs.microsoft.com/en-us/window ... dibsection
A pointer to a variable that receives a pointer to the location of the DIB bit values.
So ppvBits will be 'a pointer to the location of the DIB bit values'.
That sounds like ppvBits contains the address where the bit values start.
So vAddr := ppvBits.
Otherwise, vAddr := NumGet(ppvBits+0)
Where vAddr is the address where the bits are.
NumGet uses UPtr by default, so NumGet(MyAddr+0) works for both x64 and x32 scripts. (Although really Ptr should be used.)
NumGet can retrieve a byte (UChar). Use bitwise-and and logical-not (or bitshifts) to get individual bits.
E.g.

Code: Select all

VarSetCapacity(vData, 4, 0)
NumPut(0x04030201, &vData, 0, "UInt")
vAddr := &vData
vByte1 := NumGet(vAddr+0, "UChar")
vByte2 := NumGet(vAddr+1, "UChar")
vByte3 := NumGet(vAddr+2, "UChar")
vByte4 := NumGet(vAddr+3, "UChar")
MsgBox, % Format("{} {} {} {}", vByte1, vByte2, vByte3, vByte4)

vBit1 := !!(vByte1 & 0x80)
vBit2 := !!(vByte1 & 0x40)
vBit3 := !!(vByte1 & 0x20)
vBit4 := !!(vByte1 & 0x10)
vBit5 := !!(vByte1 & 0x8)
vBit6 := !!(vByte1 & 0x4)
vBit7 := !!(vByte1 & 0x2)
vBit8 := !!(vByte1 & 0x1)
MsgBox, % Format("{} {} {} {} {} {} {} {}", vBit1, vBit2, vBit3, vBit4, vBit5, vBit6, vBit7, vBit8)

;alternatively:
vBit1 := (vByte1 & 0x80) >> 7
vBit2 := (vByte1 & 0x40) >> 6
vBit3 := (vByte1 & 0x20) >> 5
vBit4 := (vByte1 & 0x10) >> 4
vBit5 := (vByte1 & 0x8) >> 3
vBit6 := (vByte1 & 0x4) >> 2
vBit7 := (vByte1 & 0x2) >> 1
vBit8 := (vByte1 & 0x1) >> 0
MsgBox, % Format("{} {} {} {} {} {} {} {}", vBit1, vBit2, vBit3, vBit4, vBit5, vBit6, vBit7, vBit8)
Last edited by jeeswg on 16 Nov 2018, 12:49, edited 4 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
Helgef
Posts: 4709
Joined: 17 Jul 2016, 01:02
Contact:

Re: [DllCall / NumGet] How to read the value of a pointer to pointer?

15 Nov 2018, 18:26

swagfag wrote:

Code: Select all

var := 123
MsgBox % ptr_var := &var ; prints something resembling an address
MsgBox % NumGet(ptr_var, 0, "Int") ; prints something else
MsgBox % NumGet(ptr_var+0, 0, "Int") ; prints something else entirely
Please see,

Code: Select all

var := 123
ptr_var := &var 											; ptr_var stores the address of var as a string, similar to ptr := "23423123"
while c := numget(ptr_var, (a_index-1)*2, "ushort")			; this numget operates on the (string) address of ptr_var
	str .= chr(c)
msgbox % str  "`n" ptr_var
str := ""
while c := numget(ptr_var + 0, (a_index-1)*2, "ushort")		; this numget operates on the (string) address of var, which is passed by the expression ptr_var + 0
	str .= chr(c)
msgbox % str  "`n" var
You can see numget VarOrAddress parameter.

This mess is cleaned up in v2 :thumbup:

Cheers.
oif2003
Posts: 214
Joined: 17 Oct 2018, 11:43
Contact:

Re: [DllCall / NumGet] How to read the value of a pointer to pointer?

15 Nov 2018, 18:44

Apologies for the none working snippet. It has been awhile since I've used v1.

Code: Select all

var := 123
ptr_var := &var 
msgbox % format("{:0x}", NumGet(&var + 0, 0, "Int64"))
This gives you the hex value of var in reverse.
For example:
10 => 0x300031 => 01 in ASCII
123 => 0x3300320031 => 321 in ASCII

At least now we know how to interpret the results. I remember doing this awhile back when I used v1 to read memory.

Edit: NVM! Helgef got you!
Helgef
Posts: 4709
Joined: 17 Jul 2016, 01:02
Contact:

Re: [DllCall / NumGet] How to read the value of a pointer to pointer?

16 Nov 2018, 12:20

I see that there was not really an answer to the OPs question. There are indeed two pointers involved here. The first pointer is handled by AHK, here: A_PtrSize ? "UPtr*" : "uint*", ppvBits, this passes the address to a location which can hold an "uptr" sized value, the dll function CreateDIBSection writes the second pointer, that is the pointer to the bits, to this location. When the dll function returns, AHK writes the second pointer to the variable ppvBits. So to get the bits, use value := numget(ppvBits+0, ...). See dllcall for details on type*. Now, if you instead use "ptr", &ppvBits to pass the first pointer to the dll function, you will need to use an additional numget, this is an exercise for the interested reader.

Cheers.
swagfag
Posts: 6222
Joined: 11 Jan 2017, 17:59

Re: [DllCall / NumGet] How to read the value of a pointer to pointer?

17 Nov 2018, 09:30

it is as u say, Helgef
initially i was using this:

Code: Select all

#Include Gdip_All.ahk ; https://github.com/mmikeww/AHKv2-Gdip/blob/master/Gdip_All.ahk
pToken := Gdip_Startup()
OnExit("Quit")

VarSetCapacity(ppvBits, 1000000, 0)
hbm := CreateDIBSection(A_ScreenWidth, A_ScreenHeight, "", 32, ppvBits)
MsgBox % ppvBits ; looks like an address, alright
MsgBox % NumGet(ppvBits+0, 0, "UChar") ; always prints '0', regardless of offset
ExitApp

Esc::ExitApp

Quit() {
    global
    
	DeleteObject(hbm)
	Gdip_Shutdown(pToken)
}
what threw me off was the fact that it always returned 0. i checked in VS what the memory looked like with:

Code: Select all

#include <windows.h>
#include <objidl.h>
#include <gdiplus.h>
using namespace Gdiplus;
#pragma comment (lib,"Gdiplus.lib")


int main()
{
	HDC hdc = GetDC(NULL);

	unsigned char* ppvBits;

	BITMAPINFO bi;
	ZeroMemory(&bi, sizeof(BITMAPINFO));
	bi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
	bi.bmiHeader.biWidth = 1920;
	bi.bmiHeader.biHeight = 1080;
	bi.bmiHeader.biPlanes = 1;
	bi.bmiHeader.biBitCount = 32;

	HBITMAP hbm;
	hbm = CreateDIBSection(hdc, &bi, DIB_RGB_COLORS, (VOID**)&ppvBits, NULL, 0); //ppvBits point to a zeroed out block of memory

	DIBSECTION dib;
	GetObject(hbm, sizeof(DIBSECTION), &dib); // dib->dsBM->bmBits points to another zeroed out block of memory

	return 0;
}
and sure enough the address seems correct but its all zeroed out, so i think im misusing CreateDIBSection here. I got it to work using this:

Code: Select all

#Include Gdip_All.ahk ; https://github.com/mmikeww/AHKv2-Gdip/blob/master/Gdip_All.ahk
pToken := Gdip_Startup()
OnExit("Quit")

chdc := CreateCompatibleDC()
hbm := CreateDIBSection(A_ScreenWidth, -A_ScreenHeight, chdc, 32, ppvBits) ; -h to flip the bitmap right way up
obm := SelectObject(chdc, hbm)
hhdc := GetDC()
BitBlt(chdc, 0, 0, A_ScreenWidth, A_ScreenHeight, hhdc, 0, 0)

b := hex(NumGet(ppvBits+0, 0, "UChar"))
g := hex(NumGet(ppvBits+0, 1, "UChar"))
r := hex(NumGet(ppvBits+0, 2, "UChar"))
a := hex(NumGet(ppvBits+0, 3, "UChar"))

MsgBox,
(
Pixel at (0,0)

B: %b%
G: %g%
R: %r%
A: %a%
)
ExitApp
Esc::ExitApp

Quit() {
	global

	SelectObject(chdc, obm)
	DeleteObject(hbm)
	DeleteDC(hhdc)
	DeleteDC(chdc)
	Gdip_Shutdown(pToken)
}

hex(n) {
	return Format("0x{:X}", n)
}
but i was hoping to avoid having to blit the pixels beforehand. i thought CreateDIBSection wouldve already had the pixels of whatever DC(ie, the screen) was provided to it. maybe it does, but in a different format?
swagfag
Posts: 6222
Joined: 11 Jan 2017, 17:59

Re: [DllCall / NumGet] How to read the value of a pointer to pointer?

21 Nov 2018, 11:18

turns out CreateDIBSection works fine as it is, it's just that along with CreateCompatibleDC they return a DC of the desired format and a blank bitmap(hence, the zeroed-out memory i was looking at, initially). so there's no getting around not blitting the pixels.
at this point, it would be just easier to go the LockBits route instead, it's only marginally slower due to all the error checking
User avatar
jeeswg
Posts: 6902
Joined: 19 Dec 2016, 01:58
Location: UK

Re: [DllCall / NumGet] How to read the value of a pointer to pointer?

21 Nov 2018, 11:47

I thought I'd link this script, in case it's useful at all. Cheers.
Gdip: image binary data to hex string for OCR - AutoHotkey Community
https://autohotkey.com/boards/viewtopic.php?f=5&t=35339
homepage | tutorials | wish list | fun threads | donate
WARNING: copy your posts/messages before hitting Submit as you may lose them due to CAPTCHA

Return to “Ask for Help (v1)”

Who is online

Users browsing this forum: RandomBoy and 253 guests