Jump to content

Sky Slate Blueberry Blackcurrant Watermelon Strawberry Orange Banana Apple Emerald Chocolate
Photo

[Class _Struct]+[Func sizeof] ! updated 01.04.12 ! ++AHKv2


  • Please log in to reply
219 replies to this topic
hoppfrosch
  • Members
  • 399 posts
  • Last active: Feb 26 2016 05:31 AM
  • Joined: 25 Jan 2006
Hi,

I tried out latest struct.ahk with the examples given in the first topic of this thread - and found out, that a few of them don't work with me. Of course I redownloaded (hopefully) ALL used libraries in their current versions (esp. LowLevel ...)

Following examples do not work:
[*:a3wbbbdv]Predefinition of structures
=> MsgBox containing "1" pops up, after closing this box AHK crashes ...[*:a3wbbbdv]Pointer example
=> MsgBox containing "AutoHotKey" pops up, after closing this box AHK crashes ...[*:a3wbbbdv]AHK Structures Example
=> AHK crashes immediately after startup[*:a3wbbbdv]Show original name of variables in a function
=> AHK crashes immediately after startupAs expected the examples which only work with AHK_H do not work.

Your TT-Library/example also crashes immediately after starting using this version of struct.

;     (.)~(.)   
;    (-------)                                    
;---ooO-----Ooo---------------------------------------------------
;    Hoppfrosch  - AHK 1.1.00 Unicode 32bit on Win7 Ultimate
;-----------------------------------------------------------------                        
;    ( )   ( )                            
;    /|\   /|\


HotKeyIt
  • Moderators
  • 7439 posts
  • Last active: Jun 22 2016 09:14 PM
  • Joined: 18 Jun 2008
Thanks hoppfrosch, I have uploaded fixed version and changed AHK Structures example to use AutoHotkey_H only since LowLevel cannot be used in 1.1.00.00.

Lexikos
  • Administrators
  • 9844 posts
  • AutoHotkey Foundation
  • Last active:
  • Joined: 17 Oct 2006

Note :!: You will need AutoHotkey_L++ and FileGetversionInfo_AW() to use these functions.

I must be missing something. Surely you can just use A_AhkVersion? I would think your code may fail if the script is compiled, since A_AhkPath refers to a potentially irrelevant file.
NumPut(pbin,pFunc+A_PtrSize*([color=red]FileGetVersionInfo_AW(A_AhkPath,"FileVersion")[/color]>="1.1.00.00"?2:1))


Lexikos
  • Administrators
  • 9844 posts
  • AutoHotkey Foundation
  • Last active:
  • Joined: 17 Oct 2006
Production scripts should not rely on undocumented structures internal to AutoHotkey, such as Var or Func. That means production scripts should not rely on Struct(), since it relies on those structures. Here's a perfect example of why:

I just wasted a considerable amount of time debugging a script because it kept crashing before even getting to the continuation section. It only crashed while I was debugging the script. Eventually I figured out that Struct_getVar() is called automatically at startup by a static initializer, and that this function has been updated for v1.1 incorrectly. Furthermore, it crashes in the debugger because the debugger assumes that the user-defined function which just returned is really still a user-defined function and therefore has a valid mJumpToLine. It doesn't, since the script just overwrote it.

That it would've needed an update (and will need updates in future) to continue working is reason enough to avoid using this script.

HotKeyIt, please ask yourself "Do I really need to use these risky methods?"

For educational purposes, I'll show you how to fix the function. However, I urge you to instead remove it from the library. I think you're only using it to differentiate between strings and numbers - this is error-prone to begin with due to the nature of AutoHotkey v1's read-caching. (That is, x:=y+0 and x:=y . "" affect not only the numeric/string status of x, but also of y.) So I suggest you give up that "feature".
[color=red]-[/color] NumPut(1,pFunc+[color=red]49+(A_PtrSize=4?0:24)[/color],0,"char")
[color=red]-[/color] NumPut(pbin,pFunc+A_PtrSize*([color=red]FileGetVersionInfo_AW(A_AhkPath,"FileVersion")[/color]>="1.1.00.00"?2:1))
[color=red]- return pbin[/color]
[color=green]+[/color] NumPut(1,pFunc+[color=green]25+((5+(A_AhkVersion<"1.1.00.00"))*A_PtrSize)[/color],0,"char")
[color=green]+[/color] NumPut(pbin,pFunc+A_PtrSize*([color=green]A_AhkVersion[/color]>="1.1.00.00"?2:1))
If the function uses "return", the debugger searches for its block-end in order to potentially stop there (if stepping, or if a breakpoint is on that line) to allow the user to inspect variables before the function returns. This generally crashes the program because the function has replaced its mJumpToLine with a pointer to machine code.

HotKeyIt
  • Moderators
  • 7439 posts
  • Last active: Jun 22 2016 09:14 PM
  • Joined: 18 Jun 2008
Thank you very much Lexikos for your comments, I agree with you and will remove that functionality, I will try to find another way to allow automatic string storage.

HotKeyIt
  • Moderators
  • 7439 posts
  • Last active: Jun 22 2016 09:14 PM
  • Joined: 18 Jun 2008
I have rewritten old Struct() function to a class _Struct.
Also a new function sizeof has been added to calculate size of structures regardless if it is a definition or class object.

The syntas for initialization is slightly changed.
MyStruct := new _Struct(_RECT)
Also initialization of fields is optionally supported.
When second parameter is a pointer, specify the initialization in third parameter.
RC := new _Struct(_RECT,{left:100,right:10})
RC := new _Struct(_RECT,&existingRC,{left:100,right:10})
You can receive address to keys now as well using MyStruct.key[], for pointers address will be returned instead.

LPTSTR, CHAR, UCHAR, TCHAR ... will return string/character now.
To find out the value of CHAR you can use Asc function.
To find out address of string you have to use NumGet.

I have also updated all examples.

EDIT:
smal fix for pointers like "char *mychar" ...
To assign new pointer to such a field use [""]
MyStruct.mychar[""] := new_pointer
;so this works the same as for main structure but "" has to be supplied
;for main structure [] must be used !!!
MyStruct[]:=new_pointer

Following further test scripts.
#include <_Struct>
#include <sizeof>
; Some random structures for testing
_RECT := "left,top,right,bottom"
_POINT := "x,y"
_POINTS := "Short x,Short y"

; User defined structure
_MYSTRUCT := "
(
	DWORD test,test1,test2;
	union {
		struct {
			Int a,b;
		}
		INT64 item;
	}
)"

; General Tests, assign pointer, sizeof(),create with pointer ...
	; Check sizeof() function
		RESULT .= (sizeof(_FILETIME)=8 && sizeof(_SYSTEMTIME)=32 && sizeof(_WIN32_FIND_DATA)=(A_IsUnicode?592:318)) "`tsizeof() function.`n"
	; Create Structure from existing memory
		VarSetCapacity(MEM,16),NumPut(100,MEM,0,"UInt"),NumPut(200,MEM,4,"UInt"),NumPut(300,MEM,8,"UInt"),NumPut(400,MEM,12,"UInt")
		RECT := new _Struct(_RECT,&MEM)
		RESULT .= (RECT.left=100 && RECT.top=200 && RECT.right=300 && RECT.bottom=400) "`tCreate Struct using existing memory.`n"
	; Assign new Pointer to Structure
		VarSetCapacity(MEM2,16),NumPut(10,MEM2,0,"UInt"),NumPut(20,MEM2,4,"UInt"),NumPut(30,MEM2,8,"UInt"),NumPut(40,MEM2,12,"UInt")
		RECT[] := &MEM2
		RESULT .= (RECT.left=10 && RECT.top=20 && RECT.right=30 && RECT.bottom=40) "`tAssign new pointer to Structure.`n"
		
; RECT
	RECT := new _Struct(_RECT)
	RECT.left := 100,RECT.top:=200,RECT.right:=300,RECT.bottom:=400
	RESULT .= (RECT.left=100 && RECT.top=200 && RECT.right=300 && RECT.bottom=400) "`tRECT structure.`n"
	RECT := ""
; POINTS
	POINTS := new _Struct(_POINTS),POINTS.x:=100,POINTS.y:=200
	RESULT .= (POINTS.x=100 && POINTS.y=200) "`tPOINTS structure.`n"
	POINTS := ""
; MYSTRUCT
	MYSTRUCT:=new _Struct(_MYSTRUCT)
	MYSTRUCT.test:=1,MYSTRUCT.test1:=2,MYSTRUCT.a:=10,MYSTRUCT.b:=20
	RESULT .= (MYSTRUCT.test=1 && MYSTRUCT.test1=2 && MYSTRUCT.a=10 && MYSTRUCT.b=20) "`tMYSTRUCT (User defined structure).`n"

; Initialize members on creation
	POINTS := new _Struct(_POINTS,{x:100,y:200})
	RESULT .= (POINTS.x=100 && POINTS.y=200) "`tPOINTS structure + initialization on creation.`n"
	POINTS := ""
MsgBox % RESULT
ExitApp
Following Requires AutoHotkey_H:
#include <_Struct>

; AHK Structures
_AHKLine := "UChar ActionType,UChar Argc,UShort FileIndex,LineNumber,Arg,Attribute,*_AHKLine PrevLine,*_AHKLine NextLine,RelatedLine,ParentLine"
_AHKLabel := "LPTSTR name,*_AHKLine JumpToLine,*_AHKLabel PrevLabel,*_AHKLabel NextLabel"
_AHKArgStruct := "UChar type,UChar is_expression,UShort length,text,{*_AHKDerefType deref,*_AHKVar var},*_AHKExprTokenType postfix"
_AHKDerefType := "marker,{*_AHKVar var,*_AHKFunc func},Char is_function,UChar param_count,UShort length"
_AHKExprTokenType := "{Int64 value_int64,Double value_double,*_AHKVar var,marker},buf,Int symbol,*_AHKExprTokenType circuit_token"
_AHKFuncParam := "*_AHKVar var,UShort is_byref,UShort default_type,{default_str,Int64 default_int64,Double default_double}"
_AHKRCCallbackFunc := "na1,na2,na3,callfuncptr,na4,Short na5,UChar actual_param_count,UChar create_new_thread,event_info,*_AHKFunc func"
_AHKFunc := "PTR vTable,PTR name,{PTR BIF,*_AHKLine JumpToLine},*_AHKFuncParam Param,Int ParamCount,Int MinParams,*_AHKVar var,*_AHKVar LazyVar,Int VarCount,Int VarCountMax,Int LazyVarCount,Int Instances,*_AHKFunc NextFunc,Char DefaultVarType,Char IsBuiltIn"
_AHKVar := "{Int64 ContentsInt64,Double ContentsDouble,object},Contents,{Length,AliasFor},{Capacity,BIV},UChar HowAllocated,UChar Attrib,Char IsLocal,UChar Type,Name"

; AHK Structures examples !!! TEST REQUIRES AHK_H or AutoHotkey.dll !!!
	; AHKVAR
		Variable:=1
		AHKVAR := new _Struct(_AHKVar,getvar(Variable))
		RESULT .= (AHKVAR.ContentsInt64=1 && Asc(AHKVAR.IsLocal)=0 && StrGet(AHKVAR.Name)="Variable") "`tAHKVar, Structure of AutoHotkey Variable`n"
		AHKVAR:=""

	; AHKFunc
		AHKFUNC := new _Struct(_AHKFunc,FindFunc("TestFunc"))
		RESULT .= (StrGet(AHKFUNC.name)="TestFunc" && Asc(AHKFunc.JumpToLine.Actiontype)=34) "`tAHKFunc + JumpToLine for AHK function`n"
		AHKFUNC := ""
MsgBox % RESULT
ExitApp

TestFunc(){
	MsgBox
}


HotKeyIt
  • Moderators
  • 7439 posts
  • Last active: Jun 22 2016 09:14 PM
  • Joined: 18 Jun 2008

- CHAR, UCHAR, TCHAR will write one character only

#Include <_struct>

s:=new _Struct("Char chr")

s.chr:="A"

MsgBox % s.chr

s.chr:="BCD" ;only B will be written to structure

MsgBox % s.chr



infogulch
  • Moderators
  • 717 posts
  • Last active: Jul 31 2014 08:27 PM
  • Joined: 27 Mar 2008
Cool library HotKeyIt! If you put it on GitHub I would be happy to contribute.

It would be nice if one could pass both the struct definition and an object/array with values to populate it with.

Also is this library capable of working with non-struct multi-types like a DWORD's low and high words?

Lexikos
  • Administrators
  • 9844 posts
  • AutoHotkey Foundation
  • Last active:
  • Joined: 17 Oct 2006

Also is this library capable of working with non-struct multi-types like a DWORD's low and high words?

I suppose you could just use two WORD fields...

infogulch
  • Moderators
  • 717 posts
  • Last active: Jul 31 2014 08:27 PM
  • Joined: 27 Mar 2008
Oh, good idea, but wouldn't that still pass an address when it expected the actual values?

Lexikos
  • Administrators
  • 9844 posts
  • AutoHotkey Foundation
  • Last active:
  • Joined: 17 Oct 2006
Now I see what you mean - passing a "struct" composed of high and low words directly to DllCall. It's not worth it. Just use ((high << 16) | low).

If the script supports unions, you could do something equivalent to (C/pseudo-code):
union {
  DWORD dw;
  struct {
    WORD lo;
    WORD hi;
  };
};
...and then set lo/hi but pass dw to DllCall.

infogulch
  • Moderators
  • 717 posts
  • Last active: Jul 31 2014 08:27 PM
  • Joined: 27 Mar 2008
Interesting. Yes, that is a very obvious way of composing a DWORD, I was just curious if this library could it... Not everyone is as familiar with these things as you ("we" in some cases) are though. :)

Lexikos
  • Administrators
  • 9844 posts
  • AutoHotkey Foundation
  • Last active:
  • Joined: 17 Oct 2006
If a DllCall asks for a DWORD with low and high words having particular values, will an inexperienced user think to use a struct? It might be better to have a MAKEDWORD() function, equivalent to the MAKELRESULT or MAKEWPARAM macros. (There doesn't appear to be a MAKEDWORD macro, though there is a MAKEWORD macro.)

Btw, a more general way to pass a struct of 8 bytes by value is "int64", NumGet(Struct[], "int64"). For 4 byte structs, "int", NumGet(Struct[], "int"). Beyond 8 bytes, how the struct is passed differs between 32-bit and 64-bit builds.

On a related note, I have tentative plans for DllCall:
[*:24efpyl3]Support passing objects in DllCall - i.e. let the object define the actual type and value. The awkward thing is DllCall's requirement that type always be specified.
[*:24efpyl3]Some way to specify a directly-passed struct (blob of binary data), so scripts don't need to deal with the differences between 32-bit and 64-bit builds. Maybe this could be part of the above point (using objects).

infogulch
  • Moderators
  • 717 posts
  • Last active: Jul 31 2014 08:27 PM
  • Joined: 27 Mar 2008
I like the ideas for dllcall, but I don't understand the second point.

Btw, I'm trying to wrap the OpenGL32 library. I used the [subclass {}, subclass.remove("__class")] trick from the other thread to define a list of args that each function requires, and when the function is "__call"ed I insert the arg types before each arg passed to the variadic param and then return DllCall("opengl32\" func, args*).

It would be nice to have better support for structs, that's why I'm messing around here. :)

The problem with passing data to DllCall without types is that ahk's type system is so loose that it's probably not reliable enough to determine the type of data needed to send to a compiled function. (of course I'm sure noone is more aware of that than you) I don't think there's really any way to "query" the dll for what types are expected...

I suppose what I'm trying to do with OpenGL could be considered a method of "querying" the types... I dunno

Lexikos
  • Administrators
  • 9844 posts
  • AutoHotkey Foundation
  • Last active:
  • Joined: 17 Oct 2006
If by "the second point", you meant "some way to specify a directly-passed struct", I mean for functions like SomeFunc(RECT rcParam), which would translate to DllCall("SomeFunc",int,l,int,t,int,r,int,B) on 32-bit and DllCall("SomeFunc",ptr,&rect) on 64-bit. I was thinking something simple like DllCall("SomeFunc", 16, &rect) could do the job. Alternatively, DllCall("SomeFunc", "", rect) could provide the rect object with a way to manipulate the parameter list directly.

I wasn't suggesting that Type be omitted for DllCall in general, just that it would be redundant for objects which define their own type. But actually, something like DllCall(RetType: Func, Type1: Param1, AutoTyped_Param2 ...) may be possible (for v2). For ordinary integers, it should be safe to assume Ptr thanks to the way parameters are aligned and the fact that signed vs unsigned doesn't matter for input parameters. It might even be possible to support Int64*, UInt* and Ptr* implicitly by allowing &VarContainingInteger to return the address of the variable's integer field rather than its string buffer (which mightn't exist).

However, I'm not sure I can justify a syntax change that significant.

HotKeyIt: If this is getting too off-topic, I can split the thread.