WTL、とくにメッセージクラッカー<atlcrack.h>はATLなしの単独でも使えないだろうかと思い、試してみました。結論から言うと可能でした。ただし、2つ理由で少し残念な感じでした。

  • 先頭に#define __ATLAPP_H__というおまじないを必要とする。
  • END_MSG_MAPを自分で定義する必要がある。

コードはこんな感じです。

#define _WIN32_WINNT 0x0501
 
#define OEMRESOURCE
 
#define _WTL_NO_AUTOMATIC_NAMESPACE
 
#include <system_error>
 
#include <SDKDDKVer.h>
#include <windows.h>
#include <windowsx.h>
 
#define __ATLAPP_H__
#include <atlcrack.h>
 
#define END_MSG_MAP() } return FALSE; }
 
#include <tchar.h>
 
class WindowBase
{
public:
  static void InitApplication(HINSTANCE hinst)
  {
    WNDCLASSEX wcex = {
      sizeof wcex,
      CS_HREDRAW | CS_VREDRAW,
      WndProcEntry,
      0, 0,
      hinst,
      static_cast<HICON>(LoadImage(
        nullptr, MAKEINTRESOURCE(OIC_SAMPLE), IMAGE_ICON,
        0, 0, LR_DEFAULTSIZE | LR_SHARED)),
      static_cast<HCURSOR>(
        LoadImage(nullptr, MAKEINTRESOURCE(OCR_NORMAL), IMAGE_CURSOR,
        0, 0, LR_DEFAULTSIZE | LR_SHARED)),
      reinterpret_cast<HBRUSH>(COLOR_WINDOW + 1),
      nullptr,
      TEXT("WindowBase Class"),
      nullptr,
    };
    windowClass = RegisterClassEx(&wcex);
    if (!windowClass)
    {
      throw std::system_error(
        static_cast<int>(GetLastError()),
        std::system_category());
    }
  }
 
  WindowBase() : hwnd() {}
 
  void InitInstance()
  {
    hwnd = CreateWindowEx(
    WS_EX_APPWINDOW | WS_EX_OVERLAPPEDWINDOW,
    MAKEINTATOM(windowClass),
    TEXT(""),
    WS_OVERLAPPEDWINDOW,
    CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
    nullptr,
    nullptr,
    nullptr,
    this);
    if (!hwnd)
    {
      throw std::system_error(
        static_cast<int>(GetLastError()),
        std::system_category());
    }
  }
 
  HWND GetWindow() { return hwnd; }
 
protected:
  virtual BOOL ProcessWindowMessage(
    HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam,
    LRESULT& lResult, DWORD dwMsgMapID = 0) = 0;
 
private:
  static BOOL OnNCCreate(HWND hwnd, CREATESTRUCT const* pcs)
  {
    SetWindowLongPtr(
      hwnd, GWLP_USERDATA,
      reinterpret_cast<LONG_PTR>(pcs->lpCreateParams));
    return TRUE;
  }
 
  static LRESULT CALLBACK WndProcEntry(
    HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
  {
    switch (msg)
    {
      HANDLE_MSG(hwnd, WM_NCCREATE, OnNCCreate);
    }
    if (auto pwnd =
      reinterpret_cast<WindowBase*>(GetWindowLongPtr(hwnd, GWLP_USERDATA)))
    {
      LRESULT lr = 0;
      if (pwnd->ProcessWindowMessage(hwnd, msg, wParam, lParam, lr))
      {
        return lr;
      }
    }
    return DefWindowProc(hwnd, msg, wParam, lParam);
  }
 
  static ATOM windowClass;
 
  HWND hwnd;
 
  //WindowBase(WindowBase const&) = delete;
  //WindowBase& operator=(WindowBase const&) = delete;
  WindowBase(WindowBase const&);
  WindowBase& operator=(WindowBase const&);
};
 
class TestWindow : public WindowBase
{
public:
  TestWindow()
  {
  }
 
private:
  void OnDestroy()
  {
    PostQuitMessage(0);
  }
 
  void OnPaint(HDC)
  {
    ValidateRect(GetWindow(), nullptr);
  }
 
  BEGIN_MSG_MAP_EX(TestWindow)
    MSG_WM_PAINT(OnPaint)
    MSG_WM_DESTROY(OnDestroy)
  END_MSG_MAP()
 
private:
  TestWindow(TestWindow const&);
  TestWindow& operator=(TestWindow const&);
};
 
ATOM WindowBase::windowClass;
 
int WINAPI _tWinMain(HINSTANCE hinst, HINSTANCE, LPTSTR, int cmdShow)
{
  try
  {
    WindowBase::InitApplication(hinst);
 
    TestWindow wnd;
    wnd.InitInstance();
    ShowWindow(wnd.GetWindow(), cmdShow);
    UpdateWindow(wnd.GetWindow());
 
    for (;;)
    {
      MSG msg = {};
      auto ret = GetMessage(&msg, nullptr, 0, 0);
      if (ret == 0 || ret == -1)
      {
        return ret;
      }
      TranslateMessage(&msg);
      DispatchMessage(&msg);
    }
  }
  catch (std::exception const& e)
  {
    OutputDebugStringA(e.what());
    return 1;
  }
}

ウィンドウプロシージャで非静的メンバ関数を呼び出す処理は自前で作っています。staticメンバ関数のWndProcEntryからBEGIN_MSG_MAP_EX内部で定義されるProcessWindowMessageを呼び出します。WM_NCCREATEでC++オブジェクト (this)とウィンドウを結びつけているので、ATLほど完璧ではありません。

なお、メッセージループもWTL::CMessageLoopを使用できないかと試してみましたが、ATLASSERTなど様々なATLのマクロに依存しているので諦めました。やればできるかもしれませんが無用と判断しました。

これなら、<windowsx.h>のメッセージクラッカーをC++クラスに組み合わせるのとほとんど変わらない(参照:メッセージクラッカー with C++))というのが試してみた感想です。

スポンサード リンク

この記事のカテゴリ