WindowCallback
Time for a look at WindowCallback
which implements a WNDPROC
.
template<typename WindowClassTag>
LRESULT CALLBACK WindowCallback(
HWND hWnd,
UINT message,
WPARAM wParam,
LPARAM lParam) {
__try {
return detail::WindowCallbackSafe<WindowClassTag>(
hWnd,
message,
wParam,
lParam);
} __except (FailFastFilter(GetExceptionInformation())) {
}
return 0;
}
Not much here, just set up an SEH exception filter that will prevent any exceptions from passing across the boundary from this module to the calling module.
namespace detail {
template<typename WindowClassTag>
LRESULT CALLBACK WindowCallbackSafe(
HWND hWnd,
UINT message,
WPARAM wParam,
LPARAM lParam) {
bool handled = false;
LRESULT result = 0;
if (message == WM_NCCREATE) {
auto existingType = optional_window_class_find(
hWnd,
WindowClassTag(),
0);
if (!existingType) {
if (!optional_window_class_insert(
hWnd,
optional_window_class_construct(
hWnd,
reinterpret_cast<LPCREATESTRUCT>(lParam),
WindowClassTag(),
0),
WindowClassTag(),
0)) {
return FALSE;
}
}
}
window_class_find
, window_class_insert
, window_class_construct
, window_class_erase
and window_class_destroy
are used to manage the lifetime of the implementation. The optional_ prefix refers to functions that provide a default behavior when the implementation does not choose to provide overloads of the functions. The defaults use new
and delete
for allocation and SetWindowLongPtr
to store and retrieve the implementation. Each implementation can provide overloads of these functions to control allocation, construction and storage of that implementation.
This code will create and store the implementation in response to WM_NCCREATE
. The attempt to find is here to guard against multiple WM_NCCREATE
messages.
auto type = optional_window_class_find(hWnd, WindowClassTag(), 0);
ON_UNWIND_AUTO(
[&] {
if (type && message == WM_NCDESTROY) {
optional_window_class_erase(hWnd, type, WindowClassTag(), 0);
optional_window_class_destroy(hWnd, type, WindowClassTag(), 0);
}
}
);
Here the code attempts to retrieve the implementation and setup a lambda function to destroy the implementation when WindowCallback
exits. Using ON_UNWIND
means that even a C++ exception will run the lambda.
if (type) {
Context<WindowClassTag> context = {hWnd, message, wParam, lParam};
std::tie(handled, result) = msg::dispatch(type, context);
if (handled) {
return result;
}
}
return DefWindowProc(hWnd, message, wParam, lParam);
}
}
The rest just calls the dispatch mechanism to forward the message to the correct function on the implementation - if it exists - and calls DefWindowProc otherwise.
template<typename WindowClassTag>
decltype(
window_class_find(
cmn::instance_of<HWND>::value,
WindowClassTag()))
optional_window_class_find(HWND hwnd, WindowClassTag && , int)
{
return window_class_find(hwnd, WindowClassTag());
}
template<typename WindowClassTag>
typename window_class<WindowClassTag>::traits::type*
optional_window_class_find(HWND hwnd, WindowClassTag && , ...)
{
typedef
typename window_class<WindowClassTag>::traits::type
type;
return reinterpret_cast<type*>(
GetWindowLongPtr(hwnd, GWLP_USERDATA));
}
Using optional_window_class_find
as an example of the optional_ pattern. The two functions both take the same parameters up until the last one. The last one is used so that when both functions are included in the overload set the compiler can break the tie. The compiler will choose to send 0 literal to an int parameter rather than ... but if the overload set only contains the function with the last parameter ends in ... then the compiler will select it without an error.
In this case the first function takes precedence only if the decltype can find a windows_class_find
overload that takes these parameters. If it cannot the second function will be used to provide default functionality.
Next we will look at the magical msg dispatcher..