2012年3月27日火曜日

CreateDialog + サブクラス化 + RegisterClassEx

ウィンドウプロシージャでthisポを使いたいってんでサブクラス化するんだけど、CreateDialogでウィンドウを作成する場合はどうすりゃいいんじゃろ? と試行錯誤してた。

 結局下のようになった。CreateWindow()みたいにlparamにデータを渡せればスマートなんだけど仕方なし。あとCreateDialog()なのにウィンドウプロシージャを使用するあたりやや気持ち悪いが。

[resource.h]

#pragma once
 
#define APP_CLASS_NAME  TEXT("test")
#define APP_TITLE    TEXT("test")
#define IDD_MAIN    101

[resource.rc]

#include "resource.h"
#include <windows.h>
 
IDD_MAIN DIALOG 0, 0, 310, 210
FONT 9, "MS Pゴシック"
STYLE WS_VISIBLE | WS_POPUPWINDOW
CAPTION APP_TITLE
{}

[main.cpp]

#include <windows.h>
#include "resource.h"
 
 
 
class WindowBase
{
protected:
  /** constructor */
  WindowBase() {}
  /** destructor */
  virtual ~WindowBase() {}
 
  static void attachWnd( HWND hWnd, LONG_PTR myWndData )
  {
    SetWindowLongPtr( hWnd, GWL_USERDATA, myWndData );
    SetWindowLongPtr( hWnd, GWL_WNDPROC, reinterpret_cast<LONG_PTR>(staticWndProc) );
  }
 
public:
  /** 初期化プロシージャ */
  static LRESULT CALLBACK initWndProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam )
  {
    if ( uMsg == WM_CREATE )
    {
      LPCREATESTRUCT cs = reinterpret_cast<LPCREATESTRUCT>( lParam );
      WindowBase* wb = static_cast<WindowBase*>( cs->lpCreateParams );
 
      attachWnd( hWnd, reinterpret_cast<LONG_PTR>(wb) );
      
      return wb->wndProc( hWnd, uMsg, wParam, lParam );
    }
    return DefWindowProc( hWnd, uMsg, wParam, lParam );
  }
 
  /** 間接参照プロシージャ */
  static LRESULT CALLBACK staticWndProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam )
  {
    return reinterpret_cast<WindowBase*>( GetWindowLongPtr(hWnd, GWL_USERDATA) )->wndProc( hWnd, uMsg, wParam, lParam );
  }
 
  /** オーバーライドして使用するプロシージャ */
  virtual LRESULT wndProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam )
  {
    // サブクラス化でない場合は、デフォルトウィンドウプロシージャを呼び出す
    return DefWindowProc( hWnd, uMsg, wParam, lParam );
  }
};
 
 
 
/**
* Main
*/
class Main : public WindowBase
{
public:
  /**
   * constructor
   */
  Main()
    : m_hWnd( NULL )
  {}
 
  /**
   * destructor
   */
  ~Main() {}
 
 
 
  /**
   * override wndProc
   */
  LRESULT wndProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam )
  {
    switch ( uMsg )
    {
    case WM_CREATE:
      break;
 
    case WM_CLOSE:
      DestroyWindow( hWnd );
      break;
 
    case WM_DESTROY:
      ShowWindow( hWnd, SW_HIDE );
      PostQuitMessage(0);
      break;
    }
 
    return DefWindowProc( hWnd, uMsg, wParam, lParam );
  }
 
 
  static LRESULT CALLBACK dummyProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam )
  {
    return DefWindowProc( hWnd, uMsg, wParam, lParam );
  }
 
  /**
   * WinMain
   */
  int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd )
  {
    // register
    {
      WNDCLASSEX wc = {0};
      wc.style    = CS_HREDRAW | CS_VREDRAW;
      wc.lpfnWndProc  = dummyProc;
      wc.cbClsExtra  = 0;
      wc.cbWndExtra  = DLGWINDOWEXTRA;
      wc.cbSize    = sizeof( WNDCLASSEX );
      wc.hInstance  = hInstance;
      wc.hIcon    = NULL;
      wc.hIconSm    = NULL;
      wc.hCursor    = LoadCursor( NULL, IDC_ARROW );
      wc.hbrBackground= reinterpret_cast<HBRUSH>( GetStockObject( WHITE_BRUSH ) );
      wc.lpszMenuName  = NULL;
      wc.lpszClassName= APP_CLASS_NAME;
 
      if ( !RegisterClassEx( &wc ) ) return 0;
    }
 
 
    m_hWnd = CreateDialog( hInstance, reinterpret_cast<LPCTSTR>(IDD_MAIN), NULL, NULL );
    if ( !m_hWnd ) return -1;
 
    attachWnd( m_hWnd, reinterpret_cast<LONG_PTR>(this) );
 
    ShowWindow( m_hWnd, SW_SHOWNORMAL );
    SendMessage( m_hWnd, WM_CREATE, 0, 0 );
 
    // message loop
    {
      MSG msg;
      while ( GetMessage(&msg , NULL , 0 , 0) )
      {
        if ( !IsDialogMessage(m_hWnd, &msg) )
        {
          TranslateMessage( &msg );
          DispatchMessage( &msg );
        }
      }
    }
 
 
    return 0;
  }
 
 
private:
  HWND m_hWnd;
};
 
 
 
int WINAPI WinMain(HINSTANCE hInst, HINSTANCE hPrevInst, LPSTR lpsCmdLine, int nCmdShow)
{
  Main mainWindow;
  return mainWindow.WinMain( hInst, hPrevInst, lpsCmdLine, nCmdShow );
}


 迷う点は二つで、WNDCLASSEX のlpfnWndProcと、CreateDialog()の第4引数 lpDialogFunc。
CreateDialogParam()を使用すればLPARAMに好きなデータを渡せるから、INIT_DIALOGでGWL_USERDATAにthisポを保存する計画だった。
でもWNDCLASSEXに指定するプロシージャはLRESULT CALLBACK wndProc()、いわゆるウィンドウプロシージャで、CreateDialogに指定するのはBOOL CALLBACK wndProc()ってダイアログプロシージャと型が違うのね。
両方設定すべく、reinterpret_castで無理やり型変更して指定するもウィンドウが生成されない問題が出た。

 どちらかのプロシージャ設定でNULLを指定しなければならないようだけど、いろいろ試したが、WNDCLASSEXのプロシージャ設定をNULLにしちゃうとうまく処理できないようだった。というのもダイアログプロシージャのほうはDefWindowProc()とかで汎用処理を行わないから全うに動くはずもない。

0 件のコメント: