intermittent CONTINUABLE EXCEPTION_ACCESS_VIOLATION

Ask for help, how to use AHK_H, etc.
guest3456
Posts: 3463
Joined: 09 Oct 2013, 10:31

intermittent CONTINUABLE EXCEPTION_ACCESS_VIOLATION

Post by guest3456 » 13 Mar 2020, 22:35

i use Yunit framework to do some unit tests

i get a msgbox with the error in the title, and it points to the return dllcall line in this function:

Code: Select all

Gdip_DisposeImage(pBitmap)
{
   static GdipDisposeImageAddr
   if (!GdipDisposeImageAddr)
      GdipDisposeImageAddr := DllCall("GetProcAddress", "Ptr", DllCall("GetModuleHandle", "Str", "gdiplus", "Ptr")
                                                      , "AStr", "GdipDisposeImage"
                                                      , "Ptr")

   return DllCall(GdipDisposeImageAddr, "Ptr", pBitmap)
}
obviously i'm looking up the address for performance reasons. the problem is, i only get the error maybe 10-20% of the time when i run my unit tests. all other times it works perfectly, i get no error. so i am unable to consistently reproduce it, to test whether or not a normal dllcall without the addr lookup works better. i can click No on the msgbox and continue execution, and then yunit shows me error 0xC0000005 for those lines. any ideas?

guest3456
Posts: 3463
Joined: 09 Oct 2013, 10:31

Re: intermittent CONTINUABLE EXCEPTION_ACCESS_VIOLATION

Post by guest3456 » 13 Mar 2020, 22:47

ok it seems to happen almost every time on a cold boot

this time, i immediately changed the return dllcall line to a normal DisposeImage dllcall without the addr, and it worked, but then it spit out the same error for this function:

Code: Select all

Gdip_LockBits(pBitmap, x, y, w, h, ByRef Stride, ByRef Scan0, ByRef BitmapData, LockMode = 3, PixelFormat = 0x26200a)
{
   static GdipBitmapLockBitsAddr
   if (!GdipBitmapLockBitsAddr)
      GdipBitmapLockBitsAddr := DllCall("GetProcAddress", "Ptr", DllCall("GetModuleHandle", "Str", "gdiplus", "Ptr")
                                                         , "AStr", "GdipBitmapLockBits"
                                                         , "Ptr")

   CreateRect(Rect, x, y, w, h)
   VarSetCapacity(BitmapData, 16+2*(A_PtrSize ? A_PtrSize : 4), 0)
   E := DllCall(GdipBitmapLockBitsAddr, "Ptr", pBitmap, "Ptr", &Rect, "uint", LockMode, "int", PixelFormat, "Ptr", &BitmapData)
   Stride := NumGet(BitmapData, 8, "Int")
   Scan0 := NumGet(BitmapData, 16, "Ptr")
   return E
}
on the "E := DllCall" line trying to call the addr also

i just got the error like 10 times in a row. and now its stopped. and now i can change the DisposeImage back to the addr and it works too

just rebooted again. only get the error 1 time, and then it started working

Helgef
Posts: 4709
Joined: 17 Jul 2016, 01:02
Contact:

Re: intermittent CONTINUABLE EXCEPTION_ACCESS_VIOLATION

Post by Helgef » 14 Mar 2020, 01:55

Hi guest3456 :wave:.
Your functions have the same potential problem as I pointed out :arrow: in this thread. That is, if gdiplus.dll is unloaded after the address is looked up, it becomes invalid. If you reload the dll, you need to look up the address again, your functions doesn't handle that. Although probably not part of your problem, you also do not verify the call looking up the address.

Cheers.
guest3456
Posts: 3463
Joined: 09 Oct 2013, 10:31

Re: intermittent CONTINUABLE EXCEPTION_ACCESS_VIOLATION

Post by guest3456 » 14 Mar 2020, 10:37

Helgef wrote:
14 Mar 2020, 01:55
Hi guest3456 :wave:.
Your functions have the same potential problem as I pointed out :arrow: in this thread. That is, if gdiplus.dll is unloaded after the address is looked up, it becomes invalid. If you reload the dll, you need to look up the address again, your functions doesn't handle that.
i've already changed my Gdip_startup to look like this to explicitly always loadlibrary, based on your recommendation there:

Code: Select all

Gdip_Startup()
{
        DllCall("LoadLibrary", "str", "gdiplus")
        VarSetCapacity(si, A_PtrSize = 8 ? 24 : 16, 0), si := Chr(1)
        DllCall("gdiplus\GdiplusStartup", "Ptr*", pToken, "Ptr", &si, "Ptr", 0)
        return pToken
}
so i guess if i call Gdip_Shutdown, then the dll is unloaded, but the static address variable still holds the old address, which causes it to fail? in which case, if i want to do these optimizations, i should look up all addresses within Gdip_Startup and just have them as global? but then i guess multiple Gdip_startup calls would have the same issue..
Helgef wrote:
14 Mar 2020, 01:55
Although probably not part of your problem, you also do not verify the call looking up the address.
you mean check the return value?

Helgef
Posts: 4709
Joined: 17 Jul 2016, 01:02
Contact:

Re: intermittent CONTINUABLE EXCEPTION_ACCESS_VIOLATION

Post by Helgef » 14 Mar 2020, 12:04

i guess if i call Gdip_Shutdown, then the dll is unloaded, but the static address variable still holds the old address, which causes it to fail?
Yes.
if i want to do these optimizations, i should look up all addresses within Gdip_Startup and just have them as global?
I wouldn't do it like that, although it is ofc fine if you don't mind the extra global variables. You could :arrow: pin the dll, as does v2's #dllLoad.

Cheers.
Helgef
Posts: 4709
Joined: 17 Jul 2016, 01:02
Contact:

Re: intermittent CONTINUABLE EXCEPTION_ACCESS_VIOLATION

Post by Helgef » 15 Mar 2020, 02:52

you mean check the return value?
Yes ;).

Here is an example on how you can do these optimisations, which is safe and easy to use,

Code: Select all

Gdip_GetProcAddress(fn){
	; this function should be defined above the other functions to not yeild #warn useunset
	local
	static h := 0 ; the handle to gdiplus.dll
	if !h {
		if !dllcall("LoadLibrary", "str", "gdiplus", "ptr")
			throw exception("LoadLibrary failed.",, a_lasterror)
		GET_MODULE_HANDLE_EX_FLAG_PIN := 0x00000001 ; to ensure the dll is never unloaded.
		if !dllcall("GetModuleHandleEx", "uint", GET_MODULE_HANDLE_EX_FLAG_PIN, "str", "gdiplus", "ptr*", h, "int")
			throw exception("GetModuleHandleEx failed.",, a_lasterror)
	}
	if !proc := dllcall("GetProcAddress", "ptr", h, "astr", fn, "ptr")
		throw exception("GetProcAddress failed for function: " . fn,, a_lasterror)
	return proc
}

Gdip_Startup()
{
	static GdiplusStartupAddr := Gdip_GetProcAddress("GdiplusStartup")
	local
	VarSetCapacity(si, A_PtrSize = 8 ? 24 : 16, 0), si := Chr(1)
	if r := DllCall(GdiplusStartupAddr, "Ptr*", pToken, "Ptr", &si, "Ptr", 0)
		throw exception("GdiplusStartup failed.",, r)
	return pToken
}
; gdip_shutdown doesn't need to call FreeLibrary, but can, it doesn't matter.
Gdip_DisposeImage(pBitmap)
{
   static GdipDisposeImageAddr := Gdip_GetProcAddress("GdipDisposeImage")
   return DllCall(GdipDisposeImageAddr, "Ptr", pBitmap)
}
The drawback is that the dll stays loaded for the entire duration of the program's run time, but I think it is fairly low cost (memory).

Cheers.
guest3456
Posts: 3463
Joined: 09 Oct 2013, 10:31

Re: intermittent CONTINUABLE EXCEPTION_ACCESS_VIOLATION

Post by guest3456 » 15 Mar 2020, 15:50

Helgef wrote:
15 Mar 2020, 02:52
Here is an example on how you can do these optimisations, which is safe and easy to use,
excellent thanks, i will be trying this

guest3456
Posts: 3463
Joined: 09 Oct 2013, 10:31

Re: intermittent CONTINUABLE EXCEPTION_ACCESS_VIOLATION

Post by guest3456 » 05 Jun 2020, 12:32

i've implemented the Gdip_GetProcAddress(fn) changes that @Helgef suggested above, and i stopped getting the error in my test suite.

however, two of my users are getting this error again:

Image

the error is not intermittent for him, he gets it every time he launches the executable. however, myself and others don't get it.

the problem is, my script is compiled, and so i can't pinpoint what line number the code is thats causing the problem.. i've tried creating a script to merge in the many #includes in place, however, the line # doesn't seem to match up... i'm guessing because in the compiled script, the parser removes all commented lines.

how can i track down what line that error is correspoding to

HotKeyIt
Posts: 2364
Joined: 29 Sep 2013, 18:35
Contact:

Re: intermittent CONTINUABLE EXCEPTION_ACCESS_VIOLATION

Post by HotKeyIt » 05 Jun 2020, 18:04

You can compile without compression and read the code with ResourceHacker to find the relevant line.
You can also try #WarnContinuableException Off, this would make sense if the script continues to work if user clicks "No".
guest3456
Posts: 3463
Joined: 09 Oct 2013, 10:31

Re: intermittent CONTINUABLE EXCEPTION_ACCESS_VIOLATION

Post by guest3456 » 06 Jun 2020, 10:12

HotKeyIt wrote:
05 Jun 2020, 18:04
You can compile without compression and read the code with ResourceHacker to find the relevant line.
You can also try #WarnContinuableException Off, this would make sense if the script continues to work if user clicks "No".
thanks i will try that

is there something i can add to the AHK_H source, so that when this exception is thrown, i can also see the Code of the line? like other normal exceptions will show you with an arrow ---> along with some context with like 10 lines of code?

HotKeyIt
Posts: 2364
Joined: 29 Sep 2013, 18:35
Contact:

Re: intermittent CONTINUABLE EXCEPTION_ACCESS_VIOLATION

Post by HotKeyIt » 06 Jun 2020, 16:53

AFAIK no because the script text is not kept in memory (due to compression and encryption feature).
However I have added to show the first Line mArg when process exception is thrown, hope this helps.
guest3456
Posts: 3463
Joined: 09 Oct 2013, 10:31

Re: intermittent CONTINUABLE EXCEPTION_ACCESS_VIOLATION

Post by guest3456 » 15 Jun 2020, 18:11

HotKeyIt wrote:
06 Jun 2020, 16:53
AFAIK no because the script text is not kept in memory (due to compression and encryption feature).
However I have added to show the first Line mArg when process exception is thrown, hope this helps.
thanks that helped. showing that first line arg helped me narrow down the error to this:

Code: Select all

msgbox % CPUInfo()

CPUInfo() {
  MCode(CPUID,"538b4424080fa2508b44241489188b44241889088b44241c89105b8b44240c89185bc3")
  VarSetCapacity(a,4,0), VarSetCapacity(b,4,0), VarSetCapacity(c,4,0), VarSetCapacity(d,4,0)
  dllcall(&CPUID,"UInt",0, "Str",a, "Str",b, "Str",c, "Str",d, "CDECL")
  s := StrGet(&b, 4, "utf-8") . StrGet(&d, 4, "utf-8") . StrGet(&c, 4, "utf-8") . " "
  format = %A_FormatInteger%       ; save original integer format
 
  SetFormat Integer, hex   ; lowercase 'h'ex for lowercase hex letters in AHK_L
  VarSetCapacity(a,4,0), VarSetCapacity(b,4,0), VarSetCapacity(c,4,0), VarSetCapacity(d,4,0)
  dllcall(&CPUID,"UInt",1, "Uint*",a, "UInt*",b, "UInt*",c, "UInt*",d, "CDECL")
  s .= a . " " . c . " " . d . " "
 
  VarSetCapacity(a,4,0), VarSetCapacity(b,4,0), VarSetCapacity(c,4,0), VarSetCapacity(d,4,0)
  dllcall(&CPUID,"UInt",2, "Uint*",a, "UInt*",b, "UInt*",c, "UInt*",d, "CDECL")
  s .= a . " " . b . " " . c . " " . d
 
  SetFormat Integer, %format%      ; restore original format
  return s
}

MCode(ByRef code, hex) { ; allocate memory and write Machine Code there
   VarSetCapacity(code,StrLen(hex)//2)
   Loop % StrLen(hex)//2
      NumPut("0x" . SubStr(hex,2*A_Index-1,2), code, A_Index-1, "Char")
}

the first dllcall line (3rd line of the func) is throwing the CONTINUABLE ACCESS VIOLATION

i originally got this code from this link:
https://autohotkey.com/board/topic/5427-sw-copy-protection/page-18

i simply changed the 4th line to do StrGet to get the unicode. the func works on my machine, but one of my users is getting the CONTINUABLE ACCESS VIOLATION

any ideas? is maybe the MCode func wrong for unicode or wrong for AHK_H?

Helgef
Posts: 4709
Joined: 17 Jul 2016, 01:02
Contact:

Re: intermittent CONTINUABLE EXCEPTION_ACCESS_VIOLATION

Post by Helgef » 19 Jun 2020, 04:20

If the function was written for ANSI build, you should probably use "astr" rather than "str" if you run it on unicode build.

Also note,
setformat wrote:If the slow mode "Integer" or "Float" is used anywhere in the script, even if that SetFormat line is never executed, the caching of integers or floating point numbers (respectively) is disabled the moment the script launches.
This slows down your script, that might not matter ofc.

Cheers.
guest3456
Posts: 3463
Joined: 09 Oct 2013, 10:31

Re: intermittent CONTINUABLE EXCEPTION_ACCESS_VIOLATION

Post by guest3456 » 19 Jun 2020, 12:23

Helgef wrote:
19 Jun 2020, 04:20
If the function was written for ANSI build, you should probably use "astr" rather than "str" if you run it on unicode build.
i tried changing to AStr in the test script above, and it seems to now omit the first "GenuineIntel" string from the CPUID when run on Unicode

leaving it as Str works for both ANSI and Unicode builds

Helgef wrote:
19 Jun 2020, 04:20
Also note,
setformat wrote:If the slow mode "Integer" or "Float" is used anywhere in the script, even if that SetFormat line is never executed, the caching of integers or floating point numbers (respectively) is disabled the moment the script launches.
This slows down your script, that might not matter ofc.
noted thanks!

guest3456
Posts: 3463
Joined: 09 Oct 2013, 10:31

Re: intermittent CONTINUABLE EXCEPTION_ACCESS_VIOLATION

Post by guest3456 » 21 Jun 2020, 22:42

looks like the MCode needed VirtualProtect... after adding it, the user reported no error

Post Reply

Return to “Ask for Help”