object reference count of 0 crashes script (ObjAddRef/ObjRelease)

Get help with using AutoHotkey and its commands and hotkeys
User avatar
jeeswg
Posts: 6904
Joined: 19 Dec 2016, 01:58
Location: UK

object reference count of 0 crashes script (ObjAddRef/ObjRelease)

07 May 2017, 14:11

If an object in AHK has a reference count of 0, and then an ExitApp or Reload is attempted, the script crashes. Why is this?

E.g.

Code: Select all

;q:: ;don't run this code (this will crash the script)
obj := {}
ObjRelease(&obj)
ExitApp ;script crashes

;w:: ;don't run this code (this will crash the script)
obj := {}
;ref count: 1
ObjRelease(&obj)
;ref count: 0
MsgBox
Reload ;script crashes
return
Some code for doing tests involving an object's reference count:

Code: Select all

w:: ;2 ways to get an object's reference count
obj := {}
vRefCount1 := NumGet(&obj + A_PtrSize) ;note: this method is undocumented and thus not guaranteed to work in all future versions of AHK
vRefCount2 := ObjAddRef(&obj)-1, ObjRelease(&obj)
obj := ""
MsgBox, % vRefCount1 " " vRefCount2
return

q:: ;tests involving object reference counts
obj := {}
MsgBox, % NumGet(&obj + A_PtrSize) ;ref count: 1

address := &obj
MsgBox, % NumGet(&obj + A_PtrSize) ;ref count: 1

address2 := Object(obj)
MsgBox, % NumGet(&obj + A_PtrSize) ;ref count: 2

obj2 := Object(&obj)
MsgBox, % NumGet(&obj + A_PtrSize) ;ref count: 3

obj3 := obj
MsgBox, % NumGet(&obj + A_PtrSize) ;ref count: 4

obj3 := obj ;attempt to do the same thing as in earlier line
MsgBox, % NumGet(&obj + A_PtrSize) ;ref count: 4

obj4 := obj
MsgBox, % NumGet(&obj + A_PtrSize) ;ref count: 5

obj := obj2 := obj3 := obj4 := ""
return
Btw I did not find any information on getting the reference count for an object directly, however, I knew of a similar method to get an object's key count, and so checked nearby offsets, to see if that would work, which it did.

Links:
ObjAddRef() / ObjRelease()
https://autohotkey.com/docs/commands/ObjAddRef.htm
Objects
https://autohotkey.com/docs/Objects.htm ... e_Counting
ObjRelease needed when using ComObjValue+ObjAddRef? - AutoHotkey Community
https://autohotkey.com/boards/viewtopic.php?t=4839
[ComObjEnwrap, ComObjUnwrap]
ComObjActive()
https://autohotkey.com/docs/commands/ComObjActive.htm

Btw is it so important to always clear each object via obj := "", when no longer needed, or is it OK to leave a few objects (with a small number of keys) to exist that aren't needed e.g. created from using StrSplit. [EDIT:] I will always try to clear each object when it's no longer needed, but is the overhead so bad, and is the number of objects or the size of objects more of a problem?

[EDIT:]
- Is there anything the script itself should dispose of before it closes, or does AHK tidy up *everything* for you.
- Does AHK tidy up i.e. clear any existing objects when the script is closed, or are any remnants of objects left behind?
Last edited by jeeswg on 08 May 2017, 05:12, edited 5 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: 4031
Joined: 17 Jul 2016, 01:02
Contact:

Re: object reference count of 0 crashes script (ObjAddRef/ObjRelease)

08 May 2017, 03:57

jeeswg wrote:If an object in AHK has a reference count of 0, and then an ExitApp or Reload is attempted, the script crashes. Why is this?
Hi jeeswg.
I'm not giving you an answer to the Why is this?-question, someone more familliar with the implementation should answer that, however, from your link,
Reference counting wrote: Script authors should not invoke this mechanism explicitly, except when dealing directly with unmanaged pointers to objects.
which tells me to, just don't do it.
Regarding getting the count, vRefCount2 := ObjAddRef(&obj)-1, ObjRelease(&obj) seems perfectly fine. But note
ObjAddRef wrote: This value should be used only for debugging purposes.

This,

Code: Select all

q::
; [...]
obj4 := obj
MsgBox, % NumGet(&obj + A_PtrSize) ;ref count: 5

obj := ""
return
in combination with this quote
jeeswg wrote:Btw is it so important to always clear each object via obj := "", when no longer needed, or is it OK to leave a few objects to exist that aren't needed e.g. created from using StrSplit.
makes me think you would be surprised if you replaced the obj := "" line with this,

Code: Select all

obj.a:=1
obj := ""
MsgBox, % "after obj:=""""`n`nObjAddRef(&obj4)-1:`t" ObjAddRef(&obj4)-1  "`n`nobjk.a:`t" obj4.a  ", " obj3.a  ", " obj2.a
return

The question about the importance of clearing variables, obviously there is not one right answer for that question, it depends, is the easy answer ;). You can avoid most of the need to think about it by using functions, with local variables instead of just using globals. See, local variables,
local wrote:All local variables which are not static are automatically freed (made empty) when the function returns.
Using functions basically changes the problem from clearing what you don't need, to keeping what you need, I prefer the latter.

Cheers.
User avatar
jeeswg
Posts: 6904
Joined: 19 Dec 2016, 01:58
Location: UK

Re: object reference count of 0 crashes script (ObjAddRef/ObjRelease)

08 May 2017, 16:24

In this example, when I try to replace ComObjEnwrap with ComObject, it crashes.

Code: Select all

#SingleInstance force
DetectHiddenWindows, On
Gui, New, +HwndhGui

;==================================================

vAtl := "atl"
DllCall("kernel32\LoadLibrary", Str,vAtl, Ptr)
DllCall(vAtl "\AtlAxWinInit")

vWinClass := "AtlAxWin", vWinText := "Shell.Explorer"
vWinStyle := 0x54000000, vWinExStyle := 0
vPosX := 0, vPosY := 0, vPosW := 300, vPosH := 300
hWndParent := hGui, hMenu := 0, hInstance := 0, vParam := 0

hCtl := JEE_DCCreateWindowEx(vWinExStyle, vWinClass, vWinText, vWinStyle, vPosX, vPosY, vPosW, vPosH, hWndParent, hMenu, hInstance, vParam)

DllCall(vAtl "\AtlAxGetControl", Ptr,hCtl, UIntP,pUnk)
;IID_IUnknown := "{00000000-0000-0000-C000-000000000046}"
vIID := "{00000000-0000-0000-C000-000000000046}"
vPtr := ComObjQuery(pUnk, vIID)
oWB := ComObjEnwrap(vPtr), ObjRelease(vPtr)
;oWB := ComObject(9, vPtr, 1) ;using this line or line below instead of line above causes crash
;oWB := ComObject(9, vPtr, 1), ObjAddRef(vPtr) ;see: https://autohotkey.com/docs/commands/ComObjActive.htm
oWB.Navigate("https://www.google.com/")
while oWB.busy || !(oWB.ReadyState = 4)
	Sleep 100
;OLECMDID_OPTICAL_ZOOM := 63 ;OLECMDEXECOPT_DONTPROMPTUSER := 2
oWB.ExecWB(63, 2, 30, 0) ;zoom 30%
oWB.document.parentWindow.scrollBy(120, 35)
Gui, Show, x100 y100 w300 h300
return

;==================================================

JEE_DCCreateWindowEx(vWinExStyle, vWinClass, vWinText, vWinStyle, vPosX, vPosY, vPosW, vPosH, hWndParent, hMenu, hInstance, vParam)
{
	return DllCall("CreateWindowEx", UInt,vWinExStyle, Str,vWinClass, Str,vWinText, UInt,vWinStyle, Int,vPosX, Int,vPosY, Int,vPosW, Int,vPosH, Ptr,hWndParent, Ptr,hMenu, Ptr,hInstance, Ptr,vParam, Ptr)
}

;==================================================
homepage | tutorials | wish list | fun threads | donate
WARNING: copy your posts/messages before hitting Submit as you may lose them due to CAPTCHA
qwerty12
Posts: 468
Joined: 04 Mar 2016, 04:33
GitHub: qwerty12

Re: object reference count of 0 crashes script (ObjAddRef/ObjRelease)

08 May 2017, 18:02

That's because you query pUnk for an IUnknown interface pointer, which you get, and then pass the result to ComObject telling it no, it's really, totally an IDispatch one that should be wrapped.

Set vIID to "00020400-0000-0000-C000-000000000046" and watch your problems go away with ComObject. ComObjEnwrap isn't a real function - seriously, you can change that to ComObj32666Iblis and it will still work. Presumably, ComObj doesn't entirely trust whatever you've given it and does its own QueryInterface on the pointer to see if it can get a valid IDispatch interface pointer.
Last edited by qwerty12 on 08 May 2017, 18:17, edited 2 times in total.
User avatar
jeeswg
Posts: 6904
Joined: 19 Dec 2016, 01:58
Location: UK

Re: object reference count of 0 crashes script (ObjAddRef/ObjRelease)

08 May 2017, 18:11

Amazing, it's working now. I mentioned the problem before and you said that the script worked for you that time! Much appreciated.

Mentioned it back at:
DllCall converter/cleaner (e.g. x32 to x64/x32 two-way compatible) - AutoHotkey Community
https://autohotkey.com/boards/viewtopic.php?t=31365
homepage | tutorials | wish list | fun threads | donate
WARNING: copy your posts/messages before hitting Submit as you may lose them due to CAPTCHA
qwerty12
Posts: 468
Joined: 04 Mar 2016, 04:33
GitHub: qwerty12

Re: object reference count of 0 crashes script (ObjAddRef/ObjRelease)

08 May 2017, 18:15

jeeswg wrote:Amazing, it's working now. I mentioned the problem before and you said that the script worked for you that time!
It actually was :/ Though, my memory isn't good enough to tell you whether the correct IID was in the script back then...
User avatar
jeeswg
Posts: 6904
Joined: 19 Dec 2016, 01:58
Location: UK

Re: object reference count of 0 crashes script (ObjAddRef/ObjRelease)

08 May 2017, 18:38

(I had both IIDs in the old script, although I had the same IID as the one above in use, and the other one commented out.)

Btw what you've told me seems to be the reverse. I was using IID_IUnknown in the script above, but changing it to IID_IDispatch appears to fix the problem.

[MS-OAUT]: Standards Assignments
https://msdn.microsoft.com/en-us/library/cc237842.aspx

;IID_IUnknown := "{00000000-0000-0000-C000-000000000046}"
;IID_IDispatch := "{00020400-0000-0000-C000-000000000046}"

Anyway AutoHotkey ftw.
homepage | tutorials | wish list | fun threads | donate
WARNING: copy your posts/messages before hitting Submit as you may lose them due to CAPTCHA
qwerty12
Posts: 468
Joined: 04 Mar 2016, 04:33
GitHub: qwerty12

Re: object reference count of 0 crashes script (ObjAddRef/ObjRelease)

08 May 2017, 18:41

jeeswg wrote:Btw what you've told me seems to be the reverse. I was using IID_IUnknown in the script above, but changing it to IID_IDispatch appears to fix the problem.
That's what I said (albeit in a lazier manner):
qwerty12 wrote:Set vIID to "00020400-0000-0000-C000-000000000046" and watch your problems go away with ComObject...
User avatar
jeeswg
Posts: 6904
Joined: 19 Dec 2016, 01:58
Location: UK

Re: object reference count of 0 crashes script (ObjAddRef/ObjRelease)

03 Jan 2018, 12:12

- Being able to get the ref count, without altering it, was really useful for double-checking what was happening, when I tried to, in effect, move an object to another variable name.
- Why would I want to do that? I would want to change the array variable name in this way, as a workaround regarding deleting keys: instead of deleting lots of keys, I would, rename the object, create an empty object with the original variable name, copy over a small number of keys from the original renamed object, delete the original renamed object, and end up with an object, with the same name as the original object, but with fewer keys.
- Even if I *know* or *think I know* what's going on with something, e.g. reference counts, it's always best to double-check, that's always the way with IT, double-check.

Code: Select all

q:: ;'rename' arrays and check the object reference count
oArray := StrSplit("abcdefghijklmnopqrstuvwxyz")
MsgBox, % JEE_ObjRefCount(oArray)
oArray2 := oArray
MsgBox, % JEE_ObjRefCount(oArray)
oArray2 := ""
MsgBox, % JEE_ObjRefCount(oArray)
MsgBox, % oArray.Length()
oArray := ""

MsgBox

oArray := StrSplit("abcdefghijklmnopqrstuvwxyz")
MsgBox, % JEE_ObjRefCount(oArray)
oArray2 := oArray
MsgBox, % JEE_ObjRefCount(oArray)
oArray := ""
MsgBox, % JEE_ObjRefCount(oArray2)
MsgBox, % oArray2.Length()
oArray2 := ""
return

;==================================================

;note: if you don't use ByRef, this function itself would increase the ref count
;note: tested on AHK v1.1, not guaranteed to work in future versions of AHK

JEE_ObjRefCount(ByRef oObj)
{
	return NumGet(&oObj + A_PtrSize)
}

;==================================================
- This code crashes AHK, and it doesn't surprise me that it would, but I'm curious as to the logic of why it would crash AHK, there are reasons to suppose that it wouldn't crash AHK. Is it something like this: AHK thinks the object still exists, but the ref count is zero, causing a crash.
- Why when you do ObjRelease, changing the count from 1 to 0, does AHK not tidily discard the object, just as it would when you do oArray := "". Or is changing the ref count essentially simply like editing data in the address space, you're not properly informing the program of a change to something, and so a crash is possible.
- Is it a case of AHK tries to do something to an object, checks the count and sees 0, but therefore crashes, rather than being able to resolve the situation meaningfully, or, simply, that possibility wasn't considered (which is justifiable), and so a crash occurs. Or does something happen which subtracts from 0 ending up with a huge reference count e.g. 0 -> 0xFFFFFFFF.

Code: Select all

q:: ;code that crashes AHK
oArray := {}
ObjRelease(&oArray)
Reload
- Some links re. previous ObjAddRef/ObjRelease questions.
conversion logic, v1 = -> v1 := -> v2, two-way compatibility - Page 4 - AutoHotkey Community
https://autohotkey.com/boards/viewtopic ... 36#p138636
Creating a user defined function to search in an Array - AutoHotkey Community
https://autohotkey.com/boards/viewtopic ... 37#p143037

==================================================

- [EDIT:] Another example, AHK can smartly handle the same variable name being used to create another reference. I think this is a good example of something where, it's hard to be sure how it works, unless you test it yourself. If we don't test it ourselves, we're relying on AHK having smart behaviour for all sorts of scenarios, assuming that it would do what we would hope it would do.

Code: Select all

q::
oArray := StrSplit("abcdefghijklmnopqrstuvwxyz")
oArray2 := oArray
MsgBox, % JEE_ObjRefCount(oArray) ;2
oArray2 := oArray
MsgBox, % JEE_ObjRefCount(oArray) ;2

oArray := StrSplit("abcdefghijklmnopqrstuvwxyz")
oArray2 := oArray
MsgBox, % JEE_ObjRefCount(oArray) ;2
oArray3 := oArray
MsgBox, % JEE_ObjRefCount(oArray) ;3

oArray := ""
return
- [EDIT:] A further example, subtler and subtler.

Code: Select all

q:: ;more ref count tests
oArray := StrSplit("abcdefghijklmnopqrstuvwxyz")
oArray2 := StrSplit("abcdefghijklmnopqrstuvwxyz")
oArray3 := oArray
MsgBox, % JEE_ObjRefCount(oArray) " " JEE_ObjRefCount(oArray2) ;2 1
oArray3 := oArray2
MsgBox, % JEE_ObjRefCount(oArray) " " JEE_ObjRefCount(oArray2) ;1 2
return
homepage | tutorials | wish list | fun threads | donate
WARNING: copy your posts/messages before hitting Submit as you may lose them due to CAPTCHA
User avatar
jeeswg
Posts: 6904
Joined: 19 Dec 2016, 01:58
Location: UK

Re: object reference count of 0 crashes script (ObjAddRef/ObjRelease)

05 Jan 2018, 14:50

An example to in effect remove keys from an array, by 'renaming' it, creating a new array with the old name, and moving keys from the old array to the new array.

Code: Select all

q:: ;keep specific keys, delete others
oArray := StrSplit("abcdefghijklmnopqrstuvwxyz")
oArray2 := oArray
oArray := {}
for vKey, vValue in oArray2
	if (vValue ~= "[aeiou]")
		oArray[vKey] := vValue
vOutput := ""
for vKey, vValue in oArray
	vOutput .= vKey " " vValue "`r`n"
MsgBox, % vOutput
return
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”

Who is online

Users browsing this forum: Bing [Bot], Google [Bot], tallguyyo and 213 guests