2

WINAPI用のウィンドウクリエータークラスを作成しようとしています。

Windowにコントロールを動的に追加し、それらのメッセージを登録する方法を理解するのに行き詰まっています。

#include <windows.h>
#include <iostream>
#include <vector>
#include <tuple>
#include <thread>

using namespace std;

class WinForm
{
    private:
        HWND WindowHandle = nullptr;
        std::thread Thread;
        std::vector<std::tuple<std::string, std::size_t, HWND>> ControlHandles;

    public:
        ~WinForm();
        WinForm(std::string ClassName, std::string WindowName, bool Threaded = false, int Width = CW_USEDEFAULT, int Height = CW_USEDEFAULT, WNDPROC WindowProcedure = nullptr, WNDCLASSEX WndClass = {0});
        bool AddButton(std::string ButtonName, POINT Location, int Width, int Height);
};

WinForm::~WinForm()
{
    if (Thread.joinable())
    {
        Thread.join();
    }
}

WinForm::WinForm(std::string ClassName, std::string WindowName, bool Threaded, int Width, int Height, WNDPROC WindowProcedure, WNDCLASSEX WndClass)
{
    if (WindowProcedure == nullptr)
    {
        WindowProcedure = [](HWND window, UINT msg, WPARAM wp, LPARAM lp) -> LRESULT __stdcall
        {
            switch(msg)
            {
                case WM_PAINT:
                break;

                case WM_DESTROY:
                    PostQuitMessage(0);
                    return 0;

                default:
                    return DefWindowProc(window, msg, wp, lp);
            }
            return 0;
        };
    }

    if (WndClass.cbSize == 0)
    {
        WndClass =
        {
            sizeof(WNDCLASSEX), CS_DBLCLKS, WindowProcedure,
            0, 0, GetModuleHandle(nullptr), LoadIcon(nullptr, IDI_APPLICATION),
            LoadCursor(nullptr, IDC_ARROW), HBRUSH(COLOR_WINDOW+1),
            nullptr, ClassName.c_str(), LoadIcon (nullptr, IDI_APPLICATION)
        };
    }

    if (RegisterClassEx(&WndClass))
    {
        if (Threaded)
        {
            Thread = std::thread([ClassName, WindowName, Width, Height, this]{
                WindowHandle = CreateWindowEx(0, ClassName.c_str(), WindowName.c_str(), WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, Width, Height, nullptr, nullptr, GetModuleHandle(nullptr), nullptr);
                if(WindowHandle)
                {
                    MSG msg = {nullptr};
                    ShowWindow(WindowHandle, SW_SHOWDEFAULT);
                    while(GetMessage(&msg, nullptr, 0, 0))
                    {
                        TranslateMessage(&msg);
                        DispatchMessage(&msg);
                    }
                }
            });
        }
        else
        {
            WindowHandle = CreateWindowEx(0, ClassName.c_str(), WindowName.c_str(), WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, Width, Height, nullptr, nullptr, GetModuleHandle(nullptr), nullptr);
            if(WindowHandle)
            {
                MSG msg = {nullptr};
                ShowWindow(WindowHandle, SW_SHOWDEFAULT);
                while(GetMessage(&msg, nullptr, 0, 0))
                {
                    TranslateMessage(&msg);
                    DispatchMessage(&msg);
                }
            }
        }
    }
}

bool WinForm::AddButton(std::string ButtonName, POINT Location, int Width, int Height)
{
    for (std::vector<std::tuple<std::string, std::size_t, HWND>>::iterator it = ControlHandles.begin(); it != ControlHandles.end(); ++it)
    {
        if (ButtonName == std::get<0>(*it))
        {
            return false;
        }
    }

    std::size_t ID = 1;
    for (std::vector<std::tuple<std::string, std::size_t, HWND>>::iterator it = ControlHandles.begin(); it != ControlHandles.end(); ++it, ++ID)
    {
        if (std::get<1>(*it) != ID)
        {
            break;
        }
    }

    HWND ButtonHandle = CreateWindowEx(0, "Button", ButtonName.c_str(), WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON, Location.x, Location.y, Width, Height, WindowHandle, (HMENU)ID, GetModuleHandle(nullptr), nullptr);
    ControlHandles.push_back(std::make_tuple(ButtonName, ID, ButtonHandle));
    SendMessage(WindowHandle, WM_CREATE, 0, 0);
    return true;
}

int main()
{
    WinForm Form("Class", "Title", true);
    Form.AddButton("NewButton", {50, 50}, 25, 25);
}

上記では、正常にコンパイルされ、ウィンドウが正常に表示されます。ウィンドウに動的に追加しようとしたボタンが表示されません。誰かが私がウィンドウにボタンを動的に追加し、ボタンがメッセージを登録できるようにする方法を知っていますか?

4

1 に答える 1

3

多くの問題がありますが、主な問題は、使用しているWM_PAINTハンドラーです。これにより、メインウィンドウがこの子ウィンドウをペイントできなくなります。コメントアウトして(そして他の問題を解決して)、大丈夫です。

  1. スレッド化-これが問題かどうかわからない。これは完全に標準ではありません。メインスレッドでメインウィンドウを作成します(編集:スレッドの問題は、メインウィンドウを作成することです。コントロールウィンドウが別のスレッドにあるときに、1つのスレッドでメッセージループが発生します。これは許可されていません)。
  2. falseの場合のロジックThreadedは間違っています。そこにメッセージループを設定することはできません。WinFormのコンストラクターが戻る必要があります。そうしないと、Form.AddButtonにアクセスできなくなります。
  3. メイン内の最後のこととしてメッセージループを実行します
  4. VisualStudioを使用していないと思います。多くの構文上の問題があり(Microsoftがまだ実装していないc ++ 11のもの)、Windowsアプリケーションの主な機能はWinMainという名前です。それは問題ありませんが、お勧めしません。Microsoftには優れた無料のコンパイラがあり、有料のコンパイラを使用した場合は、すばらしいATLを使用できます。
  5. WM_PAINTをコメントアウトしないと、ボタンは作成されますが、表示されません。あなたはSpy++でそれを見つけることができます
  6. を削除しcase WM_PAIN:ます。サブウィンドウを呼び出して自分自身を指すDefWindowProcをブロックしています。
  7. WidnowProcのラムダ表記を初めて見ました。これはいいトリックですが、理由はわかりません。

以下のコードは機能しており、Visual Studio 2012の修正が含まれています。Microsoftにはまだ初期化リストがないことに注意してください(これは残念です)。どういたしまして。

#include "stdafx.h"

using namespace std;

WNDCLASSEX defWndClass = { 0 };

class WinForm
{
    private:
        HWND WindowHandle;
        std::thread Thread;
        std::vector<std::tuple<std::string, std::size_t, HWND>> ControlHandles;

    public:
        ~WinForm();
        WinForm(std::string ClassName, std::string WindowName, bool Threaded = false, int Width = CW_USEDEFAULT,
            int Height = CW_USEDEFAULT, WNDPROC WindowProcedure = nullptr, WNDCLASSEX WndClass = defWndClass);
        bool AddButton(std::string ButtonName, POINT Location, int Width, int Height);
};

WinForm::~WinForm()
{
    if (Thread.joinable())
    {
        Thread.join();
    }
}

WinForm::WinForm(std::string ClassName, std::string WindowName, bool Threaded, int Width, int Height, WNDPROC WindowProcedure, WNDCLASSEX WndClass)
    :WindowHandle(nullptr)
{
    if (WindowProcedure == nullptr)
    {
        WindowProcedure = [](HWND window, UINT msg, WPARAM wp, LPARAM lp) -> LRESULT __stdcall
        {
            switch(msg)
            {
                /*
                case WM_PAINT:
                    break;
                    */

                case WM_DESTROY:
                    PostQuitMessage(0);
                    return 0;

                case WM_CREATE:
                    break;

                default:
                    return DefWindowProc(window, msg, wp, lp);
            }
            return 0;
        };
    }

    if (WndClass.cbSize == 0)
    {
        WndClass.cbSize = sizeof(WNDCLASSEX);
        WndClass.style = CS_DBLCLKS;
        WndClass.lpfnWndProc = WindowProcedure;
        WndClass.cbClsExtra = 0;
        WndClass.cbWndExtra = 0;
        WndClass.hInstance = GetModuleHandle(nullptr);
        WndClass.hIcon = LoadIcon(nullptr, IDI_APPLICATION);
        WndClass.hCursor = LoadCursor(nullptr, IDC_ARROW);
        WndClass.hbrBackground = HBRUSH(COLOR_WINDOW+1);
        WndClass.lpszMenuName = nullptr;
        WndClass.lpszClassName = ClassName.c_str();
        WndClass.hIconSm = LoadIcon( nullptr, IDI_APPLICATION);
    }

    if (RegisterClassEx(&WndClass))
    {
        if (Threaded)
        {
            // can't do that!
        }
        else
        {
            WindowHandle = CreateWindowEx(0, ClassName.c_str(), WindowName.c_str(), WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, Width, Height, nullptr, nullptr, GetModuleHandle(nullptr), nullptr);
            if(WindowHandle)
            {
                ShowWindow(WindowHandle, SW_SHOWDEFAULT);

                // don't put message loop here!
            }
        }
    }
}

bool WinForm::AddButton(std::string ButtonName, POINT Location, int Width, int Height)
{
    for (std::vector<std::tuple<std::string, std::size_t, HWND>>::iterator it = ControlHandles.begin(); it != ControlHandles.end(); ++it)
    {
        auto& tu = *it;
        auto& str = std::get<0>(tu);
        if( ButtonName.compare( str ) == 0 ) {
            return false;
        }
    }

    std::size_t ID = 1;
    for (std::vector<std::tuple<std::string, std::size_t, HWND>>::iterator it = ControlHandles.begin(); it != ControlHandles.end(); ++it, ++ID)
    {
        if (std::get<1>(*it) != ID)
        {
            break;
        }
    }

    HWND ButtonHandle = CreateWindowEx(
        0, "button", ButtonName.c_str(), WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON, Location.x, Location.y, Width, Height, 
        WindowHandle, (HMENU)ID, (HINSTANCE)GetWindowLong(WindowHandle, GWL_HINSTANCE), nullptr);
    ShowWindow( ButtonHandle, SW_SHOW );
    ControlHandles.push_back(std::make_tuple(ButtonName, ID, ButtonHandle));

    //SendMessage(WindowHandle, WM_CREATE, 0, 0);
    return true;
}

int WINAPI WinMain( HINSTANCE, HINSTANCE, LPSTR, int )
{
    WinForm Form("Class", "Title", false);
    POINT pt = { 50, 50 };
    Form.AddButton("NewButton", pt, 80, 50);

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

}
于 2013-03-06T15:57:10.660 に答える