Open evilC opened 7 years ago
It appears that the problem is to do with SendPipeMessage
var ipcHandler = new Func<string, string>(fromAhk =>
{
Console.WriteLine("received message from ahk " + fromAhk);
return ".NET: I LIKE PIE!";
});
ahk.ExecRaw(@"
F11::
Tooltip, % ""HERE - "" A_TickCount
return
F12::
SendPipeMessage(""HERE - "" A_TickCount)
return
");
The F11 hotkey displays a tooltip reliably on every press, but the F12 hotkey does not.
The problem appears to be due to the fact that SendPipeMessage is a two-way communication:
SendPipeMessage(strMessage) {
global A__PIPECLIENT
A__PIPECLIENT.write(strMessage)
sleep, 100
A__PIPECLIENT_RESULT := A__PIPECLIENT.read()
sleep, 100
return A__PIPECLIENT_RESULT
}
Those sleep 100s clearly are causing the problem.
If I comment out everything after A__PIPECLIENT.write(strMessage)
and ignore the return value, I get callbacks for all hotkeys.
All I actually want is one-way communication from the script to the host C# code (ie an Action instead of a Func) I tried to change the code in this way, but had no joy.
I have been chatting to HotkeyIt about this, and he reckons this could also be done with ObjShare, although he hasn't done C# before and I have not got a clue how this all works. We'll keep beavering away, but if you are about then I could really do with some help on this one.
How does the program behave, if you just take the sleeps out ?
Yeah it seems OK if I take them out and don't try to do anything with a return value, but I don't know enough about it to know if it is safe to do so, or could be more performant without it.
HotkeyIt also thought it would be better to implement communication via ObjShare, but neither of use are really sure how to implement this in C# - any ideas?
https://github.com/HotKeyIt/ahkdll/blob/master/source/resources/reslib/ObjShare.ahk
The CSharp code acts as a Server by hosting the Named Pipe, used for communication. The AHK code acts as a client by making the connection only when it needs to send something.
Currently the named pipe server behaves like the following:
Overall the .NET and AHK processes are waiting on each other before they continue, so they block each other's threads. The sleeps are probably put in by me for testing purposes. Sometimes I find AHK is more stable when you give it some time around important pieces of code.
If you find that your system handles the calls without the two sleeps well, I don't see an issue with removing them. I agree it may perform better.
Also I've looked at ObjShare, and please correct me if i'm wrong. But it looks like a way to share an AHK object between AHK Threads. I'm not sure how this can translate sending a message to the hosting environment and returning a result back from it. Do you have an example in any programming language of the ObjShare being used to call a function in the hosting environment, outside of AHK and then return the result back to AHK? I can look at this and perhaps find a way to replicate it in CSharp.
Not got time to read this all right now, will get back to it, but for now here is an example of inter-thread communication using a function object wrapped with ObjShare:
Main thread launching second thread, passing callback func object wrapped with ObjShare:
https://github.com/evilC/UCR/blob/master/Classes/Profile.ahk#L106
Child thread getting function object:
https://github.com/evilC/UCR/blob/master/Threads/ProfileInputThread.ahk#L14
Child thread using function object:
https://github.com/evilC/UCR/blob/master/Threads/ProfileInputThread.ahk#L195
So I've been toying around with this and the SendPipeMessage really is kinda unreliable. However from what I found out, since the ahkdll runs in the same process, you don't even need any sort of IPC for communicating, you can just directly call a C# delegate with DllCall.
Example:
void AHKCallback(string s)
{
Console.WriteLine(s);
}
[UnmanagedFunctionPointer(CallingConvention.StdCall)]
delegate void AHKDelegate([MarshalAs(UnmanagedType.LPWStr)]string s);
AHKDelegate ahkDelegate = AHKCallback;
void writeCrapInConsoleOnPressingA()
{
IntPtr ptr = Marshal.GetFunctionPointerForDelegate(ahkDelegate);
var ahk = AutoHotkeyEngine.Instance;
ahk.ExecRaw(@"a::
DllCall(" + ptr + @", ""Str"", ""whatever you wanna pass to AHKCallback"")
return");
}
I like this method a lot. Should be simple and less hassles implementing this type communication between the two worlds.
So if we switched to this method, does that mean that all the pipes code could be removed?
I need to have lots of copies of AHK running - up to a minimum of about 6, but ideally as many as the user is willing to sacrifice memory for.
Why so many? Because I need one for "Bind Mode" (Has hotkeys to all keyboard and mouse buttons declared) which is used to detect which key the end-user wishes to use, then one copy of AHK per user profile.
This way, it is easy to turn on/off sets of hotkeys - you just Suspend/Unsuspend the copies of AHK as appropriate.
In order to handle "Shifted" inputs, my system uses child profiles - so these must be available pretty much instantly, and could contain hundreds of hotkeys - so in this case they need to be loaded and suspended so they can be activated at very short notice.
I managed to alter the code to not be a singleton, and in order to do so had to comment out a bunch of stuff to do with pipes.
Am hoping maybe switching to this method will also make this possible without having to have my own custom fork.
Sometimes a runtime error is thrown ("Functions cannot contain functions")
Sometimes none of the hotkeys work
Hotkeys NEVER fire as often as they should (Spam F12 and you do not see a console log for each press / release)
If you have a release hotkey, after about the 5th press/release, all hotkeys stop firing