-
Notifications
You must be signed in to change notification settings - Fork 900
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
v2.1: Implemented callback for SendMessage #330
base: alpha
Are you sure you want to change the base?
Conversation
Putting aside whether this should be merged... If the target process is terminated before replying to the message, the callback is called with a result of 0. In any other situation, I think it would have to be assumed that the process could reply eventually, and therefore the function object must not be released.
There's no need for _alloca when you are unconditionally allocating a fixed size. It probably makes more sense to put the result parameter first, so one can write |
I had forgotten I had merged this into a local test branch, so accidentally noticed that it causes build warnings/errors on x64. |
…hanged SendMessage callback argument list from (hWnd, msg, result) to (result, hWnd, msg).
I figure that the user should account for the fact that the process may die in the meanwhile, and can use
I opted to increase the function object ref count if The other suggestions have been addressed now. |
Neither am I. Even if such situations are possible, I've concluded that there will be no way to detect it, so whatever parameter the callback is given must remain valid forever if the callback is never called. This is my answer to your comment about "possibility for a memory leak". However, a level of indirection can allow the object to be freed before a reply is received. For instance, the script could implement a timeout like this: SendMessageTimeout(timeout, ..., callback) {
SendMessage(..., (a*) => callback?.(a*))
SetTimer () => callback := unset, -timeout
} |
This pull request implements the use of a callback function with SendMessage to get the result asynchronously. This is done with the use of SendMessageCallback in PostSendMessage in addition to SendMessageTimeout.
The user defined callback function (hereby referred to as aFunc) is passed via the timeout argument. This is safe to use because SendMessageCallback doesn't use a timeout, and the previous version of SendMessage didn't allow non-integer types for the timeout argument. Thus this change should be backwards-compatible.
Because aTimeout is now of type ExprTokenType not optl, TypeErrors are not automatically thrown any more for invalid argument types. I opted to default to the 5000ms timeout in case of strings and floats, but additional checks for them could be implemented.
SendMessageCallbackProc is used as an interim callback function that is passed into SendMessageCallback, and the pointer to aFunc is passed via the dwData argument. SendMessageCallbackProc is used because I couldn't figure out how to use aFunc directly with SendMessageCallback, if it even is possible. The loss of the dwData argument isn't important for the user, because it's trivial to replace using a BoundFunc.
Alternatively I considered using CallbackCreate and returning the created pointer with the SendMessage call, but that would require the user to explicitly use CallbackFree inside the callback function. That would be less convenient than the implementation proposed in this PR.
This implementation uses
aFunc->AddRef();
and releases in SendMessageCallbackProc. This theoretically creates a possibility for a memory leak if the callback function is never called, but that risk should be negligible if the SendMessageCallback call is successful. Alternatively the obligation to keep aFunc alive could be passed to the user instead, but I couldn't figure out how to do that.Example
Receiver.ahk
Sender.ahk
I'm not quite sure how useful this addition would even be though, because SendMessageCallback can also be rather easily used via a DllCall (
DllCall("SendMessageCallback", "ptr", receiverHwnd, "uint", MsgNum, "ptr", 123, "ptr", -456, "ptr", CallbackCreate(SendMessageCallback), "ptr", 0)
), which compared to this PR is only marginally harder to use and necessitates the additional use of CallbackFree. Since I would consider using SendMessage with a callback an advanced technique and advanced users should know how to use a simple DllCall anyway, then implementing this PR may not be worth it? Although the same could be said for the regular SendMessage...