Tuesday, November 29, 2011

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..

Thursday, November 24, 2011

window_class::Register

Continuing the look at window_class. here are the Register methods. Each one will initialize defaults for WNDCLASSEX then allow the window implementation to override the defaults by calling window_class_register via ADL. Then it will force the WNDPROC to WindowCallback and call Register.

template<typename WindowClassTag>
//static
ATOM window_class<WindowClassTag>::Register()
{
  WNDCLASSEX wcex = {};
  wcex.cbSize = sizeof(WNDCLASSEX);

  // defaults that can be overriden
  wcex.style = CS_HREDRAW | CS_VREDRAW;
  wcex.hInstance = GetCurrentInstance();
  wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);

  window_class_register(&wcex, tag());

  // not overridable
  wcex.lpfnWndProc = WindowCallback<WindowClassTag>;

  return RegisterClassEx(&wcex);
}

The default overload does the bare minimum

template<typename WindowClassTag>
template<typename T>
//static
ATOM window_class<WindowClassTag>::Register(T && t)
{
  WNDCLASSEX wcex = {};
  wcex.cbSize = sizeof(WNDCLASSEX);

  // defaults that can be overriden
  wcex.style = CS_HREDRAW | CS_VREDRAW;
  wcex.hInstance = GetCurrentInstance();
  wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);

  window_class_register(std::forward<T>(t), &wcex, tag());

  // not overridable
  wcex.lpfnWndProc = WindowCallback<WindowClassTag>;

  return RegisterClassEx(&wcex);
}

This overload will accept a parameter, any parameter that the implementation desires to require, it could even be a fully filled out WNDCLASSEX that is just copied over the defaults.

Tuesday, November 22, 2011

win32 window_class

I have been working on a library for building win32 windows. MFC and WTL were both great designs at the time that they were introduced, but C++ has moved on.

My goals for this library:

  • I wanted to enable the building of window controls like the common controls in windows
  • I wanted to draw a distinct line between the window implementation and window usage
  • I wanted to remove the window message map from the implementation
  • I wanted to eliminate virtual functions from the implementation
  • I wanted to enable exception based implementations without requiring them

Code Walkthrough

template<typename WindowClassTag>
struct traits {
  typedef
    decltype(window_class_traits(WindowClassTag()))
  type;
};

traits encapsulates the lookup of a type that provides traits. the lookup is done using ADL (argument dependent lookup) using the WindowClassTag to cause the compiler to find the window_class_traits function that is in the same namespace as the provided WindowClassTag. The type returned from this is arbitrary as long as it has the typedefs that window_class makes reference to.

template<typename WindowClassTag>
LRESULT CALLBACK WindowCallback(
  HWND hWnd,
  UINT message,
  WPARAM wParam,
  LPARAM lParam);

This is the WNDPROC implementation where all the magic happens.

template<typename WindowClassTag>
class window_class {
public:
  typedef
    WindowClassTag
  tag;

  typedef
    typename traits<tag>::type
  traits;

start of the declaration of window_class including some simple typedefs to shorten usage inside window_class

  static ATOM Register();

  template<typename T>
  static ATOM Register(T && t);

overloads of the static Register function. when called these will register the window class.

private:
  ~window_class();
  window_class();
  window_class(window_class&);
  window_class& operator=(window_class&);
};

the end of window_class which prevents any instances of window_class from being made, this is a static only class.

The next post will show the Register implementations..

Tuesday, November 15, 2011

walk-through of RootWindow from scratch program

Last time I posted a port of Raymond Chen's scratch program. I mentioned that I had some goals for the design of the window library. One of those goals was to follow the intended window lifetime model. Most of the controls built into windows work by registering window classes that will allocate memory to store their state on the first message (usually WM_NCCREATE) and deallocate that state on the last message (usually WM_NCDESTROY). The window handle returned from CreateWindow is then used to send messages to get or set state.

In most existing libraries there is a single class with both the window handle and the state and an instance of the class is allocated and then passed to CreateWindow. Deallocation becomes challenging. If it is deallocated on WM_NCDESTROY then there is a risk that the object will be accessed after the destructor has run. In addition there is always the risk that delete could be called while the window is still trying to send messages.

Following the pattern used by the built-in controls results in a much safer lifetime model.

The following walkthrough will explain how each line contributes to the pattern:

Add a namespace where the implementation details of the window can be placed

namespace RootWindow {

This struct tag is the glue that is used to leverage ADL to stitch all the pieces of the window together.

struct tag {};

Adding a typedef for the Context just reduces typing. Notice the tag is used to make this Context unique to RootWindow.

typedef
  l::wnd::Context<tag>
    Context;

The struct window is allocated by the library on WM_NCCREATE to provide the runtime state for the window. in addition to the state it also provides a function for each window message it wishes to receive
The child member is there to mirror Raymond's scratch program as is the empty OnCreate.
OnPaint and OnPrintClient are in the base class of Raymond's scratch and I could have placed them in a common location as well, but I have not settled on the best place for them so they are here for now.

Notice that there is no message map. The library uses templates to detect which methods exist on struct window and only handle those. The rest go to DefWindowProc.

struct window
{
  l::wr::unique_close_window child;

  LRESULT OnCreate(const Context& , LPCREATESTRUCT) {
    return 0;
  }

  LRESULT OnSize(const Context& , UINT , int cx, int cy) {
    if (child) {
      SetWindowPos(
        child.get(), NULL,
        0, 0, cx, cy,
        SWP_NOZORDER | SWP_NOACTIVATE);
    }
    return 0;
  }

  LRESULT PaintContent(PAINTSTRUCT&) {
    return 0;
  }

  LRESULT OnPaint(const Context& context) {
    PAINTSTRUCT ps = {};
    BeginPaint(context.window, &ps);
    l::wr::unique_gdi_end_paint ender(
      std::make_pair(context.window, &ps));
    return PaintContent(ps);
  }

  LRESULT OnPrintClient(const Context& context, HDC hdc, DWORD) {
    PAINTSTRUCT ps = {};
    ps.hdc = hdc;
    GetClientRect(context.window, &ps.rcPaint);
    return PaintContent(ps);
  }

  LRESULT OnNCDestroy(const Context&) {
    PostQuitMessage(0);
    return 0;
  }
};

This declares a function that has no implementation. This function is used by the library to locate the window class using the tag struct. Since it is never called and is only referenced in a decltype expression there is no need for an implementation.

l::wnd::window_class_traits_builder<window>
window_class_traits(tag &&);

This function does need an implementation. The library calls this function to allow the window registration defaults to be changed. The library uses the tag to find this function.

void window_class_register(
  PCWSTR windowClass,
  WNDCLASSEX* wcex,
  tag &&) {
  wcex->style         = 0;
  wcex->hIcon         = NULL;
  wcex->hCursor       = LoadCursor(NULL, IDC_ARROW);
  wcex->lpszMenuName  = NULL;
  wcex->lpszClassName = windowClass;
}
}

This function is found using the tag as well. The library calls this function for each message that it dispatches. This function provides user-specified error handling. If this function does not catch exceptions the library will exit the process if an exception is thrown. This allows the library to be used in code that does not support exceptions as well (by skipping the try/catches).

template<typename Function, typename MessageTag>
void window_message_error_contract(
  Function && function,
  RootWindow::Context& ,
  MessageTag && ,
  RootWindow::tag &&)
{
  try {
    std::forward<Function>(function)();
  } catch (std::bad_alloc &&) {
  } catch (unique_winerror::exception &&) {
  } catch (unique_hresult::exception &&) {
  }
}

This typedef actually stitches all the previous pieces into the final type. the window_class is given the tag after which it uses the tag to search out all the pieces and build a WndProc that will create a struct window and start calling OnCreate etc..

typedef
  l::wnd::window_class<RootWindow::tag>
    RootWindowClass;

Thursday, November 10, 2011

a scratch program

I have been working on a library for building win32 windows. MFC and WTL were both great designs at the time that they were introduced, but C++ has moved on. Then there is Raymond Chen's new(2005) C++ scratch program which eschews libraries and builds a simple base class with virtual functions

One of the things that bothers me about all the existing libraries is that they mix two distinct objects with separate lifetimes into one object with incoherent lifetime. I will go into more detail on this and other design decisions in additional posts.

So here is a port of Raymond's scratch program using my window library.

#define STRICT
#define WIN32_LEAN_AND_MEAN
#define NOMINMAX
#define UNICODE
#define _UNICODE

#include <windows.h>
#include <windowsx.h>
#include <ole2.h>
#include <commctrl.h>
#include <shlwapi.h>
#include <shlobj.h>
#include <shellapi.h>

#include <new>
#include <utility>
#include <memory>
#include <type_traits>
#include <tuple>
#include <string>
#include <vector>

#define LIBRARIES_NAMESPACE mylib
#include "..\libraries\libraries.h"
namespace l = LIBRARIES_NAMESPACE;

void unique_error_report_initiated(
  DWORD value,
  unique_winerror::tag &&)
{
  static DWORD anchor;
  anchor = value;
}

void unique_error_report_reset(
  DWORD value,
  unique_winerror::tag &&)
{
  static DWORD anchor;
  anchor = value;
}

void unique_error_report_initiated(
  HRESULT value,
  unique_hresult::tag &&)
{
  static HRESULT anchor;
  anchor = value;
}

void unique_error_report_reset(
  HRESULT value,
  unique_hresult::tag &&)
{
  static HRESULT anchor;
  anchor = value;
}

namespace RootWindow {
struct tag {};

typedef
  l::wnd::Context<tag>
    Context;

struct window
{
  l::wr::unique_close_window child;

  LRESULT OnCreate(const Context& , LPCREATESTRUCT) {
    return 0;
  }

  LRESULT OnSize(const Context& , UINT , int cx, int cy) {
    if (child) {
      SetWindowPos(
        child.get(), NULL,
        0, 0, cx, cy,
        SWP_NOZORDER | SWP_NOACTIVATE);
    }
    return 0;
  }

  LRESULT PaintContent(PAINTSTRUCT&) {
    return 0;
  }

  LRESULT OnPaint(const Context& context) {
    PAINTSTRUCT ps = {};
    BeginPaint(context.window, &ps);
    l::wr::unique_gdi_end_paint ender(
      std::make_pair(context.window, &ps));
    return PaintContent(ps);
  }

  LRESULT OnPrintClient(const Context& context, HDC hdc, DWORD) {
    PAINTSTRUCT ps = {};
    ps.hdc = hdc;
    GetClientRect(context.window, &ps.rcPaint);
    return PaintContent(ps);
  }

  LRESULT OnNCDestroy(const Context&) {
    PostQuitMessage(0);
    return 0;
  }
};

l::wnd::window_class_traits_builder<window>
window_class_traits(tag &&);

void window_class_register(
  PCWSTR windowClass,
  WNDCLASSEX* wcex,
  tag &&) {
  wcex->style         = 0;
  wcex->hIcon         = NULL;
  wcex->hCursor       = LoadCursor(NULL, IDC_ARROW);
  wcex->lpszMenuName  = NULL;
  wcex->lpszClassName = windowClass;
}
}

template<typename Function, typename MessageTag>
void window_message_error_contract(
  Function && function,
  RootWindow::Context& ,
  MessageTag && ,
  RootWindow::tag &&)
{
  try {
    std::forward<Function>(function)();
  } catch (std::bad_alloc &&) {
  } catch (unique_winerror::exception &&) {
  } catch (unique_hresult::exception &&) {
  }
}

typedef
  l::wnd::window_class<RootWindow::tag>
    RootWindowClass;

int PASCAL
wWinMain(HINSTANCE hinst, HINSTANCE, LPWSTR, int nShowCmd)
{
  unique_hresult hr;

  hr.reset(CoInitialize(NULL));
  if (!hr) {
    return FALSE;
  }
  ON_UNWIND_AUTO([&] {CoUninitialize();});

  InitCommonControls();

  RootWindowClass::Register(L"Scratch");

  unique_winerror winerror;
  l::wr::unique_close_window window;

  std::tie(winerror, window) =
    l::wr::winerror_and_close_window(
        CreateWindow(
            L"Scratch", L"Scratch",
            WS_OVERLAPPEDWINDOW,
            CW_USEDEFAULT, CW_USEDEFAULT,
            CW_USEDEFAULT, CW_USEDEFAULT,
            NULL, NULL,
            hinst,
            NULL));

  if (!winerror || !window) {
    return winerror.get();
  }

  ShowWindow(window.get(), nShowCmd);

  MSG msg = {};
  while (GetMessage(&msg, NULL, 0, 0)) {
    TranslateMessage(&msg);
    DispatchMessage(&msg);
  }
  return 0;
}

Tuesday, November 8, 2011

more on optional traits via ADL

I posted earlier about a way to provide optional functionality through traits. I used operator [] as the example. As I have been using the library I found some issues with that approach. The main issue was that operator[] is not a template method, being on a templates class is not enough. This meant that using SFINAE to remove the function entirely when the trait was not specified was not possible. Another issue was that some compilers would complain if auto was used on free-functions. These are both addressed now, here is the latest.
template<typename TypeTag, typename ResourceType>
decltype(unique_t_at(
    instance_of<ResourceType>::value,
    instance_of<size_t>::value,
    TypeTag()))
optional_unique_t_at(
  ResourceType && resource,
  size_t index,
  int)
{
  return unique_t_at(
      std::forward<ResourceType>(resource),
      index,
      TypeTag());
}

struct no_indexing_support {};

template<typename TypeTag>
no_indexing_support
optional_unique_t_at(...)
{
  return no_indexing_support();
}

template<typename TypeTag>
class unique_t {
private:
  typedef
  decltype(optional_unique_t_at<TypeTag>(
    instance_of<type>::value,
    instance_of<size_t>::value,
    0))
  optional_at_result;
public:
  ...
  optional_at_result operator[](
    size_t at) {
    return optional_unique_t_at<TypeTag>(
      t,
      at,
      0);
  }
private:
  type t;
};

Lines 1-24 take care of routing to the trait implementation, when it exists, and a default that provides a suitable error when used. The auto keyword is not used, as a consequence we cannot use the parameters and must synthesize the values used in the decltype. These free-functions are templates and are therefore able to use SFINAE. Here we can see most of the adjustments that were needed to get optional traits working.

Thursday, November 3, 2011

Synology

First a quick recap of my past data losses. I have had 8 drive failures in the last decade. After the first one that caused painful data loss (even the recovery company couldn't retrieve anything) I bgan buying HD's in pairs with RAID controllers mirroring them. This has saved me many times.

I decided some years ago to buy a NAS with some serious storage to hold my music (lossless compression). I bought a Synology CubeStation and 3 500GB seagate drives and set up in RAID 5. This worried me ever since because sometimes the failures are controller failures not drive failures.

A couple of months ago I started to hear fan noise from the Synology and knew it was time to replace it. It took a while (and some additional hardware failures) for that to reach the top of my list. But I finally ordered a new RAID device.

One of the best things about the Synology was the constant updates and improvements. They took the NVidia/Apple model and kept similar software for all their devices so my old device kept getting addition features, bug fixes and even a couple of complete UI redesigns while I had it.

I decided to try one of those just before the new device arrived. The UI warned me to upload the file to the Synology a different way, but I decided to go for it. The update indeed failed, then the UI stopped responding and then the device started flashing an orange light. It was inaccessible.

I opened a support ticket and they told me to go through the normal install process from a PC connected by ethernet. I balked at this because I did not want to lose data. However, I tried it and it failed. Then I unplugged all the drives and tried again. it failed. Then I connected all the drives and tried again (at their prompting).

Each time I ran the install it listed the steps like 'format system partition' and 'format data partition' - very scary to me. But the last time I tried it went all the way through and when it got to the 'format data partition' step it skipped it! When it all came back it had my data intact. I am quite impressed and very thankful!

Tuesday, November 1, 2011

FAIL_FAST

In Windows 7 an new weapon in the fight against bugs was released in the form of RaiseFailFastException

In a world where Windows Error Reporting (WER) collects each crash and reports it with a minidump. FAIL_FAST is essential to finding and fixing bugs quickly. Instead of laboring for a repro, just open the minidump and it points to the line of code that failed and usually has enough state to determine the cause.

FAIL_FAST should be used to report bugs in code. Transient or environmental failures like no-memory or access-denied are not suited for reporting via FAIL_FAST, rather things like 'I do not understand this enum value' or 'I did not expect this error value'

#define FAIL_FAST_FILTER() \
  __except(FailFastFilter(GetExceptionInformation())) \
  { \
  } do {} while(0,0)

inline
LONG WINAPI FailFastFilter(
  __in  struct _EXCEPTION_POINTERS* exceptionInfo)
{
  RaiseFailFastException(
    exceptionInfo - > ExceptionRecord,
    exceptionInfo - > ContextRecord,
    0);
  return EXCEPTION_CONTINUE_SEARCH;
}

template<typename Function>
auto FailFastOnThrow(
  Function && function) -> decltype(
      std::forward<Function>(function)())
{
  //
  // __ try must be isolated in its own
  // function in order for the compiler
  // to reason about C++ unwind in the
  // calling and called functions.
  //
  __try {
    return std::forward <Function >(function)();
  }
  FAIL_FAST_FILTER();
}

#define FAIL_FAST_ON_THROW(Function) \
  FailFastOnThrow((Function))

#define FAIL_FAST(Code) \
  FAIL_FAST_ON_THROW(
    [&]{ \
      RaiseException( \
        (Code), \
        EXCEPTION_NONCONTINUABLE, \
        0, \
        nullptr \
      ); \
    } \
  )