InputHook: Clearing input buffer? Topic is solved

Get help with using AutoHotkey (v2 or newer) and its commands and hotkeys
ThePeter
Posts: 49
Joined: 25 Oct 2022, 05:57

InputHook: Clearing input buffer?

Post by ThePeter » 09 Dec 2022, 05:20

Hi,

Is there any way to empty the input buffer held in InputHook.Input, so that the script could easily discard any non-pertinent input?

EDIT: Only way I have figured out so far would be

Code: Select all

Hook.Stop
Hook.Start
but that seems to be a costly operation and may have all kinds of side effects.

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

Re: InputHook: Clearing input buffer?

Post by swagfag » 09 Dec 2022, 08:36

no guarantees how long this will work for yeah, best dont use this at all

Code: Select all

InputHook.Prototype.DefineProp('Clear', {Call: InputHook_Clear})
InputHook_Clear(this) {
	#Requires AutoHotkey v2.0-rc.2

	pInputHook := ObjPtr(this)

	static is64bit := (A_PtrSize = 8)
	static offsetof_Buffer := is64bit ? 56 : 32
	pBuffer := NumGet(pInputHook, offsetof_Buffer, 'Ptr')

	#DllLoad 'ucrtbase.dll'
	DllCall('ucrtbase\free', 'Ptr', pBuffer)

	static offsetof_BufferLengthMax := is64bit ? 68 : 40
	BufferLengthMax := NumGet(pInputHook, offsetof_BufferLengthMax, 'Int')

	if !pBuffer := DllCall('ucrtbase\malloc', 'Ptr', (BufferLengthMax + 1) * 2)
		throw MemoryError('Out of memory.')

	NumPut('UChar', 0, pBuffer)

	NumPut('Ptr', pBuffer, pInputHook, offsetof_Buffer)
}

ih := InputHook("L2")
ih.Start()
ToolTip 'type something'
ih.Wait()
MsgBox 'u typed: ' ih.Input
ih.Clear() 
MsgBox 'and now its gone: ' ih.Input
Last edited by swagfag on 10 Dec 2022, 15:52, edited 1 time in total.

lexikos
Posts: 9583
Joined: 30 Sep 2013, 04:07
Contact:

Re: InputHook: Clearing input buffer?  Topic is solved

Post by lexikos » 09 Dec 2022, 19:27

Related:
Can the input collection of InputHook be cleared?
How to modify inputhook's property:inputhook.input

@swagfag
I had not implemented a method to do this because concerns about thread safety and race conditions made it not worth the effort at the time. It is certainly not safe to free and reallocate the buffer from the script while the InputHook is active, as the hook could write into the buffer at any moment. Even reordering the allocation and swapping of the pointer may not be sufficient, unless you do something to synchronize with the hook thread.

Even putting that aside, how did you conclude that ucrtbase operates on the same memory heap as AutoHotkey's statically-linked copy of the C runtime?

What is even the purpose of freeing the buffer and reallocating one the same size?

You missed setting BufferLength, which causes two problems:
  • If the hook is still active, it will write to the wrong position next time you type a character.
  • The value returned by Input will have the wrong length, and will include garbage that may or may not be visible in MsgBox.
As far as I can tell, there isn't even a need to terminate the buffer. If you just set the length to 0 and do nothing else, there is probably less risk of a race condition with the hook thread.

In your example, you wait for input collection to stop before clearing the buffer. What's the purpose? If you're not going to collect more input, there seems to be no need to clear the buffer. If you are going to collect more input, the buffer will be cleared automatically when you call Start.

Code: Select all

#Requires AutoHotkey =v2.0-rc.2
InputHook.Prototype.DefineProp 'Clear', {Call: this => (
    NumPut('int', 0, ObjPtr(this), A_PtrSize = 8 ? 64 : 36), ""
)}

; Esc to end.  Enter to shift Input to Entry.
ih := InputHook('', '{Esc}')
ih.Entry := '' ; Custom property
ih.KeyOpt('{Enter}', 'N')
ih.OnKeyDown := (ih, vk, sc) => (
    ih.Entry := ih.Input,
    ih.Clear()
)
ih.Start()
while ih.InProgress {
    Sleep 100
    ToolTip 'Input: ' ih.Input '`nEntry: ' ih.Entry
}
Note that even if this is thread-safe in a sense, it is prone to race conditions. For instance, suppose you want to detect a pattern in ih.Input and then reset the buffer to detect the next pattern. Once the pattern is typed, there is a delay before the script detects the pattern and resets the buffer. During that time, more input may arrive and be added to the buffer, to then be discarded. It seems to end up being not much better than Stop/Start.

Assigning to Input would carry an even higher risk of race conditions. Real usage of InputHook needs more complicated solutions. For instance:
  • A method that instructs the hook thread to discard a certain number of characters from the buffer could be used to discard only the input that the script has already retrieved and processed.
  • A method that retrieves the input and resets the buffer as one "atomic" operation would be less prone to race conditions.
  • A method that instructs the hook thread to replace a range of characters with a new string could be used to delete text from the end or middle of the buffer, or make some other edit. The range specified by the script would exclude any characters that it doesn't know the hook has been appending.
  • Automatically resetting when an end condition is met, but keeping a copy of the prior input or passing it to OnEnd.
    Related: Automatically refresh Inputhook's input collection according to the Length limit.

ThePeter wrote:but that seems to be a costly operation and may have all kinds of side effects.
I think that a vague notion of costs and side-effects (that you probably can't even begin to define) is not a good reason to avoid using the only apparent solution to a problem. Why not just do it, and observe for any problems? It is not as "costly" as using unsupported, version-specific code, even if you understand the code.

If this InputHook is the only reason that the keyboard hook is installed, it will be unregistered when the InputHook stops and registered again when the InputHook starts. You can avoid this by calling InstallKeybdHook.

Stopping the InputHook and starting it again means:
  • There is a very short time during which it won't catch input.
  • If there are multiple InputHooks, it is pushed to the top of the stack (it gets priority).
  • If it has a timeout, the timer is reset.

ThePeter
Posts: 49
Joined: 25 Oct 2022, 05:57

Re: InputHook: Clearing input buffer?

Post by ThePeter » 10 Dec 2022, 03:02

First of all, thank you both for taking the time and effort to dig into this. I find this immensely instructive.
lexikos wrote:
09 Dec 2022, 19:27
I think that a vague notion of costs and side-effects (that you probably can't even begin to define) is not a good reason to avoid using the only apparent solution to a problem. Why not just do it, and observe for any problems? It is not as "costly" as using unsupported, version-specific code, even if you understand the code.
After a bit of testing, the cost in terms of processing time indeed does not seem to be a problem. The side effects are real and I am very able to define them. Each time I call Stop, the actions defined in the OnEnd handler will be executed, which will not necessarily be what I want if the only thing I want to do is wiping the buffer. Resetting of the timer is another, but that's not relevant for my use case. Anyway, I can certainly work around that.

lexikos
Posts: 9583
Joined: 30 Sep 2013, 04:07
Contact:

Re: InputHook: Clearing input buffer?

Post by lexikos » 10 Dec 2022, 07:08

The side effects are real and I am very able to define them.
"may have all kinds of side effects" gives off a very different vibe, and your reply doesn't do much to dispel this.
Each time I call Stop, the actions defined in the OnEnd handler will be executed, which will not necessarily be what I want if the only thing I want to do is wiping the buffer.
If you want OnEnd to take some action only if input ended for some reason other than Stop being called, all you need to do is check ih.EndReason. If there's some part of the ending code that needs to run even when you end it by calling Stop, you can just put it in a function and call it both from OnEnd and after calling Stop.

I wouldn't describe a direct action that you are taking in response to your own prior action as a side-effect. You have complete control over this part of what happens. Well, you only said that you are able to define the side-effects, not that what you've written about are the side-effects...

ThePeter
Posts: 49
Joined: 25 Oct 2022, 05:57

Re: InputHook: Clearing input buffer?

Post by ThePeter » 10 Dec 2022, 09:27

lexikos wrote:
10 Dec 2022, 07:08
"may have all kinds of side effects" gives off a very different vibe, and your reply doesn't do much to dispel this.
I only mentioned the side effects that are clear to me and that I have control over. Otherwise, indeed, I have no knowledge of whether side effects are generated. Hence my words "may have". Generally, it is not good programming practice to apply a functionality that you do not want in order to achieve a desired side effect of such functionality.

In the case at hand, I have no interest in stopping the input hook. I would be doing it because it happens to have the desired side effect of clearing the buffer. I do not know what things (if any) happen behind the AHK scenes when an input hook is stopped (and started). Therefore, I was looking for a more specific way to clear the buffer, being unaware at the time whether such way exists. From your answer, I have learned that there is not. That is fine.

This is the "Ask for help" forum. I am not reporting a bug or even presenting something in the Wish List. You are giving me an astonishingly hard time for even asking.

lexikos
Posts: 9583
Joined: 30 Sep 2013, 04:07
Contact:

Re: InputHook: Clearing input buffer?

Post by lexikos » 10 Dec 2022, 18:56

You are giving me an astonishingly hard time for even asking.
:eh:

Do you expect me, or anyone, to refrain from giving unwanted opinions or arguing with you because you are asking for help?

ThePeter
Posts: 49
Joined: 25 Oct 2022, 05:57

Re: InputHook: Clearing input buffer?

Post by ThePeter » 12 Dec 2022, 02:01

I may have been oversensitive. Your responses are great and much appreciated!

Post Reply

Return to “Ask for Help (v2)”