[Multi-Threaded AHK_H] Error 0x8001010D - An outgoing call cannot be made [while] dispatching an input-synchronous call

Ask for help, how to use AHK_H, etc.
User avatar
evilC
Posts: 4823
Joined: 27 Feb 2014, 12:30

[Multi-Threaded AHK_H] Error 0x8001010D - An outgoing call cannot be made [while] dispatching an input-synchronous call

21 Feb 2016, 11:48

Image

I have run into an issue with UCR, and am not sure how to proceed, so am seeking any advice on how to work around this issue.

The scenario is thus:
Each profile in UCR has it's own "Input thread" that contains all hotkey mappings for that profile. When a profile becomes active, the main thread calls the input thread for the current profile and has it Suspend, On all it's hotkeys, then the main thread calls the input thread for the new active profile and has it Suspend, Off all it's hotkeys. When an input thread's hotkey is triggered, it calls the main thread to have it take the appropriate action.

The problem has arisen when I try to make a plugin that allows you to use a hotkey to change profile.
The below code simulates a setup with two profiles - both profiles are rigged such that F12 changes to the other profile. Each profile is rigged such that F11 produces a beep (To simulate work).
If you spam F11 and F12 simultaneously and rapidly, you can cause the error.
Main thread:

Code: Select all

#SingleInstance force
#Persistent
OutputDebug DBGVIEWCLEAR

global UCR
new MainClass()
return

Class MainClass {
	Profiles := {}
	CurrentProfile := ""
	__New(){
		global UCR := this
		this.Profiles.Profile1 := new _Profile(this, "Profile1", "Profile2", 1000)
		this.Profiles.Profile2 := new _Profile(this, "Profile2", "Profile1", 500)
		this.ChangeProfile("Profile1")
	}
	
	ChangeProfile(name){
		CurrentProfile := this.Profiles[this.CurrentProfile]
		if (IsObject(CurrentProfile))
			CurrentProfile.DeActivate()
		this.CurrentProfile := name
		this.Profiles[name].Activate()
	}
}

Class _Profile {
	__New(parent, name, switchto, beepfreq){
		this.Parent := parent
		this.Name := name
		this.SwitchTo := switchto
		this.BeepFreq := beepfreq
		this._InputThread := AhkThread(A_ScriptDir "\inputthread.ahk",,1) ; Loads the AutoHotkey module and starts the script.
		While !this._InputThread.ahkgetvar.autoexecute_done
			Sleep 50 ; wait until variable has been set.
		str := "InputThread := new _InputThread(" ObjShare(this.InputEvent.Bind(this)) ", """ this.Name """)"
		this._InputThread.ahkExec(str)
	}
	
	InputEvent(evt){
		OutputDebug, % "Profile " this.Name " InputEvent - " evt
		if (evt == 1){
			; Simulate some work
			SoundBeep % this.BeepFreq, 500
		} else {
			; Change profile
			UCR.ChangeProfile(this.SwitchTo)
		}
	}
	
	Activate(){
		str := "InputThread.SetState(1)"
		this._InputThread.ahkExec(str)
	}
	
	DeActivate(){
		str := "InputThread.SetState(0)"
		this._InputThread.ahkExec(str)
	}
}
Input Thread:

Code: Select all

#Persistent
#NoTrayIcon
#MaxHotkeysPerInterval 9999
autoexecute_done := 1
return

class _InputThread {
	__New(CallbackPtr, name){
		this.Name := name
		this.Callback := ObjShare(CallbackPtr)
		this.MasterThread := AhkExported()
	}
	
	InputEvent(evt){
		this.Callback.Call(evt)
	}
	
	Setstate(state){
		OutputDebug % "Thread " this.Name " " (state ? "On" : "Off")
		if (state){
			; Hotkey makes a beep
			fn := this.InputEvent.Bind(this, 1)
			hotkey, F11, % fn, On
			; hotkey switches profile
			fn := this.InputEvent.Bind(this, 2)
			hotkey, F12, % fn, On
		} else {
			fn := this.InputEvent.Bind(this, 1)
			hotkey, F11, % fn, Off
			fn := this.InputEvent.Bind(this, 2)
			hotkey, F12, % fn, Off
		}
	}
}
The inter-thread communication is done using COM objects - see the ObjShare() docs
I guess the problem is to do with the somewhat cyclical nature of the change profile hotkey: The input thread calls the main thread to notify it of hotkey down, the main thread recognizes it as a change profile hotkey and changes the profile, which then notifies the same input thread to turn off it's hotkeys.
However, it seems a little more complicated than that.
Can anyone shed any more light on the subject?
HotKeyIt
Posts: 2364
Joined: 29 Sep 2013, 18:35
Contact:

Re: [Multi-Threaded AHK_H] Error 0x8001010D - An outgoing call cannot be made [while] dispatching an input-synchronous c

21 Feb 2016, 13:28

You must not use ahkExec because it uses SendMessage to execute code and this conflicts with ObjShare.

Code: Select all

#SingleInstance force
#Persistent
OutputDebug DBGVIEWCLEAR
 
global UCR
new MainClass()
return
 
Class MainClass {
	Profiles := {}
	CurrentProfile := ""
	__New(){
		global UCR := this
		this.Profiles.Profile1 := new _Profile(this, "Profile1", "Profile2", 1000)
		this.Profiles.Profile2 := new _Profile(this, "Profile2", "Profile1", 500)
		this.ChangeProfile("Profile1")
	}
 
	ChangeProfile(name){
		CurrentProfile := this.Profiles[this.CurrentProfile]
		if (IsObject(CurrentProfile))
			CurrentProfile.SetState(0)
		this.CurrentProfile := name
		this.Profiles[name].SetState(1)
	}
}
 
Class _Profile {
	__New(parent, name, switchto, beepfreq){
		this.Parent := parent
		this.Name := name
		this.SwitchTo := switchto
		this.BeepFreq := beepfreq
		FileRead, Script, % A_ScriptDir "\inputthread.ahk"
		; Load the AutoHotkey module and starts the script.
		this._InputThread := AhkThread("
							(
							InputThread := new _InputThread(" ObjShare(this.InputEvent.Bind(this)) ", """ this.Name """)
							_SetState := ObjShare(InputThread.SetState.Bind(InputThread))
							)`n" Script)
		While !this._InputThread.ahkgetvar.autoexecute_done
			Sleep 50 ; wait until variable has been set.
		this.SetState := ObjShare(this._InputThread.ahkgetvar("_SetState"))
	}
 
	InputEvent(evt){
		OutputDebug, % "Profile " this.Name " InputEvent - " evt
		if (evt == 1){
			; Simulate some work
			SoundBeep % this.BeepFreq, 500
		} else {
			; Change profile
			UCR.ChangeProfile(this.SwitchTo)
		}
	}
}

Return to “Ask for Help”

Who is online

Users browsing this forum: No registered users and 47 guests