For the second time since I have been at Mozilla I have encountered a situation where hooks are called for notifications of a newly created window, but that window has not yet been initialized properly, causing the hooks to behave badly.
The first time was inside our window neutering code in IPC, while the second time was in our accessibility code.
Every time I have seen this, there is code that follows this pattern:
1 2 3 4 5
This seems innocuous enough, right?
The problem is that
CreateWindowEx calls hooks. If those hooks then try to do
GetProp(hwnd, "Foo"), that call is going to fail because the
“Foo” prop has not yet been set.
The key takeaway from this is that, if you are creating a new window, you must
do any follow-up initialization from within your window proc’s
handler. This will guarantee that your window’s initialization will have
completed before any hooks are called.
You might be thinking, “But I don’t set any hooks!” While this may be true, you must not forget about hooks set by third-party code.
“But those hooks won’t know anything about my program’s internals, right?”
Perhaps, perhaps not. But when those hooks fire, they give third-party software the opportunity to run. In some cases, those hooks might even cause the thread to reenter your own code. Your window had better be completely initialized when this happens!
In the case of my latest discovery of this issue in bug 1380471, I made it possible to use a C++11 lambda to simplify this pattern.
lpParam to a pointer to a
std::function<void(HWND)>, we may then
supply any callable that we wish for follow-up window initialization.
Using the previous code sample as a baseline, this allows me to revise the code to safely set a property like this:
1 2 3 4 5 6
Note that since
lpParam is always passed during
WM_CREATE, which always fires
CreateWindowEx returns, it is safe for
onCreate to live on the stack.
I liked this solution for the a11y case because it preserved the locality of
the initialization code within the function that called
window proc for this window is implemented in another source file and the
follow-up initialization depends on the context surrounding the
Speaking of window procs, here is how that window’s
WM_CREATE handler invokes
1 2 3 4 5 6 7 8 9 10 11 12 13
TL;DR: If you see a pattern where further initialization work is being done
HWND after a
CreateWindowEx call, move that initialization code to your
WM_CREATE handler instead.