ぬの部屋(仮)
nu-no-he-ya
  •      12
    3456789
    10111213141516
    17181920212223
    242526272829 
           
      12345
    6789101112
    13141516171819
    20212223242526
    2728293031  
           
          1
    2345678
    9101112131415
    16171819202122
    23242526272829
    3031     
        123
    45678910
    11121314151617
    18192021222324
    252627282930 
           
     123456
    78910111213
    14151617181920
    21222324252627
    28293031   
           
          1
    2345678
    9101112131415
    16171819202122
    23242526272829
    30      
       1234
    567891011
    12131415161718
    19202122232425
    262728293031 
           
    1234567
    891011121314
    15161718192021
    22232425262728
    293031    
           
         12
    3456789
    10111213141516
    17181920212223
    24252627282930
           
      12345
    6789101112
    13141516171819
    20212223242526
    2728293031  
           
    1234567
    891011121314
    15161718192021
    22232425262728
    2930     
           
        123
    45678910
    11121314151617
    18192021222324
    25262728293031
           
        123
    45678910
    11121314151617
    18192021222324
    25262728   
           
     123456
    78910111213
    14151617181920
    21222324252627
    28293031   
           
         12
    3456789
    10111213141516
    17181920212223
    24252627282930
    31      
       1234
    567891011
    12131415161718
    19202122232425
    2627282930  
           
    1234567
    15161718192021
    293031    
           
         12
    3456789
    10111213141516
           
      12345
    6789101112
    13141516171819
    20212223242526
    2728293031  
           
          1
    2345678
    9101112131415
    16171819202122
    23242526272829
    3031     
        123
    45678910
    11121314151617
    18192021222324
    252627282930 
           
     123456
    78910111213
    14151617181920
    21222324252627
    28293031   
           
          1
    2345678
    9101112131415
    16171819202122
    23242526272829
    30      
       1234
    567891011
    12131415161718
    19202122232425
    262728293031 
           
    1234567
    891011121314
    15161718192021
    22232425262728
    293031    
           
        123
    45678910
    11121314151617
    18192021222324
    25262728293031
           
      12345
    6789101112
    13141516171819
    20212223242526
    27282930   
           
        123
    45678910
    11121314151617
    18192021222324
    252627282930 
           
     123456
    78910111213
    14151617181920
    21222324252627
    28293031   
           
       1234
    567891011
    12131415161718
    19202122232425
    2627282930  
           
    1234567
    891011121314
    15161718192021
    22232425262728
    293031    
           
         12
    3456789
    10111213141516
    17181920212223
    24252627282930
           
      12345
    6789101112
    13141516171819
    20212223242526
    2728293031  
           
      12345
    6789101112
    13141516171819
    20212223242526
    2728     
           
          1
    2345678
    9101112131415
    16171819202122
    23242526272829
    3031     
     123456
    78910111213
    14151617181920
    21222324252627
    282930    
           
         12
    3456789
    10111213141516
    17181920212223
    24252627282930
    31      
    1234567
    891011121314
    15161718192021
    22232425262728
    293031    
           
        123
    45678910
    11121314151617
    18192021222324
    252627282930 
           
     123456
    78910111213
    14151617181920
    21222324252627
    28293031   
           
     123456
    78910111213
    14151617181920
    21222324252627
    28293031   
           
       1234
    567891011
    12131415161718
    19202122232425
    262728293031 
           
     123456
    78910111213
    14151617181920
    21222324252627
    282930    
           
         12
    3456789
    10111213141516
    17181920212223
    24252627282930
    31      
      12345
    6789101112
    13141516171819
    20212223242526
    2728293031  
           
    1234567
    891011121314
    15161718192021
    22232425262728
    2930     
           
        123
    45678910
    11121314151617
    18192021222324
    25262728293031
           
      12345
    6789101112
    13141516171819
    20212223242526
    27282930   
           
          1
    2345678
    9101112131415
    16171819202122
    23242526272829
    3031     
          1
    2345678
    9101112131415
    16171819202122
    232425262728 
           
       1234
    567891011
    12131415161718
    19202122232425
    262728293031 
           
    1234567
    891011121314
    15161718192021
    22232425262728
    293031    
           
         12
    3456789
    10111213141516
    17181920212223
    24252627282930
           
      12345
    6789101112
    13141516171819
    20212223242526
    2728293031  
           
    1234567
    891011121314
    15161718192021
    22232425262728
    2930     
           
        123
    45678910
    11121314151617
    18192021222324
    25262728293031
           
  • Win32APIでウィンドウをカプセル化

    ウィンドウプロシージャをメンバ関数にしたい。しかしWNDCLASSEXのlpfnWndProcに指定できるのは普通の関数かstaticメンバ関数のみ。

    しかし一工夫することで、ウィンドウをカプセル化することができる。コードは以下。

    (ソースコードをまとめたファイル:CnuWindow.zip )

     

    CnuWindow.h

    #pragma once
    #include <windows.h>
    class CnuWindow
    {
       TCHAR WINDOW_CLASS_NAME[1024];
    
       static LRESULT CALLBACK StaticWndProc(
          HWND hWnd, 
          UINT msg, 
          WPARAM wp, 
          LPARAM lp);
    private:
       HWND m_hwnd;
       HINSTANCE m_hInstance;
    public:
       CnuWindow();
       virtual ~CnuWindow();
    
       //! @brief ウィンドウ定義用メンバ関数
       bool RegistWindow(
          const TCHAR* window_class_name, 
          const HINSTANCE hInst
       );
    
       //! @brief ウィンドウプロシージャ
       virtual LRESULT WndProc(
          HWND hWnd, 
          UINT msg, 
          WPARAM wp, 
          LPARAM lp
       );
    
       HWND GetHWnd()const { return m_hwnd; }
       HINSTANCE GetHInstance()const { return m_hInstance; }
    
       //! @brief ウィンドウ作成用メンバ関数
       bool nuCreateWindow(
          LPCTSTR lpWindowName,   // ウィンドウ名
          DWORD dwStyle,         // ウィンドウスタイル
          int x,               // ウィンドウの横方向の位置
          int y,               // ウィンドウの縦方向の位置
          int nWidth,            // ウィンドウの幅
          int nHeight,         // ウィンドウの高さ
          HWND hWndParent,      // 親ウィンドウまたはオーナーウィンドウのハンドル
          HMENU hMenu            // メニューハンドルまたは子ウィンドウ ID
       );
    };
    

     

    CnuWindow.cpp

    #include "CnuWindow.h"
    #include <windows.h>
    #include <tchar.h>
    CnuWindow::CnuWindow()
    {
    }
    CnuWindow::~CnuWindow()
    {
    }
    // ウィンドウを作成する
    bool CnuWindow::RegistWindow(const TCHAR* window_class_name, const HINSTANCE hInst)
    {
       _tcscpy_s(WINDOW_CLASS_NAME, window_class_name);
       m_hInstance = hInst;
       WNDCLASSEX wc;
    
       // ウィンドウクラスの情報を設定
       wc.cbSize = sizeof(wc);
       wc.style = CS_HREDRAW | CS_VREDRAW;
       wc.lpfnWndProc = CnuWindow::StaticWndProc; // ウィンドウプロシージャ
       wc.cbClsExtra = 0;
       wc.cbWndExtra = 0;
       wc.hInstance = hInst;                     // インスタンスハンドル
       wc.hIcon = (HICON)LoadIcon(NULL, IDI_APPLICATION);
       wc.hIconSm = wc.hIcon;
       wc.hCursor = (HCURSOR)LoadCursor(NULL, IDC_ARROW);
       wc.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
       wc.lpszMenuName = NULL;
       wc.lpszClassName = WINDOW_CLASS_NAME;     // ウィンドウクラス名
    
       // ウィンドウクラスを登録する
       if (RegisterClassEx(&wc) == 0) { return false; }
    
       return true;
    }
    bool CnuWindow::nuCreateWindow(
       LPCTSTR lpWindowTitle, // ウィンドウ名
       DWORD dwStyle,        // ウィンドウスタイル
       int x,                // ウィンドウの横方向の位置
       int y,                // ウィンドウの縦方向の位置
       int nWidth,           // ウィンドウの幅
       int nHeight,          // ウィンドウの高さ
       HWND hWndParent,      // 親ウィンドウまたはオーナーウィンドウのハンドル
       HMENU hMenu           // メニューハンドルまたは子ウィンドウ ID
    ) {
       m_hwnd = CreateWindow(
          WINDOW_CLASS_NAME,      // ウィンドウクラス名
          lpWindowTitle,         // タイトルバーに表示する文字列
          dwStyle,            //  ウィンドウの種類
          x,                  // ウィンドウを表示する位置(X座標)
          y,                  // ウィンドウを表示する位置(Y座標)
          nWidth,               // ウィンドウの幅
          nHeight,            // ウィンドウの高さ
          hWndParent,            // 親ウィンドウのウィンドウハンドル
          hMenu,               // メニューハンドル
          m_hInstance,         // インスタンスハンドル
          this               // その他の作成データ
       );
       if (m_hwnd == nullptr)
          return false;
    
       return true;
    }
    LRESULT  CnuWindow::WndProc(HWND hWnd, UINT msg, WPARAM wp, LPARAM lp)
    {
       switch (msg)
       {
       case WM_DESTROY:
          PostQuitMessage(0);
          return 0;
       }
       return DefWindowProc(hWnd, msg, wp, lp);
    }
    LRESULT CALLBACK CnuWindow::StaticWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
    {
    
       CnuWindow* This = (CnuWindow*)GetWindowLongPtr(hWnd, GWLP_USERDATA);
       if (!This) {//取得できなかった(ウィンドウ生成中)場合
          if (message == WM_CREATE) {
             This = (CnuWindow*)((LPCREATESTRUCT)lParam)->lpCreateParams;
             if (This) {
                SetWindowLongPtr(hWnd, GWLP_USERDATA, (LONG_PTR)This);
                return This->WndProc(hWnd, message, wParam, lParam);
             }
          }
       }
       else {//取得できた場合(ウィンドウ生成後)
          return This->WndProc(hWnd, message, wParam, lParam);
       }
       return DefWindowProc(hWnd, message, wParam, lParam);
    }
    

     

    main.cpp

    #include<windows.h>
    #include <tchar.h>
    #include "CnuWindow.h"
    int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
       PSTR lpCmdLine, int nCmdShow) {
    
       CnuWindow wino;
       wino.RegistWindow(_T("WINO"), hInstance);
       wino.nuCreateWindow(
          _T("ウィンドウO"), 
          WS_OVERLAPPEDWINDOW | WS_VISIBLE, 
          100, 100, 
          300, 300, 
          nullptr, 
          nullptr
       );
    
    
       MSG msg;
       while (GetMessage(&msg, NULL, 0, 0)) {
          TranslateMessage(&msg);
          DispatchMessage(&msg);
       }
       return (int)msg.wParam;
    }
    

     

    クラス名はCnuWindowにしているので、それを置換すれば独自のクラス名にできる。

    内容的にはCreateWindow時にlpParamに自身のthisを入れる典型的な方法なので詳細は他所へ譲るとして、多少解説をする

    解説1 SetWindowLongPtrを使う

    LRESULT CALLBACK CnuWindow::StaticWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
    {
    
       CnuWindow* This = (CnuWindow*)GetWindowLongPtr(hWnd, GWLP_USERDATA);
       if (!This) {//取得できなかった(ウィンドウ生成中)場合
          if (message == WM_CREATE) {
             This = (CnuWindow*)((LPCREATESTRUCT)lParam)->lpCreateParams;
             if (This) {
                SetWindowLongPtr(hWnd, GWLP_USERDATA, (LONG_PTR)This);
                return This->WndProc(hWnd, message, wParam, lParam);
             }
          }
       }
       else {//取得できた場合(ウィンドウ生成後)
          return This->WndProc(hWnd, message, wParam, lParam);
       }
       return DefWindowProc(hWnd, message, wParam, lParam);
    }
    

    64ビット環境ではSetWindowLongではなくSetWindowLongPtrを使わなくてはならない。それに伴い、GetWindowLongPtr,GWLP_USERDATAに変更する。

     

    解説2 (staticでない)メンバ関数のWndProcをvirtualにする

    virtual LRESULT  WndProc(HWND hWnd, UINT msg, WPARAM wp, LPARAM lp);
    

     

    これをvirtualにする理由は、main.cppでいずれ以下のように書きたいからである。

    #include<windows.h>
    #include <tchar.h>
    #include <cstdint>
    #include "CnuWindow.h"
    class CWin1 : public CnuWindow {
    
       LRESULT WndProc(HWND hWnd, UINT msg, WPARAM wp, LPARAM lp)
       {
          switch (msg)
          {
          case WM_CREATE:
             break;
          case WM_LBUTTONDOWN:
             MessageBox(hWnd, _T("CWin1"), 0, 0);
             break;
          case WM_DESTROY:
             PostQuitMessage(0);
             return 0;
          }
          return DefWindowProc(hWnd, msg, wp, lp);
       }
    
    };
    
    class CWin2 : public CnuWindow {
    
       LRESULT WndProc(HWND hWnd, UINT msg, WPARAM wp, LPARAM lp)
       {
          switch (msg)
          {
          case WM_CREATE:
             break;
          case WM_LBUTTONDOWN:
             MessageBox(hWnd, _T("CWin2"), 0, 0);
             break;
          case WM_DESTROY:
             PostQuitMessage(0);
             return 0;
          }
          return DefWindowProc(hWnd, msg, wp, lp);
       }
    
    };
    
    
    int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
       PSTR lpCmdLine, int nCmdShow) {
    
       CWin1 win1;
       win1.RegistWindow(_T("WIN1"), hInstance);
       win1.nuCreateWindow(_T("ウィンドウ1"), WS_OVERLAPPEDWINDOW | WS_VISIBLE, 100, 100, 300, 300, nullptr, nullptr);
    
       CWin2 win2;
       win2.RegistWindow(_T("WIN2"), hInstance);
       win2.nuCreateWindow(_T("ウィンドウ2"), WS_OVERLAPPEDWINDOW | WS_VISIBLE, 100, 400, 300, 300, nullptr, nullptr);
    
       CnuWindow wino;
       wino.RegistWindow(_T("WINO"), hInstance);
       wino.nuCreateWindow(_T("ウィンドウO"), WS_OVERLAPPEDWINDOW | WS_VISIBLE, 100, 800, 300, 300, nullptr, nullptr);
    
    
       MSG msg;
       while (GetMessage(&msg, NULL, 0, 0)) {
          TranslateMessage(&msg);
          DispatchMessage(&msg);
       }
       return (int)msg.wParam;
    
    }
    

     

    上記コードは、CnuWindowを継承したCWin1,CWin2クラスを作成し、それぞれのインスタンスがまったく異なるウィンドウとして動作する。

    WndProcを仮想関数にしないと、CWin1,CWin2を作成してもメッセージが全てCnuWindowの方へ行ってしまうので、変数をグローバルにしなくていい程度のメリットしかなくなってしまう。このようにクラスを作れば、同じコードで違う挙動をするウィンドウを最小限の労力で作ることができる。