recreating AHK v2's Buffer object

Get help with using AutoHotkey (v1.1 and older) and its commands and hotkeys
User avatar
jeeswg
Posts: 6902
Joined: 19 Dec 2016, 01:58
Location: UK

Re: commands as functions (AHK v2 functions for AHK v1)

31 May 2019, 14:30

I now have 4 versions of the Buffer class, here:
commands as functions (AHK v2 functions for AHK v1) - Page 3 - AutoHotkey Community
https://autohotkey.com/boards/viewtopic.php?f=37&t=29689&p=279037#p279037
Any comments are welcome.

- ObjSetCapacity is of key importance, documented here:
Basic Object - Methods & Properties | AutoHotkey
https://autohotkey.com/docs/objects/Object.htm#SetCapacity
otherwise all existing data is preserved
- ObjSetCapacity is designed for strings, but it appears that all data, including binary data, is preserved when changing the capacity.
- When the capacity is increased, the added capacity is *not* zero initialised, however, this can be done via ObjGetAddress, and DllCall with the RtlFillMemory Winapi function.
homepage | tutorials | wish list | fun threads | donate
WARNING: copy your posts/messages before hitting Submit as you may lose them due to CAPTCHA
swagfag
Posts: 6222
Joined: 11 Jan 2017, 17:59

Re: commands as functions (AHK v2 functions for AHK v1)

31 May 2019, 20:52

Code: Select all

Rect := makeRect(10, 10, 200, 200)
MsgBox % "left: " NumGet(Rect.Ptr, 0, "Int")
	  . "`ntop: " NumGet(Rect.Ptr, 4, "Int")
	  . "`nright: " NumGet(Rect.Ptr, 8, "Int")
	  . "`nbottom: " NumGet(Rect.Ptr, 12, "Int")

makeRect(x1, y1, x2, y2) {
	Rect := BufferAlloc(16)

	NumPut(x1, Rect.Ptr, 0, "Int")
	NumPut(y1, Rect.Ptr, 4, "Int")
	NumPut(x2, Rect.Ptr, 8, "Int")
	NumPut(y2, Rect.Ptr, 12, "Int")

	return Rect
}

BufferAlloc(Size) {
	return new BufferObject(Size)
}

class BufferObject
{
	static HEAP_ZERO_MEMORY := 0x00000008
	static hDefaultHeap := BufferObject.__GetProcessHeap()

	Ptr := ""
	Size_ := ""

	Size[]
	{
		get {
			return this.Size_
		}

		set {
			this.__AssertSizeValid(value, "BufferObject Size[] Set")
			this.Ptr := this.__HeapReAlloc(value)
			this.Size_ := this.__HeapSize()

			return value
		}
	}

	__New(Size) {
		this.__AssertSizeValid(Size, "BufferAlloc")
		this.Ptr := this.__HeapAlloc(Size)
		this.Size_ := this.__HeapSize()
	}

	__Delete() {
		this.__HeapFree()
	}

	__AssertSizeValid(Size, caller) {
		if Size is not number
			this.__Error(caller, "Invalid parameter #1: NaN")

		if (Size < 0)
			this.__Error(caller, "Invalid parameter #1: Negative number")
	}

	__GetProcessHeap() {
		if !hDefaultHeap := DllCall("GetProcessHeap", "Ptr")
			this.__Error("GetProcessHeap")

		return hDefaultHeap
	}

	__HeapAlloc(dwBytes) {
		if !pMem := DllCall("HeapAlloc", "Ptr", this.hDefaultHeap, "UInt", this.HEAP_ZERO_MEMORY, "UPtr", dwBytes, "Ptr")
		{
			; If the function fails, it does not call SetLastError.
			; An application cannot call GetLastError for extended error information.
			this.__Error("HeapAlloc", "", false)
		}

		return pMem
	}

	__HeapReAlloc(dwBytes) {
		if !pMem := DllCall("HeapReAlloc", "Ptr", this.hDefaultHeap, "UInt", this.HEAP_ZERO_MEMORY, "Ptr", this.Ptr, "UPtr", dwBytes, "Ptr")
		{
			; If the function fails, it does not call SetLastError.
			; An application cannot call GetLastError for extended error information.
			this.__Error("HeapReAlloc", "Original memory not freed, original pointer still valid", false)
		}

		return pMem
	}

	__HeapSize() {
		return DllCall("HeapSize", "Ptr", this.hDefaultHeap, "UInt", 0, "Ptr", this.Ptr, "UPtr")
	}

	__HeapFree() {
		if !this.Ptr
			this.__Error("HeapFree", "Pointer to memory block was NULL")

		if !DllCall("HeapFree", "Ptr", this.hDefaultHeap, "UInt", 0, "Ptr", this.Ptr, "Int")
			this.__Error("HeapFree")
	}

	__Error(calledFunc, extraInfo := "", showLastErr := true) {
		static ERROR_TEMPLATE := "
		(LTrim
			{} failed{}
			Error Level: {}
			{}
		)"

		errorMsg := Format(ERROR_TEMPLATE
			, calledFunc
			, (extraInfo != "") ? " (" extraInfo ")" : "."
			, ErrorLevel
			, showLastErr ? "Last Error: " A_LastError : "")

		throw Exception(errorMsg, -4)
	}

}
lexikos
Posts: 9621
Joined: 30 Sep 2013, 04:07
Contact:

Re: commands as functions (AHK v2 functions for AHK v1)

31 May 2019, 22:53

SetCapacity's two parameter mode was added when _GetAddress was added in L45, and is primarily intended for binary data. I presume I did not spell this out in the documentation because users using it for that purpose should not need hand holding. If it was intended for strings only, it would accept a character count, not a byte count. There's also the fact that it preserves binary data, as you've noted.

SetCapacity sets the size of a buffer, in bytes. GetAddress gets the buffer's address. It is called a string buffer because that's what the buffer itself exists for, and how the value is interpreted if you refer to the field directly - perhaps because there is no binary data type in AutoHotkey v1, as you know. You can do whatever you want with that memory space. This is no different to the use of VarSetCapacity for binary data - there's no special provision for binary data there either, just a lot more documentation.
User avatar
jeeswg
Posts: 6902
Joined: 19 Dec 2016, 01:58
Location: UK

Re: commands as functions (AHK v2 functions for AHK v1)

20 Jun 2019, 06:12

@lexikos: Thanks for the info.

@Helgef: I had a problem with CallbackFree, here is the error message, and a quick fix:
Warning in #include file "C:\Users\me\Desktop\JEEAHK1FC.ahk":
This variable has not been assigned a value.

Specifically: callback_cache (a static variable)

Code: Select all

	;modified line (before):
	static callback_cache := []	; Contains all callback objects.
	;modified line (after):
	static callback_cache, dummy	; Contains all callback objects.

	;added lines:
	if !VarSetCapacity(dummy)
		callback_cache := [], dummy := "dummy"
Last edited by jeeswg on 20 Jun 2019, 11:04, edited 3 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: commands as functions (AHK v2 functions for AHK v1)

20 Jun 2019, 09:37

jeeswg wrote:
20 Jun 2019, 06:12
I had a problem with CallbackFree, here is the error message,
My code doesn't need a fix, you need to manage your includes.
a quick fix:

Code: Select all

	;modified line:
	static callback_cache := [], dummy := "dummy"	; Contains all callback objects.

	;added lines:
	if !VarSetCapacity(dummy)
		callback_cache := []
Perhaps it was quick, but it certainly is no fix :lolno:.
Helgef
Posts: 4709
Joined: 17 Jul 2016, 01:02
Contact:

Re: commands as functions (AHK v2 functions for AHK v1)

20 Jun 2019, 11:17

static variables wrote:Static var := expression is supported. All such expressions are evaluated immediately before the script's auto-execute section in the order they are encountered in the script.
#include wrote:Causes the script to behave as though the specified file's contents are present at this exact position.
So,

Code: Select all

#include callbackcreate.ahk
#include spaghetti.ahk
User avatar
nnnik
Posts: 4500
Joined: 30 Sep 2013, 01:01
Location: Germany

Re: commands as functions (AHK v2 functions for AHK v1)

20 Jun 2019, 11:27

You shouldnt reference other libraries inside a static scope keyword unless you know what you are doing.
Recommends AHK Studio
User avatar
jeeswg
Posts: 6902
Joined: 19 Dec 2016, 01:58
Location: UK

Re: commands as functions (AHK v2 functions for AHK v1)

20 Jun 2019, 13:34

- @Helgef: Unfortunately, your CallbackCreate/CallbackFree functions do need a fix. The functions should work, regardless of where they're placed. (AHK v2 behaviour should be replicated as far as possible.)
- My edited quick fix is sufficient.
- I will also review the use of 'static' in MsgBox and Type.

- (You said 'no fix', but knew that the fix was in principle correct. You cited spaghetti code, but the problems with your functions could affect any, even trivial, scripts. I would ask you to not be misleading, and to not act in bad faith.)

- @nnnik: The problem occurs when you do:
static pFunc := CallbackCreate("MyFunc")
in a function located above the CallbackCreate/CallbackFree custom backports.
- It's not the user's fault.
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: Bing [Bot], Darkmaster006, OrangeCat and 167 guests