How to capture an external function inside of thread? Topic is solved

Post AHK_H specific scripts & libraries and discuss the usage and development of HotKeyIt's fork/branch
User avatar
manehscripts
Posts: 126
Joined: 03 May 2019, 16:10

How to capture an external function inside of thread?

Post by manehscripts » 10 Feb 2021, 01:56

Please, someone can help me?
Here is an example (line 36):

Code: Select all

#NoEnv
#NoTrayIcon

Global StopDll, share
share:=CriticalObject({DllExternalFuncOn:"",ExternalMSG:""})
StopDll:=CriticalObject({StopDllExternalFuncOn:0})

share.ExternalMSG := "External Message! No Function"

; GET MSG FROM EXTERNAL FUNCTION
ExternalFunc()

Gui, Add, Button, x5 w440 gExternalFunc, Click to get the message from External Function in Thread
Gui, Show, w450 , External Function
return

ExternalFunc:
    toggleExternalFunc := !toggleExternalFunc
    if (toggleExternalFunc) {
        ahkthread_free(share.DllExternalFuncOn),share.DllExternalFuncOn:=""
        
        scriptActiveExternalFunc := "
        (
            #NoEnv
            #NoTrayIcon
			share := CriticalObject(" (&share) ")
			StopDll := CriticalObject(" (&StopDll) ")
            CoordMode, Tooltip
            
            ExternalMSG := share.ExternalMSG
            MsgBox, `%ExternalMSG`% ; OK WILL GET EXTERNAL MESSAGE

            Loop
            {
				; `%ExternalFunc()`%  ; <----- Dont work
				ExternalFunc()  ; <----- Dont work. How to capture an external function?
            }
        )"
		StopDll.StopDllExternalFuncOn := 0
		while !share.DllExternalFuncOn.ahkReady() {
			share.DllExternalFuncOn := AhkThread(scriptActiveExternalFunc)
		}
    } else {
		StopDll.StopDllExternalFuncOn := 1
		while share.DllExternalFuncOn.ahkReady() {
			Sleep, 500
		}
    }
return

ExternalFunc() {
    MsgBox, Message from External Function!
}

GuiClose:
ExitApp
-----------------------------------------------------------
Stop to think, shut up to resist, and act to win!

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

Re: How to capture an external function inside of thread?  Topic is solved

Post by HotKeyIt » 10 Feb 2021, 03:30

exe:=AhkExported() -> exe.ahkFunction(""ExternalFunc"") see https://hotkeyit.github.io/v1/docs/commands/ahkFunction.htm

Code: Select all

#NoEnv
#NoTrayIcon

Global StopDll, share
share:=CriticalObject({DllExternalFuncOn:"",ExternalMSG:""})
StopDll:=CriticalObject({StopDllExternalFuncOn:0})

share.ExternalMSG := "External Message! No Function"

; GET MSG FROM EXTERNAL FUNCTION
ExternalFunc()

Gui, Add, Button, x5 w440 gExternalFunc, Click to get the message from External Function in Thread
Gui, Show, w450 , External Function
return

ExternalFunc:
    toggleExternalFunc := !toggleExternalFunc
    if (toggleExternalFunc) {
        ahkthread_free(share.DllExternalFuncOn),share.DllExternalFuncOn:=""
        
        scriptActiveExternalFunc := "
        (
            #NoEnv
            #NoTrayIcon
            exe:=AhkExported()
			share := CriticalObject(" (&share) ")
			StopDll := CriticalObject(" (&StopDll) ")
            CoordMode, Tooltip
            
            ExternalMSG := share.ExternalMSG
            MsgBox, `%ExternalMSG`% ; OK WILL GET EXTERNAL MESSAGE

            Loop
				exe.ahkFunction(""ExternalFunc"")
        )"
		StopDll.StopDllExternalFuncOn := 0
		while !share.DllExternalFuncOn.ahkReady() {
			share.DllExternalFuncOn := AhkThread(scriptActiveExternalFunc)
		}
    } else {
		StopDll.StopDllExternalFuncOn := 1
		while share.DllExternalFuncOn.ahkReady() {
			Sleep, 500
		}
    }
return

ExternalFunc() {
    MsgBox, Message from External Function!
}

GuiClose:
ExitApp

User avatar
manehscripts
Posts: 126
Joined: 03 May 2019, 16:10

Re: How to capture an external function inside of thread?

Post by manehscripts » 26 Feb 2021, 16:47

HotKeyIt wrote:
10 Feb 2021, 03:30
exe:=AhkExported() -> exe.ahkFunction(""ExternalFunc"") see https://hotkeyit.github.io/v1/docs/commands/ahkFunction.htm

Code: Select all

#NoEnv
#NoTrayIcon

Global StopDll, share
share:=CriticalObject({DllExternalFuncOn:"",ExternalMSG:""})
StopDll:=CriticalObject({StopDllExternalFuncOn:0})

share.ExternalMSG := "External Message! No Function"

; GET MSG FROM EXTERNAL FUNCTION
ExternalFunc()

Gui, Add, Button, x5 w440 gExternalFunc, Click to get the message from External Function in Thread
Gui, Show, w450 , External Function
return

ExternalFunc:
    toggleExternalFunc := !toggleExternalFunc
    if (toggleExternalFunc) {
        ahkthread_free(share.DllExternalFuncOn),share.DllExternalFuncOn:=""
        
        scriptActiveExternalFunc := "
        (
            #NoEnv
            #NoTrayIcon
            exe:=AhkExported()
			share := CriticalObject(" (&share) ")
			StopDll := CriticalObject(" (&StopDll) ")
            CoordMode, Tooltip
            
            ExternalMSG := share.ExternalMSG
            MsgBox, `%ExternalMSG`% ; OK WILL GET EXTERNAL MESSAGE

            Loop
				exe.ahkFunction(""ExternalFunc"")
        )"
		StopDll.StopDllExternalFuncOn := 0
		while !share.DllExternalFuncOn.ahkReady() {
			share.DllExternalFuncOn := AhkThread(scriptActiveExternalFunc)
		}
    } else {
		StopDll.StopDllExternalFuncOn := 1
		while share.DllExternalFuncOn.ahkReady() {
			Sleep, 500
		}
    }
return

ExternalFunc() {
    MsgBox, Message from External Function!
}

GuiClose:
ExitApp

Bro, how do I return a parameter from an external function?

Like..

Code: Select all

exe.ahkFunction(""ExternalFunc"")

ExternalFunc(msg) {
    MsgBox, msg
}
-----------------------------------------------------------
Stop to think, shut up to resist, and act to win!

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

Re: How to capture an external function inside of thread?

Post by HotKeyIt » 26 Feb 2021, 22:35

Return value will be always string in ahkFunction:

Code: Select all

#NoEnv
#NoTrayIcon

Global StopDll, share
share:=CriticalObject({DllExternalFuncOn:"",ExternalMSG:""})
StopDll:=CriticalObject({StopDllExternalFuncOn:0})

share.ExternalMSG := "External Message! No Function"

; GET MSG FROM EXTERNAL FUNCTION
ExternalFunc()

Gui, Add, Button, x5 w440 gExternalFunc, Click to get the message from External Function in Thread
Gui, Show, w450 , External Function
return

ExternalFunc:
    toggleExternalFunc := !toggleExternalFunc
    if (toggleExternalFunc) {
        ahkthread_free(share.DllExternalFuncOn),share.DllExternalFuncOn:=""
        
        scriptActiveExternalFunc := "
        (
            #NoEnv
            #NoTrayIcon
            exe:=AhkExported()
			share := CriticalObject(" (&share) ")
			StopDll := CriticalObject(" (&StopDll) ")
            CoordMode, Tooltip
            
            ExternalMSG := share.ExternalMSG
            MsgBox, `%ExternalMSG`% ; OK WILL GET EXTERNAL MESSAGE

            Loop
				ToolTip % exe.ahkFunction(""ExternalFunc"")
        )"
		StopDll.StopDllExternalFuncOn := 0
		while !share.DllExternalFuncOn.ahkReady() {
			share.DllExternalFuncOn := AhkThread(scriptActiveExternalFunc)
		}
    } else {
		StopDll.StopDllExternalFuncOn := 1
		while share.DllExternalFuncOn.ahkReady() {
			Sleep, 500
		}
    }
return

ExternalFunc() {
    return "Message from External Function!`n" A_TickCount
}

GuiClose:
ExitApp

User avatar
manehscripts
Posts: 126
Joined: 03 May 2019, 16:10

Re: How to capture an external function inside of thread?

Post by manehscripts » 27 Feb 2021, 02:27

@HotKeyIt, hello!
I'm trying to use Gdip's functions, but they have several parameters that I need to pass inside the Thread ... I would like to know the correct way to put these parameters.

Exemplo of Gdip_DrawImage function:

Code: Select all

Gdip_DrawImage(pGraphics, pBitmap, dx:="", dy:="", dw:="", dh:="", sx:="", sy:="", sw:="", sh:="", Matrix:=1, Unit:=2, ImageAttr:=0) {
...
}

I'm trying to call the functions inside Thread this way, but it doesn't seem to be working. Nothing is being returned ..
And when it arrives at the function Gdip_DrawImage, it presents a fatal error. Error: CONTINUABLE EXCEPTION_ACCESS_VIOLATION

Code: Select all

imagefull = `%A_ScriptDir`%\image.png
bh := getExternalFunc.ahkFunction(""Gdip_CreateBitmapFromFile"",""imagefull"") 
hw := getExternalFunc.ahkFunction(""Gdip_GetImageWidth"",""bh"")
hh := getExternalFunc.ahkFunction(""Gdip_GetImageHeight"",""bh"")
bh1 := getExternalFunc.ahkFunction(""Gdip_CreateBitmap"",""hw"",""hh"")
H := getExternalFunc.ahkFunction(""Gdip_GraphicsFromImage"",""bh1"")
getExternalFunc.ahkFunction(""QPX"",""True"")
MatrixGreyScale = 0.299|0.299|0.299|0|0|0.587|0.587|0.587|0|0|0.114|0.114|0.114|0|0|0|0|0|1|0|0|0|0|0|1
getExternalFunc.ahkFunction(""Gdip_DrawImage"",""H"",""bh"",""0"",""0"",""hw"",""hh"",""0"",""0"",""hw"",""hh"",""MatrixGreyScale"")
Ti :=  getExternalFunc.ahkFunction(""QPX"",""False"")
-----------------------------------------------------------
Stop to think, shut up to resist, and act to win!

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

Re: How to capture an external function inside of thread?

Post by HotKeyIt » 27 Feb 2021, 09:00

All Parameters need to be converted to string

Code: Select all

imagefull = `%A_ScriptDir`%\image.png
bh := getExternalFunc.ahkFunction(""Gdip_CreateBitmapFromFile"",imagefull """") 
hw := getExternalFunc.ahkFunction(""Gdip_GetImageWidth"",bh """")
hh := getExternalFunc.ahkFunction(""Gdip_GetImageHeight"",bh """")
bh1 := getExternalFunc.ahkFunction(""Gdip_CreateBitmap"",hw """",hh """")
H := getExternalFunc.ahkFunction(""Gdip_GraphicsFromImage"",bh1 """")
getExternalFunc.ahkFunction(""QPX"",True """")
MatrixGreyScale = 0.299|0.299|0.299|0|0|0.587|0.587|0.587|0|0|0.114|0.114|0.114|0|0|0|0|0|1|0|0|0|0|0|1
getExternalFunc.ahkFunction(""Gdip_DrawImage"",H """",bh """",""0"",""0"",hw """",hh """",""0"",""0"",hw """",hh """",MatrixGreyScale """")
Ti :=  getExternalFunc.ahkFunction(""QPX"",False """")

User avatar
manehscripts
Posts: 126
Joined: 03 May 2019, 16:10

Re: How to capture an external function inside of thread?

Post by manehscripts » 27 Feb 2021, 11:42

Now I can understand how it works, thanks! The exact values are returning, however, exactly in Gdip_DrawImage it still presents a fatal error.

Code: Select all

imagefull = `%A_ScriptDir`%\image.png
bh := getExternalFunc.ahkFunction(""Gdip_CreateBitmapFromFile"",imagefull """") 
hw := getExternalFunc.ahkFunction(""Gdip_GetImageWidth"",bh """")
hh := getExternalFunc.ahkFunction(""Gdip_GetImageHeight"",bh """")
bh1 := getExternalFunc.ahkFunction(""Gdip_CreateBitmap"",hw """",hh """")
H := getExternalFunc.ahkFunction(""Gdip_GraphicsFromImage"",bh1 """")
getExternalFunc.ahkFunction(""QPX"",True """")
MatrixGreyScale = 0.299|0.299|0.299|0|0|0.587|0.587|0.587|0|0|0.114|0.114|0.114|0|0|0|0|0|1|0|0|0|0|0|1

MsgBox, `%bh`% | `%hw`% | `%hh`% | `%bh1`% | `%H`% | `%MatrixGreyScale`% ; OK
MsgBox, Below will cause an error

getExternalFunc.ahkFunction(""Gdip_DrawImage"",H """",bh """",""0"",""0"",hw """",hh """",""0"",""0"",hw """",hh """",MatrixGreyScale """")
Ti :=  getExternalFunc.ahkFunction(""QPX"",False """")
Screenshot_1.png
Screenshot_1.png (16.17 KiB) Viewed 8045 times

Function Gdip_DrawImage:

Code: Select all

;#####################################################################################

; Function		  Gdip_DrawImage
; Description	  This function draws a bitmap into the Graphics of another bitmap
;
; pGraphics		 Pointer to the Graphics of a bitmap
; pBitmap			Pointer to a bitmap to be drawn
; dX, dY			 x, y coordinates of the destination upper-left corner
; dW, dH			 width and height of the destination image
; sX, sY			 x, y coordinates of the source upper-left corner
; sW, sH			 width and height of the source image
; Matrix			 a color matrix used to alter image attributes when drawing
; Unit				Unit of measurement:
;					  0 - World coordinates, a nonphysical unit
;					  1 - Display units
;					  2 - A unit is 1 pixel
;					  3 - A unit is 1 point or 1/72 inch
;					  4 - A unit is 1 inch
;					  5 - A unit is 1/300 inch
;					  6 - A unit is 1 millimeter
;
; return			 status enumeration. 0 = success
;
; notes			  When sx,sy,sw,sh are omitted the entire source bitmap will be used
;					  Gdip_DrawImage performs faster.
;					  Matrix can be omitted to just draw with no alteration to ARGB
;					  Matrix may be passed as a digit from 0.0 - 1.0 to change just transparency
;					  Matrix can be passed as a matrix with "|" as delimiter. For example:
;					  MatrixBright=
;					  (
;					  1.5	|0	 |0	 |0	 |0
;					  0	  |1.5  |0	 |0	 |0
;					  0	  |0	 |1.5  |0	 |0
;					  0	  |0	 |0	 |1	 |0
;					  0.05  |0.05 |0.05 |0	 |1
;					  )
;
; example color matrix:
;					  MatrixBright = 1.5|0|0|0|0|0|1.5|0|0|0|0|0|1.5|0|0|0|0|0|1|0|0.05|0.05|0.05|0|1
;					  MatrixGreyScale = 0.299|0.299|0.299|0|0|0.587|0.587|0.587|0|0|0.114|0.114|0.114|0|0|0|0|0|1|0|0|0|0|0|1
;					  MatrixNegative = -1|0|0|0|0|0|-1|0|0|0|0|0|-1|0|0|0|0|0|1|0|1|1|1|0|1
;					  To generate a color matrix using user-friendly parameters,
;					  use GenerateColorMatrix()

Gdip_DrawImage(pGraphics, pBitmap, dx:="", dy:="", dw:="", dh:="", sx:="", sy:="", sw:="", sh:="", Matrix:=1, Unit:=2, ImageAttr:=0) {
	Static Ptr := "UPtr"
	If !ImageAttr
	{
		if !IsNumber(Matrix)
			ImageAttr := Gdip_SetImageAttributesColorMatrix(Matrix)
		else if (Matrix!=1)
			ImageAttr := Gdip_SetImageAttributesColorMatrix("1|0|0|0|0|0|1|0|0|0|0|0|1|0|0|0|0|0|" Matrix "|0|0|0|0|0|1")
	} Else usrImageAttr := 1

	If (dx!="" && dy!="" && dw="" && dh="" && sx="" && sy="" && sw="" && sh="")
	{
		sx := sy := 0
		sw := dw := Gdip_GetImageWidth(pBitmap)
		sh := dh := Gdip_GetImageHeight(pBitmap)
	} Else If (sx="" && sy="" && sw="" && sh="")
	{
		If (dx="" && dy="" && dw="" && dh="")
		{
			sx := dx := 0, sy := dy := 0
			sw := dw := Gdip_GetImageWidth(pBitmap)
			sh := dh := Gdip_GetImageHeight(pBitmap)
		} Else
		{
			sx := sy := 0
			Gdip_GetImageDimensions(pBitmap, sw, sh)
		}
	}

	_E := DllCall("gdiplus\GdipDrawImageRectRect"
				, Ptr, pGraphics
				, Ptr, pBitmap
				, "float", dX, "float", dY
				, "float", dW, "float", dH
				, "float", sX, "float", sY
				, "float", sW, "float", sH
				, "int", Unit
				, Ptr, ImageAttr ? ImageAttr : 0
				, Ptr, 0, Ptr, 0)

	if (ImageAttr && usrImageAttr!=1)
		Gdip_DisposeImageAttributes(ImageAttr)

	return _E
}
-----------------------------------------------------------
Stop to think, shut up to resist, and act to win!

User avatar
manehscripts
Posts: 126
Joined: 03 May 2019, 16:10

Re: How to capture an external function inside of thread?

Post by manehscripts » 02 Mar 2021, 00:40

Hey @HotKeyIt. Let me know if you can help me, please.

My main source is complex, and I tried to summarize the maximum so that you could understand. All main code is inside a thread body.
I have the following situation below, where one of the parameters is not returning correctly. This parameter must return 2 values, example: 100,300.
All other values that the Gdip_ImageSearch function receives are correct, only the data result is not being displayed. I've tried it as a string and inside the quotes. Do you know if need to change the function?

Source:

Code: Select all

#NoEnv
#NoTrayIcon
CoordMode, Tooltip
CoordMode, Pixel, Screen
CoordMode, Mouse, Screen
SHARE := CriticalObject({DllTesteGdipOn:""})
STOPDLL := CriticalObject({stopDllTesteGdipOn:0})
gosub, Start
OnExit, ExitProgram
return

Start:
    toggle := !toggle
    if (toggle) {
        ahkthread_free(SHARE.DllTesteGdipOn),SHARE.DllTesteGdipOn:=""
        scriptTesteGdip := "
        (
			#NoEnv
			#NoTrayIcon
			EXTERNAL_FUNC := AhkExported()
			SHARE := CriticalObject(" (&SHARE) ")
			STOPDLL := CriticalObject(" (&STOPDLL) ")
			CoordMode, Tooltip
			CoordMode, Pixel, Screen
			CoordMode, Mouse, Screen

			;~ Functions to generate the result for Gdip_ImageSearch below. OK
			;~ ...
			;~ ...
			;~ ...
			;~ Functions to generate the result for Gdip_ImageSearch below. OK
			
			result := EXTERNAL_FUNC.ahkFunction(""Gdip_ImageSearch"",""102647088"",""102648080"",data """",""0"",""0"",""0"",""0"",""100"")
			MsgBox, `% data
        )"
		STOPDLL.stopDllTesteGdipOn := 0
		while !SHARE.DllTesteGdipOn.ahkReady() {
			SHARE.DllTesteGdipOn := AhkThread(scriptTesteGdip)
		}
    } else {
		STOPDLL.stopDllTesteGdipOn := 1
		while SHARE.DllTesteGdipOn.ahkReady() {
			Sleep, 500
		}
    }
return

ExitProgram:
    ExitApp
return

Esc::
	ExitApp
Functions:

Code: Select all

;**********************************************************************************
;
; Gdip_ImageSearch()
; by MasterFocus - 02/APRIL/2013 00:30h BRT
; Thanks to guest3456 for helping me ponder some ideas
; Requires GDIP, Gdip_SetBitmapTransColor() and Gdip_MultiLockedBitsSearch()
; http://www.autohotkey.com/board/topic/71100-gdip-imagesearch/
;
; Licensed under CC BY-SA 3.0 -> http://creativecommons.org/licenses/by-sa/3.0/
; I waive compliance with the "Share Alike" condition of the license EXCLUSIVELY
; for these users: tic , Rseding91 , guest3456
;
;==================================================================================
;
; This function searches for pBitmapNeedle within pBitmapHaystack
; The returned value is the number of instances found (negative = error)
;
; ++ PARAMETERS ++
;
; pBitmapHaystack and pBitmapNeedle
;	Self-explanatory bitmap pointers, are the only required parameters
;
; OutputList
;	ByRef variable to store the list of coordinates where a match was found
;
; OuterX1, OuterY1, OuterX2, OuterY2
;	Equivalent to ImageSearch's X1,Y1,X2,Y2
;	Default: 0 for all (which searches the whole haystack area)
;
; Variation
;	Just like ImageSearch, a value from 0 to 255
;	Default: 0
;
; Trans
;	Needle RGB transparent color, should be a numerical value from 0 to 0xFFFFFF
;	Default: blank (does not use transparency)
;
; SearchDirection
;	Haystack search direction
;	  Vertical preference:
;		 1 = top->left->right->bottom [default]
;		 2 = bottom->left->right->top
;		 3 = bottom->right->left->top
;		 4 = top->right->left->bottom
;	  Horizontal preference:
;		 5 = left->top->bottom->right
;		 6 = left->bottom->top->right
;		 7 = right->bottom->top->left
;		 8 = right->top->bottom->left
;
; Instances
;	Maximum number of instances to find when searching (0 = find all)
;	Default: 1 (stops after one match is found)
;
; LineDelim and CoordDelim
;	Outer and inner delimiters for the list of coordinates (OutputList)
;	Defaults: "`n" and ","
;
; ++ RETURN VALUES ++
;
; -1001 ==> invalid haystack and/or needle bitmap pointer
; -1002 ==> invalid variation value
; -1003 ==> X1 and Y1 cannot be negative
; -1004 ==> unable to lock haystack bitmap bits
; -1005 ==> unable to lock needle bitmap bits
; any non-negative value ==> the number of instances found
;
;==================================================================================
;
;**********************************************************************************

Gdip_ImageSearch(pBitmapHaystack,pBitmapNeedle,ByRef OutputList="",OuterX1=0,OuterY1=0,OuterX2=0,OuterY2=0,Variation=0,Trans="",SearchDirection=1,Instances=1,LineDelim="`n",CoordDelim=",") {

	 ; Some validations that can be done before proceeding any further
	 If !( pBitmapHaystack && pBitmapNeedle )
		  Return -1001
	 If Variation not between 0 and 255
		  return -1002
	 If ( ( OuterX1 < 0 ) || ( OuterY1 < 0 ) )
		  return -1003
	 If SearchDirection not between 1 and 8
		  SearchDirection := 1
	 If ( Instances < 0 )
		  Instances := 0

	 ; Getting the dimensions and locking the bits [haystack]
	 Gdip_GetImageDimensions(pBitmapHaystack,hWidth,hHeight)
	 ; Last parameter being 1 says the LockMode flag is "READ only"
	 If Gdip_LockBits(pBitmapHaystack,0,0,hWidth,hHeight,hStride,hScan,hBitmapData,1)
	 OR !(hWidth := NumGet(hBitmapData,0,"UInt"))
	 OR !(hHeight := NumGet(hBitmapData,4,"UInt"))
		  Return -1004

	 ; Careful! From this point on, we must do the following before returning:
	 ; - unlock haystack bits

	 ; Getting the dimensions and locking the bits [needle]
	 Gdip_GetImageDimensions(pBitmapNeedle,nWidth,nHeight)
	 ; If Trans is correctly specified, create a backup of the original needle bitmap
	 ; and modify the current one, setting the desired color as transparent.
	 ; Also, since a copy is created, we must remember to dispose the new bitmap later.
	 ; This whole thing has to be done before locking the bits.
	 If Trans between 0 and 0xFFFFFF
	 {
		  pOriginalBmpNeedle := pBitmapNeedle
		  pBitmapNeedle := Gdip_CloneBitmapArea(pOriginalBmpNeedle,0,0,nWidth,nHeight)
		  Gdip_SetBitmapTransColor(pBitmapNeedle,Trans)
		  DumpCurrentNeedle := true
	 }

	 ; Careful! From this point on, we must do the following before returning:
	 ; - unlock haystack bits
	 ; - dispose current needle bitmap (if necessary)

	 If Gdip_LockBits(pBitmapNeedle,0,0,nWidth,nHeight,nStride,nScan,nBitmapData)
	 OR !(nWidth := NumGet(nBitmapData,0,"UInt"))
	 OR !(nHeight := NumGet(nBitmapData,4,"UInt"))
	 {
		  If ( DumpCurrentNeedle )
				Gdip_DisposeImage(pBitmapNeedle)
		  Gdip_UnlockBits(pBitmapHaystack,hBitmapData)
		  Return -1005
	 }
	 
	 ; Careful! From this point on, we must do the following before returning:
	 ; - unlock haystack bits
	 ; - unlock needle bits
	 ; - dispose current needle bitmap (if necessary)

	 ; Adjust the search box. "OuterX2,OuterY2" will be the last pixel evaluated
	 ; as possibly matching with the needle's first pixel. So, we must avoid going
	 ; beyond this maximum final coordinate.
	 OuterX2 := ( !OuterX2 ? hWidth-nWidth+1 : OuterX2-nWidth+1 )
	 OuterY2 := ( !OuterY2 ? hHeight-nHeight+1 : OuterY2-nHeight+1 )

	 OutputCount := Gdip_MultiLockedBitsSearch(hStride,hScan,hWidth,hHeight
	 ,nStride,nScan,nWidth,nHeight,OutputList,OuterX1,OuterY1,OuterX2,OuterY2
	 ,Variation,SearchDirection,Instances,LineDelim,CoordDelim)

	 Gdip_UnlockBits(pBitmapHaystack,hBitmapData)
	 Gdip_UnlockBits(pBitmapNeedle,nBitmapData)
	 If ( DumpCurrentNeedle )
		  Gdip_DisposeImage(pBitmapNeedle)

	 Return OutputCount
}

;#####################################################################################

; Function			  Gdip_GetImageDimensions
; Description		  Gives the width and height of a bitmap
;
; pBitmap				Pointer to a bitmap
; Width				  ByRef variable. This variable will be set to the width of the bitmap
; Height				 ByRef variable. This variable will be set to the height of the bitmap
;
; return				 GDI+ status enumeration return value

Gdip_GetImageDimensions(pBitmap, ByRef Width, ByRef Height) {
	If StrLen(pBitmap)<3
		Return -1

	Width := 0, Height := 0
	E := Gdip_GetImageDimension(pBitmap, Width, Height)
	Width := Round(Width)
	Height := Round(Height)
	return E
}


Gdip_CloneBitmapArea(pBitmap, x:="", y:="", w:=0, h:=0, PixelFormat:=0, KeepPixelFormat:=0) {
; The new pBitmap is by default in the 32-ARGB PixelFormat.
;
; If the specified coordinates exceed the boundaries of pBitmap
; the resulted pBitmap is erroneuous / defective.
	pBitmapDest := 0
	If !PixelFormat
		PixelFormat := 0x26200A	 ; 32-ARGB

	If (KeepPixelFormat=1)
		PixelFormat := Gdip_GetImagePixelFormat(pBitmap, 1)

	If (y="")
		y := 0

	If (x="")
		x := 0

	If (!w && !h)
		Gdip_GetImageDimensions(pBitmap, w, h)

	E := DllCall("gdiplus\GdipCloneBitmapArea"
					, "float", x, "float", y
					, "float", w, "float", h
					, "int", PixelFormat
					, "UPtr", pBitmap
					, "UPtr*", pBitmapDest)
	return pBitmapDest
}

;#####################################################################################
; BitmapLockBits
;#####################################################################################

Gdip_LockBits(pBitmap, x, y, w, h, ByRef Stride, ByRef Scan0, ByRef BitmapData, LockMode := 3, PixelFormat := 0x26200a) {
	Static Ptr := "UPtr"

	CreateRect(_Rect, x, y, w, h)
	VarSetCapacity(BitmapData, 16+2*A_PtrSize, 0)
	_E := DllCall("Gdiplus\GdipBitmapLockBits", Ptr, pBitmap, Ptr, &_Rect, "uint", LockMode, "int", PixelFormat, Ptr, &BitmapData)
	Stride := NumGet(BitmapData, 8, "Int")
	Scan0 := NumGet(BitmapData, 16, Ptr)
	return _E
}

Gdip_DisposeImage(pBitmap, noErr:=0) {
; modified by Marius ?ucan to help avoid crashes 
; by disposing a non-existent pBitmap

	If (StrLen(pBitmap)<=2 && noErr=1)
		Return 0

	r := DllCall("gdiplus\GdipDisposeImage", "UPtr", pBitmap)
	If (r=2 || r=1) && (noErr=1)
		r := 0
	Return r
}

Gdip_GetImageDimension(pBitmap, ByRef w, ByRef h) {
	Static Ptr := "UPtr"
	return DllCall("gdiplus\GdipGetImageDimension", Ptr, pBitmap, "float*", w, "float*", h)
}


Gdip_GetImagePixelFormat(pBitmap, mode:=0) {
; Mode options 
; 0 - in decimal
; 1 - in hex
; 2 - in human readable format
;
; PXF01INDEXED = 0x00030101  ; 1 bpp, indexed
; PXF04INDEXED = 0x00030402  ; 4 bpp, indexed
; PXF08INDEXED = 0x00030803  ; 8 bpp, indexed
; PXF16GRAYSCALE = 0x00101004; 16 bpp, grayscale
; PXF16RGB555 = 0x00021005	; 16 bpp; 5 bits for each RGB
; PXF16RGB565 = 0x00021006	; 16 bpp; 5 bits red, 6 bits green, and 5 bits blue
; PXF16ARGB1555 = 0x00061007 ; 16 bpp; 1 bit for alpha and 5 bits for each RGB component
; PXF24RGB = 0x00021808	; 24 bpp; 8 bits for each RGB
; PXF32RGB = 0x00022009	; 32 bpp; 8 bits for each RGB, no alpha.
; PXF32ARGB = 0x0026200A  ; 32 bpp; 8 bits for each RGB and alpha
; PXF32PARGB = 0x000E200B ; 32 bpp; 8 bits for each RGB and alpha, pre-mulitiplied
; PXF48RGB = 0x0010300C	; 48 bpp; 16 bits for each RGB
; PXF64ARGB = 0x0034400D  ; 64 bpp; 16 bits for each RGB and alpha
; PXF64PARGB = 0x001A400E ; 64 bpp; 16 bits for each RGB and alpha, pre-multiplied

; INDEXED [1-bits, 4-bits and 8-bits] pixel formats rely on color palettes.
; The color information for the pixels is stored in palettes.
; Indexed images always contain a palette - a special table of colors.
; Each pixel is an index in this table. Usually a palette contains 256
; or less entries. That's why the maximum depth of an indexed pixel is 8 bpp.
; Using palettes is a common practice when working with small color depths.

; modified by Marius ?ucan

	Static PixelFormatsList := {0x30101:"1-INDEXED", 0x30402:"4-INDEXED", 0x30803:"8-INDEXED", 0x101004:"16-GRAYSCALE", 0x021005:"16-RGB555", 0x21006:"16-RGB565", 0x61007:"16-ARGB1555", 0x21808:"24-RGB", 0x22009:"32-RGB", 0x26200A:"32-ARGB", 0xE200B:"32-PARGB", 0x10300C:"48-RGB", 0x34400D:"64-ARGB", 0x1A400E:"64-PARGB"}
	PixelFormat := 0
	E := DllCall("gdiplus\GdipGetImagePixelFormat", "UPtr", pBitmap, "UPtr*", PixelFormat)
	If E
		Return -1

	If (mode=0)
		Return PixelFormat

	inHEX := Format("{1:#x}", PixelFormat)
	If (PixelFormatsList.Haskey(inHEX) && mode=2)
		result := PixelFormatsList[inHEX]
	Else
		result := inHEX
	return result
}

;#####################################################################################

; Function			  CreateRectF
; Description		  Creates a RectF object, containing a the coordinates and dimensions of a rectangle
;
; RectF				  Name to call the RectF object
; x, y					x, y coordinates of the upper left corner of the rectangle
; w, h					Width and height of the rectangle
;
; return				 No return value

CreateRectF(ByRef RectF, x, y, w, h) {
	VarSetCapacity(RectF, 16)
	NumPut(x, RectF, 0, "float"), NumPut(y, RectF, 4, "float")
	NumPut(w, RectF, 8, "float"), NumPut(h, RectF, 12, "float")
}
-----------------------------------------------------------
Stop to think, shut up to resist, and act to win!

swagfag
Posts: 6222
Joined: 11 Jan 2017, 17:59

Re: How to capture an external function inside of thread?

Post by swagfag » 02 Mar 2021, 06:22

  • considering only string parameters are supported, ByRef clearly wont work in this case
  • i dont get why ure making calls from the child thread to the parent thread to have it call some GDI+ functions while shuffling the arguments back and forth. instead, separate the GDI+ functions into a library file and #Include it in both thread
  • i dont know how u got the idea of hardcoding what appears to be the addresses of bitmaps or where u even got these numbers from, but that clearly has no chance of working. i cant exactly recall if GDI+ was thread-safe but it probably isnt, so depending on where ure getting those bitmaps from and what's accessing them, ull probably have to synchronize the access somehow

Qhimin
Posts: 16
Joined: 30 Nov 2020, 19:24

Re: How to capture an external function inside of thread?

Post by Qhimin » 02 Mar 2021, 09:05

swagfag wrote:
02 Mar 2021, 06:22
  • considering only string parameters are supported, ByRef clearly wont work in this case
  • i dont get why ure making calls from the child thread to the parent thread to have it call some GDI+ functions while shuffling the arguments back and forth. instead, separate the GDI+ functions into a library file and #Include it in both thread
  • i dont know how u got the idea of hardcoding what appears to be the addresses of bitmaps or where u even got these numbers from, but that clearly has no chance of working. i cant exactly recall if GDI+ was thread-safe but it probably isnt, so depending on where ure getting those bitmaps from and what's accessing them, ull probably have to synchronize the access somehow
I'm facing a similar problem and ended up in this topic.
Your points are valid but what do you suggest to use instead of a ByRef? I've tried to use ObjByRef but no success either.
About Gdip specifically, if we use that as external file library with #Include it brings up the same errors that @manehscripts already showed us, while using ahkFunction().

Thank you very much for ur help :D

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

Re: How to capture an external function inside of thread?

Post by HotKeyIt » 02 Mar 2021, 23:20

Suggestion was not to use ahkFunction() but instead use the Gdip functions directly via #include.
However you can wrap the function into Class:

Code: Select all

SetBatchLines, -1
dll:=AhkThread("
(
#Persistent
SetBatchLines, -1
Class CallFunc {
	myfun(ByRef a, b, c){
		a:=b*c
		return ""success"" 
	}
}
)")
Alias(CallFunc,dll.ahkgetvar("CallFunc",1))
MsgBox % CallFunc.myFun(a,2,3)
MsgBox % a

swagfag
Posts: 6222
Joined: 11 Jan 2017, 17:59

Re: How to capture an external function inside of thread?

Post by swagfag » 04 Mar 2021, 12:26

what limited testing ive done with GDI seems to confirm my suspicions about thread-safety, namely that there isnt any.

Code: Select all

#Requires AutoHotkey v2.0-a122-f595abc2
#SingleInstance Force

w := h := 50
hdc := GetDC(NULL)
memDC := CreateCompatibleDC(hdc)
hbm := CreateCompatibleBitmap(hdc, w, h)
obm := SelectObject(memDC, hbm)

CritSec := BufferAlloc(40, 0)
InitializeCriticalSectionAndSpinCount(CritSec, 0x400)

Half := {w: w // 2, h: h // 2}

FillParams := Map(
	0xFF0000, {x: 0, y: 0}, ; top-left blue
	0x00FF00, {x: Half.w, y: 0}, ; top-right green
	0x0000FF, {x: 0, y: Half.w}, ; bottom-left red
	0xFF00FF, {x: Half.w, y: Half.h} ; bottom-right magenta
)

for rgb, Origin in FillParams
	fillRegion(memDC, rgb, Origin.x, Origin.y, Half.w, Half.h)

fillRegion(hdc, rgb, x0, y0, w, h) {
	Loop h
	{
		y := y0 + A_Index - 1

		Loop w
		{
			x := x0 + A_Index - 1

			SetPixel(hdc, x, y, rgb)
		}
	}
}

Threads := []
Loop 4
	Threads.Push(prepareThread(CritSec.Ptr, memDC, w, h))

for T in Threads	
	T.AhkPause(false) ; unpause

prepareThread(pCritSec, hdc, w, h) {
	cmdLine := Format('{} {} {} {}', pCritSec, hdc, w, h)

	return AhkThread('
		(
			pCritSec := A_Args[1]
			hdc := A_Args[2] 
			w := A_Args[3] 
			h := A_Args[4]

			Pause

			EnterCriticalSection(pCritSec) ; COMMENT ME OUT

			pixels := ""
			Loop h
			{
				y := A_Index - 1

				Loop w
				{
					x := A_Index - 1

					switch GetPixel(hdc, x, y)
					{
					case 0xFF0000: pixels .= "b"	
					case 0x00FF00: pixels .= "g"	
					case 0x0000FF: pixels .= "r"	
					case 0xFF00FF: pixels .= "m"	
					}
				}

				pixels .= "``n"
			}

			LeaveCriticalSection(pCritSec) ; COMMENT ME OUT

			MsgBox(pixels, A_ThreadID)
		)', cmdLine)
}

G := Gui.New('+AlwaysOnTop +ToolWindow -Caption')
G.Add('Picture', 'w' w ' h' h, 'HBITMAP:' hbm)
G.Show()

Esc::ExitApp()
Unsynchronized
Synchronized

feel free to test GDI+ urself if ure keen on doing that but chances are as already mentioned its not thread-safe either. so ull have to divvy up the problem into chunks such that they can be processed independently of one another(ie, u cant Bitmap::LockBits a region and bang on it with multiple threads. u can try doing it anyway but its probably gonna error out best case scenario or produce hardly debuggable errant values, worst case)

robodesign
Posts: 934
Joined: 30 Sep 2017, 03:59
Location: Romania
Contact:

Re: How to capture an external function inside of thread?

Post by robodesign » 19 Feb 2022, 08:25

Yes, GDI+ is not thread safe.

When one uses GDI+ start-up function, it can be used in any thread, and all the operations will be serialized in execution. If one attempts to work with the same objects and bitmaps, errors can occur - "object busy".

Conclusion, it's not worth bothering to use ahk-h to multi-thread anything related to GDI+.

You can use FreeImage library, GDI, D2D or WIC, these allow multi-threading.

Best regards, Marius.
-------------------------
KeyPress OSD v4: GitHub or forum. (presentation video)
Quick Picto Viewer: GitHub or forum.
AHK GDI+ expanded / compilation library (on GitHub)
My home page.

Post Reply

Return to “AutoHotkey_H”