v2-beta.1 - VarRef for static variables in functions

Get help with using AutoHotkey (v2 or newer) and its commands and hotkeys
just me
Posts: 9450
Joined: 02 Oct 2013, 08:51
Location: Germany

v2-beta.1 - VarRef for static variables in functions

28 Jul 2021, 04:26

I started to port some of my scripts to v2 (again).

v1 UseGDIP:

Code: Select all

UseGDIP(Params*) { ; Loads and initializes the Gdiplus.dll at load-time
   ; GET_MODULE_HANDLE_EX_FLAG_PIN = 0x00000001
   Static GdipObject := ""
        , GdipModule := ""
        , GdipToken  := ""
   Static OnLoad := UseGDIP()
   If (GdipModule = "") {
      If !DllCall("LoadLibrary", "Str", "Gdiplus.dll", "UPtr")
         UseGDIP_Error("The Gdiplus.dll could not be loaded!`n`nThe program will exit!")
      If !DllCall("GetModuleHandleEx", "UInt", 0x00000001, "Str", "Gdiplus.dll", "PtrP", GdipModule, "UInt")
         UseGDIP_Error("The Gdiplus.dll could not be loaded!`n`nThe program will exit!")
      VarSetCapacity(SI, 24, 0), NumPut(1, SI, 0, "UInt") ; size of 64-bit structure
      If DllCall("Gdiplus.dll\GdiplusStartup", "PtrP", GdipToken, "Ptr", &SI, "Ptr", 0)
         UseGDIP_Error("GDI+ could not be startet!`n`nThe program will exit!")
      GdipObject := {Base: {__Delete: Func("UseGDIP").Bind(GdipModule, GdipToken)}}
   }
   Else If (Params[1] = GdipModule) && (Params[2] = GdipToken)
      DllCall("Gdiplus.dll\GdiplusShutdown", "Ptr", GdipToken)
}
UseGDIP_Error(ErrorMsg) {
   MsgBox, 262160, UseGDIP, %ErrorMsg%
   ExitApp
}
I had some problems with this function. Among other things you cannot call a user function as a static initializer any more. Another problem is VarRef for static variables used with PtrP DllCall types.
v2 UseGDIP:

Code: Select all

UseGDIP()

UseGDIP(Params*) { ; Loads and initializes the Gdiplus.dll at load-time
   ; GET_MODULE_HANDLE_EX_FLAG_PIN = 0x00000001
   Static GdipObject := Object(),
          GdipModule := 0,
          GdipToken  := 0
   If (GdipModule = 0) {
      If !DllCall("LoadLibrary", "Str", "Gdiplus.dll", "UPtr")
         UseGDIP_Error("The Gdiplus.dll could not be loaded!`n`nThe program will exit!")
      If !DllCall("GetModuleHandleEx", "UInt", 0x00000001, "Str", "Gdiplus.dll", "PtrP", &GdipModule, "UInt")
         UseGDIP_Error("The Gdiplus.dll could not be loaded!`n`nThe program will exit!")
      SI := Buffer(24, 0) ; size of 64-bit structure
      NumPut("UInt", 1, SI)
      If DllCall("Gdiplus.dll\GdiplusStartup", "PtrP", &GdipToken, "Ptr", SI, "Ptr", 0, "UInt")
         UseGDIP_Error("GDI+ could not be startet!`n`nThe program will exit!")
      GdipObject := {__Delete: UseGDIP.Bind(GdipModule, GdipToken)}
      MsgBox(GdipModule . " - " . GdipToken, A_ThisFunc)
   }
   Else If (Params[1] = GdipModule) && (Params[2] = GdipToken)
      DllCall("Gdiplus.dll\GdiplusShutdown", "Ptr", GdipToken)
}
UseGDIP_Error(ErrorMsg) {
   MsgBox(ErrorMsg, "UseGDIP", 262160)
   ExitApp
}
This results in an error

Code: Select all

---------------------------
UseGDIP.ahk
---------------------------
Error:  This variable has not been assigned a value.

Specifically: static GdipModule

	Line#
	001: UseGDIP()
	003: {
--->	008: If (GdipModule = 0)
	008: {
	009: If !DllCall("LoadLibrary", "Str", "Gdiplus.dll", "UPtr")
	010: UseGDIP_Error("The Gdiplus.dll could not be loaded!

The program will exit!")
	011: If !DllCall("GetModuleHandleEx", "UInt", 0x00000001, "Str", "Gdiplus.dll", "PtrP", &GdipModule, "UInt")
	012: UseGDIP_Error("The Gdiplus.dll could not be loaded!

The program will exit!")
	013: SI := Buffer(24, 0)
	014: NumPut("UInt", 1, SI)

Try to continue anyway?
---------------------------
Ja   Nein
---------------------------
on the second call from __Delete(). What am I doing wrong?
swagfag
Posts: 6222
Joined: 11 Jan 2017, 17:59

Re: v2-beta.1 - VarRef for static variables in functions

28 Jul 2021, 05:10

can u explain what the code is meant to achieve? it looks like a scuffed raii wrapper but i cant tell why that would be needed if ure then gonna store stuff in static variables(destructors wont run until the script exits, at which point is kinda pointless(lol) to "shutdown gdip". the OS would have done that regardless)

Code: Select all

; GET_MODULE_HANDLE_EX_FLAG_PIN
#DllLoad already does that, so use it instead
Error: This variable has not been assigned a value.

Specifically: static GdipModule
When the script exits, objects contained by global and static variables are released automatically in an arbitrary, implementation-defined order. When __Delete is called during this process, some global or static variables may have already been released, but any references contained by the object itself are still valid. It is therefore best for __Delete to be entirely self-contained, and not rely on any global or static variables.
the (for now) implementation-defined order is whatever _stricmp() decides it should be. its not positional(as might have assumed), so GdipModule probably really is uninitialized by the time ur destructor gets to run
just me
Posts: 9450
Joined: 02 Oct 2013, 08:51
Location: Germany

Re: v2-beta.1 - VarRef for static variables in functions

28 Jul 2021, 05:30

@swagfag,

obviously UseGDIP() is some kind of #DllLoad replacement for the Gdiplus.dll for v1 additionally processing StartUp and ShutDown automatically.

Apart from the question if it is needed in v2 or not, I'd like to know how to do it in v2. Interestingly this version works:

Code: Select all

UseGDIP()

UseGDIP(Params*) { ; Loads and initializes the Gdiplus.dll at load-time
   ; GET_MODULE_HANDLE_EX_FLAG_PIN = 0x00000001
   Static GdipObject := Object(),
          GdipModule := 0,
          GdipToken  := 0
   MsgBox(GdipModule . " - " . GdipToken, A_ThisFunc)
   If (GdipModule = 0) {
      If !DllCall("LoadLibrary", "Str", "Gdiplus.dll", "UPtr")
         UseGDIP_Error("The Gdiplus.dll could not be loaded!`n`nThe program will exit!")
      If !DllCall("GetModuleHandleEx", "UInt", 0x00000001, "Str", "Gdiplus.dll", "PtrP", &Module := 0, "UInt")
         UseGDIP_Error("The Gdiplus.dll could not be loaded!`n`nThe program will exit!")
      GdipModule := Module
      SI := Buffer(24, 0) ; size of 64-bit structure
      NumPut("UInt", 1, SI)
      If DllCall("Gdiplus.dll\GdiplusStartup", "PtrP", &Token := 0, "Ptr", SI, "Ptr", 0, "UInt")
         UseGDIP_Error("GDI+ could not be startet!`n`nThe program will exit!")
      GdipToken := Token
      GdipObject := {__Delete: UseGDIP.Bind(GdipModule, GdipToken)}
   }
   Else If (Params[1] = GdipModule) && (Params[2] = GdipToken)
      DllCall("Gdiplus.dll\GdiplusShutdown", "Ptr", GdipToken)
}
UseGDIP_Error(ErrorMsg) {
   MsgBox(ErrorMsg, "UseGDIP", 262160)
   ExitApp
}
swagfag
Posts: 6222
Joined: 11 Jan 2017, 17:59

Re: v2-beta.1 - VarRef for static variables in functions

28 Jul 2021, 07:24

if by "it" u mean run the function automatically without actually calling it urself like u used to be able to in v1, u cant. the v2 code could be:

Code: Select all

#DllLoad gdiplus.dll

UseGDIP()

UseGDIP() {	; Loads and initializes the Gdiplus.dll at load-time
	SI := Buffer(24, 0)	; size of 64-bit structure
	NumPut("UInt", 1, SI)
	If DllCall("Gdiplus.dll\GdiplusStartup", "PtrP", 0, "Ptr", SI, "Ptr", 0)
		UseGDIP_Error("GDI+ could not be startet!`n`nThe program will exit!")
}
UseGDIP_Error(ErrorMsg) {
	MsgBox(ErrorMsg, "UseGDIP", 262160)
	ExitApp
}
just me
Posts: 9450
Joined: 02 Oct 2013, 08:51
Location: Germany

Re: v2-beta.1 - VarRef for static variables in functions

28 Jul 2021, 08:18

No, by "it" I mean how to use a VarRef for the static variable GdipModule in

Code: Select all

DllCall("GetModuleHandleEx", "UInt", 0x00000001, "Str", "Gdiplus.dll", "PtrP", ???, "UInt")
swagfag
Posts: 6222
Joined: 11 Jan 2017, 17:59

Re: v2-beta.1 - VarRef for static variables in functions

28 Jul 2021, 09:13

Code: Select all

DllCall("GetModuleHandleEx", "UInt", 0x00000001, "Str", "Gdiplus.dll", "PtrP", &GdipModule, "UInt")
User avatar
TheArkive
Posts: 1027
Joined: 05 Aug 2016, 08:06
Location: The Construct
Contact:

Re: v2-beta.1 - VarRef for static variables in functions

28 Jul 2021, 10:14

I think i see what @just me is getting at.

Oddly, this also works:

Code: Select all

UseGDIP()

UseGDIP(Params*) { ; Loads and initializes the Gdiplus.dll at load-time
   Global GdipObject := Object() ; <--- change this to global
   Static GdipModule := 0,       ; <--- or even change this to global also works
          GdipToken  := 0
   If (GdipModule = 0) {
      If !DllCall("LoadLibrary", "Str", "Gdiplus.dll", "UPtr")
         UseGDIP_Error("The Gdiplus.dll could not be loaded!`n`nThe program will exit!")
      If !DllCall("GetModuleHandleEx", "UInt", 0x00000001, "Str", "Gdiplus.dll", "PtrP", &GdipModule, "UInt") ; GET_MODULE_HANDLE_EX_FLAG_PIN = 0x00000001
         UseGDIP_Error("The Gdiplus.dll could not be loaded!`n`nThe program will exit!")
      SI := Buffer(24, 0) ; size of 64-bit structure
      NumPut("UInt", 1, SI)
      If DllCall("Gdiplus.dll\GdiplusStartup", "PtrP", &GdipToken, "Ptr", SI, "Ptr", 0, "UInt")
         UseGDIP_Error("GDI+ could not be startet!`n`nThe program will exit!")
      GdipObject := {__Delete: UseGDIP.Bind(GdipModule, GdipToken)}
      MsgBox(GdipModule . " - " . GdipToken, A_ThisFunc)
   }
   Else If (Params[1] = GdipModule) && (Params[2] = GdipToken)
      DllCall("Gdiplus.dll\GdiplusShutdown", "Ptr", GdipToken)
}
UseGDIP_Error(ErrorMsg) {
   MsgBox(ErrorMsg, "UseGDIP", 262160)
   ExitApp
}

msgbox("this is the end...")
And for some reason the Static GdipModule var works in this context. I know I've had issues setting certain values as initializers with values like Object(), not just static vars, but global, and also properties in classes, static or not.

EDIT: In particular setting optional parameters in a func or method.

My first thought would be to apply a global class.

Something like:

Code: Select all

class app { ; load the class first
    Static GdipModule := 0 ; i would usually store more than just one value though
}

; then the func ...
And then of course making adjustments in the func to store the output var from DllCall(). The idea would be for the script/application to remember how to cleanup after itself on exit right?

EDIT2: My guess is the order in which objects are destroyed is the key factor. As @swagfag quoted from the docs, since __Delete() relies on the reference to the outer func to still exist, and it's contained static vars, thus it's not self contained, so the order of destruction ends up throwing an error.

Return to “Ask for Help (v2)”

Who is online

Users browsing this forum: Albireo, CraigM, Panaku and 49 guests