C++ WINAPI Dynamic Wrapper Class












0















First off, I want to say, for anyone who is going to say "why am I reinventing the wheel", I am doing this for fun, and for a project of mine I'm currently working on.



As you can see from the code below, I am trying to dynamically create a window and a button, but something I'm having trouble with is adding a function to that button when it's clicked.



I know it would be really simple to go into the window procedure in WM_COMMAND and do so there, but that's missing the whole point of what I'm trying to accomplish here, have it so I can simple call btn.add(params) to a certain window, and add a certain function to that button by calling say btn.click(function); without having to go into the window procedure itself when adding the controls.



How would I accomplish this?



#include <Windows.h>
#include <vector>
#include <thread>

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);
}

}









share|improve this question




















  • 2





    One way to do this is to have the window procedure consult a map from buttons to functions. When you add a button, add an entry to the map.

    – Raymond Chen
    Nov 17 '18 at 2:45






  • 1





    No matter what you do, the parent window of the button is going to receive a BN_CLICKED notification via WM_COMMAND when the button is clicked. If the parent window doesn't handle the message, it ends up going to DefWindowProc() and gets ignored. So, the parent window needs to handle WM_COMMAND. When the notification code is BN_CLICKED, the parent can look up the provided HWND in its list of buttons, and if found then call the corresponding function if one has been assigned.

    – Remy Lebeau
    Nov 17 '18 at 2:51













  • Could you possibly give an example of what you're talking about?

    – Joshua
    Nov 17 '18 at 3:12






  • 1





    Hate to be the one, but I'll be the one to throw cold water on this project. To create a C++ wrapper for the Windows API, you better know the Windows API itself from its C-based roots. That means having concrete C-based examples that work, plus having books is the way to learn the API. Otherwise you totally miss the things mentioned by @RemyLebeau.

    – PaulMcKenzie
    Nov 17 '18 at 3:22













  • referencesource.microsoft.com/#System.Windows.Forms/winforms/…

    – Hans Passant
    Nov 17 '18 at 7:43
















0















First off, I want to say, for anyone who is going to say "why am I reinventing the wheel", I am doing this for fun, and for a project of mine I'm currently working on.



As you can see from the code below, I am trying to dynamically create a window and a button, but something I'm having trouble with is adding a function to that button when it's clicked.



I know it would be really simple to go into the window procedure in WM_COMMAND and do so there, but that's missing the whole point of what I'm trying to accomplish here, have it so I can simple call btn.add(params) to a certain window, and add a certain function to that button by calling say btn.click(function); without having to go into the window procedure itself when adding the controls.



How would I accomplish this?



#include <Windows.h>
#include <vector>
#include <thread>

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);
}

}









share|improve this question




















  • 2





    One way to do this is to have the window procedure consult a map from buttons to functions. When you add a button, add an entry to the map.

    – Raymond Chen
    Nov 17 '18 at 2:45






  • 1





    No matter what you do, the parent window of the button is going to receive a BN_CLICKED notification via WM_COMMAND when the button is clicked. If the parent window doesn't handle the message, it ends up going to DefWindowProc() and gets ignored. So, the parent window needs to handle WM_COMMAND. When the notification code is BN_CLICKED, the parent can look up the provided HWND in its list of buttons, and if found then call the corresponding function if one has been assigned.

    – Remy Lebeau
    Nov 17 '18 at 2:51













  • Could you possibly give an example of what you're talking about?

    – Joshua
    Nov 17 '18 at 3:12






  • 1





    Hate to be the one, but I'll be the one to throw cold water on this project. To create a C++ wrapper for the Windows API, you better know the Windows API itself from its C-based roots. That means having concrete C-based examples that work, plus having books is the way to learn the API. Otherwise you totally miss the things mentioned by @RemyLebeau.

    – PaulMcKenzie
    Nov 17 '18 at 3:22













  • referencesource.microsoft.com/#System.Windows.Forms/winforms/…

    – Hans Passant
    Nov 17 '18 at 7:43














0












0








0


1






First off, I want to say, for anyone who is going to say "why am I reinventing the wheel", I am doing this for fun, and for a project of mine I'm currently working on.



As you can see from the code below, I am trying to dynamically create a window and a button, but something I'm having trouble with is adding a function to that button when it's clicked.



I know it would be really simple to go into the window procedure in WM_COMMAND and do so there, but that's missing the whole point of what I'm trying to accomplish here, have it so I can simple call btn.add(params) to a certain window, and add a certain function to that button by calling say btn.click(function); without having to go into the window procedure itself when adding the controls.



How would I accomplish this?



#include <Windows.h>
#include <vector>
#include <thread>

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);
}

}









share|improve this question
















First off, I want to say, for anyone who is going to say "why am I reinventing the wheel", I am doing this for fun, and for a project of mine I'm currently working on.



As you can see from the code below, I am trying to dynamically create a window and a button, but something I'm having trouble with is adding a function to that button when it's clicked.



I know it would be really simple to go into the window procedure in WM_COMMAND and do so there, but that's missing the whole point of what I'm trying to accomplish here, have it so I can simple call btn.add(params) to a certain window, and add a certain function to that button by calling say btn.click(function); without having to go into the window procedure itself when adding the controls.



How would I accomplish this?



#include <Windows.h>
#include <vector>
#include <thread>

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);
}

}






c++ class winapi






share|improve this question















share|improve this question













share|improve this question




share|improve this question








edited Nov 17 '18 at 2:46









Remy Lebeau

339k19262456




339k19262456










asked Nov 17 '18 at 2:25









JoshuaJoshua

11




11








  • 2





    One way to do this is to have the window procedure consult a map from buttons to functions. When you add a button, add an entry to the map.

    – Raymond Chen
    Nov 17 '18 at 2:45






  • 1





    No matter what you do, the parent window of the button is going to receive a BN_CLICKED notification via WM_COMMAND when the button is clicked. If the parent window doesn't handle the message, it ends up going to DefWindowProc() and gets ignored. So, the parent window needs to handle WM_COMMAND. When the notification code is BN_CLICKED, the parent can look up the provided HWND in its list of buttons, and if found then call the corresponding function if one has been assigned.

    – Remy Lebeau
    Nov 17 '18 at 2:51













  • Could you possibly give an example of what you're talking about?

    – Joshua
    Nov 17 '18 at 3:12






  • 1





    Hate to be the one, but I'll be the one to throw cold water on this project. To create a C++ wrapper for the Windows API, you better know the Windows API itself from its C-based roots. That means having concrete C-based examples that work, plus having books is the way to learn the API. Otherwise you totally miss the things mentioned by @RemyLebeau.

    – PaulMcKenzie
    Nov 17 '18 at 3:22













  • referencesource.microsoft.com/#System.Windows.Forms/winforms/…

    – Hans Passant
    Nov 17 '18 at 7:43














  • 2





    One way to do this is to have the window procedure consult a map from buttons to functions. When you add a button, add an entry to the map.

    – Raymond Chen
    Nov 17 '18 at 2:45






  • 1





    No matter what you do, the parent window of the button is going to receive a BN_CLICKED notification via WM_COMMAND when the button is clicked. If the parent window doesn't handle the message, it ends up going to DefWindowProc() and gets ignored. So, the parent window needs to handle WM_COMMAND. When the notification code is BN_CLICKED, the parent can look up the provided HWND in its list of buttons, and if found then call the corresponding function if one has been assigned.

    – Remy Lebeau
    Nov 17 '18 at 2:51













  • Could you possibly give an example of what you're talking about?

    – Joshua
    Nov 17 '18 at 3:12






  • 1





    Hate to be the one, but I'll be the one to throw cold water on this project. To create a C++ wrapper for the Windows API, you better know the Windows API itself from its C-based roots. That means having concrete C-based examples that work, plus having books is the way to learn the API. Otherwise you totally miss the things mentioned by @RemyLebeau.

    – PaulMcKenzie
    Nov 17 '18 at 3:22













  • referencesource.microsoft.com/#System.Windows.Forms/winforms/…

    – Hans Passant
    Nov 17 '18 at 7:43








2




2





One way to do this is to have the window procedure consult a map from buttons to functions. When you add a button, add an entry to the map.

– Raymond Chen
Nov 17 '18 at 2:45





One way to do this is to have the window procedure consult a map from buttons to functions. When you add a button, add an entry to the map.

– Raymond Chen
Nov 17 '18 at 2:45




1




1





No matter what you do, the parent window of the button is going to receive a BN_CLICKED notification via WM_COMMAND when the button is clicked. If the parent window doesn't handle the message, it ends up going to DefWindowProc() and gets ignored. So, the parent window needs to handle WM_COMMAND. When the notification code is BN_CLICKED, the parent can look up the provided HWND in its list of buttons, and if found then call the corresponding function if one has been assigned.

– Remy Lebeau
Nov 17 '18 at 2:51







No matter what you do, the parent window of the button is going to receive a BN_CLICKED notification via WM_COMMAND when the button is clicked. If the parent window doesn't handle the message, it ends up going to DefWindowProc() and gets ignored. So, the parent window needs to handle WM_COMMAND. When the notification code is BN_CLICKED, the parent can look up the provided HWND in its list of buttons, and if found then call the corresponding function if one has been assigned.

– Remy Lebeau
Nov 17 '18 at 2:51















Could you possibly give an example of what you're talking about?

– Joshua
Nov 17 '18 at 3:12





Could you possibly give an example of what you're talking about?

– Joshua
Nov 17 '18 at 3:12




1




1





Hate to be the one, but I'll be the one to throw cold water on this project. To create a C++ wrapper for the Windows API, you better know the Windows API itself from its C-based roots. That means having concrete C-based examples that work, plus having books is the way to learn the API. Otherwise you totally miss the things mentioned by @RemyLebeau.

– PaulMcKenzie
Nov 17 '18 at 3:22







Hate to be the one, but I'll be the one to throw cold water on this project. To create a C++ wrapper for the Windows API, you better know the Windows API itself from its C-based roots. That means having concrete C-based examples that work, plus having books is the way to learn the API. Otherwise you totally miss the things mentioned by @RemyLebeau.

– PaulMcKenzie
Nov 17 '18 at 3:22















referencesource.microsoft.com/#System.Windows.Forms/winforms/…

– Hans Passant
Nov 17 '18 at 7:43





referencesource.microsoft.com/#System.Windows.Forms/winforms/…

– Hans Passant
Nov 17 '18 at 7:43












1 Answer
1






active

oldest

votes


















3














The parent window of the button is going to receive a BN_CLICKED notification via WM_COMMAND when the button is clicked. If the parent window doesn't handle the message, it ends up going to DefWindowProc() and gets ignored. So, the parent window needs to handle WM_COMMAND. When the notification code is BN_CLICKED, simply lookup the provided button HWND in your list of buttons, and if found then call the corresponding function, if one has been assigned.



#include <Windows.h>
#include <vector>
#include <thread>
#include <algorithm>
#include <functional>

class WinForm
{
public:
using ControlActionFunc = std::function<void(const std::string &)>;

WinForm(std::string ClassName, std::string WindowName, bool Threaded = false, int Width = CW_USEDEFAULT, int Height = CW_USEDEFAULT);

~WinForm();

bool AddButton(const std::string &ButtonName, POINT Location, int Width, int Height, ControlActionFunc OnClick);

private:
HWND WindowHandle;
std::thread Thread;

using ControlInfo = std::tuple<std::string, std::size_t, HWND, ControlActionFunc>;
using ControlInfoVector = std::vector<ControlInfo>;
ControlInfoVector Controls;

static LRESULT __stdcall StaticWindowProcedure(HWND window, UINT msg, WPARAM wp, LPARAM lp);

protected:
virtual LRESULT WindowProcedure(UINT msg, WPARAM wp, LPARAM lp);
};

class MainAppWinForm : public WInForm
{
public:
using WinForm::WinForm;

protected:
LRESULT WindowProcedure(UINT msg, WPARAM wp, LPARAM lp) override;
};

WinForm::WinForm(std::string ClassName, std::string WindowName, bool Threaded, int Width, int Height)
: WindowHandle(nullptr)
{
HINSTANCE hInstance = GetModuleHandle(nullptr);
WNDCLASSEX WndClass = {};

bool isRegistered = GetClassInfoEx(hInstance, ClassName.c_str(), &WndClass);
if ((!isRegistered) || (WndClass.lpfnWndProc != &WinForm::StaticWindowProcedure))
{
if (isRegistered)
UnregisterClass(ClassName.c_str(), hInstance);

WndClass.cbSize = sizeof(WNDCLASSEX);
WndClass.style = CS_DBLCLKS;
WndClass.lpfnWndProc = &WinForm::StaticWindowProcedure;
WndClass.cbClsExtra = 0;
WndClass.cbWndExtra = sizeof(WinForm*);
WndClass.hInstance = hInstance;
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))
return;
}

if (Threaded)
{
// can't do that!
return;
}

WindowHandle = CreateWindowEx(0, ClassName.c_str(), WindowName.c_str(), WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, Width, Height, nullptr, nullptr, hInstance, this);
if (!WindowHandle)
return;

ShowWindow(WindowHandle, SW_SHOWDEFAULT);

// don't put message loop here!
}

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

LRESULT __stdcall WinForm::StaticWindowProcedure(HWND window, UINT msg, WPARAM wp, LPARAM lp)
{
WinForm *This;
if (msg == WM_NCCREATE)
{
This = static_cast<WinForm*>(reinterpret_cast<CREATESTRUCT*>(lp)->lpCreateParams);
This->WindowHandle = window;
SetWindowLongPtr(window, 0, reinterpret_cast<LONG_PTR>(This));
}
else
This = reinterpret_cast<WinForm*>(GetWindowLongPtr(window, 0));

if (This)
return This->WindowProcedure(msg, wp, lp);

return DefWindowProc(window, msg, wp, lp);
}

LRESULT WinForm::WindowProcedure(UINT msg, WPARAM wp, LPARAM lp)
{
switch (msg)
{
/*
case WM_PAINT:
break;
*/

case WM_COMMAND:
{
if (lp != 0)
{
if (HIWORD(wp) == BN_CLICKED)
{
HWND ControlWindow = reinterpret_cast<HWND>(lp);

auto it = std::find_if(Controls.begin(), Controls.end(),
(ControlInfo &info){ return (std::get<2>(info) == ControlWindow); }
);

if (it != Controls.end())
{
auto &tu = *it;
auto actionFunc = std::get<3>(tu);
if (actionFunc) actionFunc(std::get<0>(tu));
return 0;
}
}
}
break;
}

case WM_CREATE:
break;
}

return DefWindowProc(WindowHandle, msg, wp, lp);
}

bool WinForm::AddButton(const std::string &ButtonName, POINT Location, int Width, int Height, ControlActionFunc OnClick)
{
auto it = std::find_if(Controls.begin(), Controls.end(),
[&](ControlInfo &info){ return (std::get<0>(info).compare(ButtonName) == 0); }
);

if (it != Controls.end()) {
return false;
}

std::size_t ID = 1;
auto matchesID = [&](ControlInfo &info){ return (std::get<1>(tu) == ID); };
while (std::find_if(Controls.begin(), Controls.end(), matchesID) != Controls.end()) {
++ID;
}

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);
if (!ButtonHandle)
return false;

Controls.push_back(std::make_tuple(ButtonName, ID, ButtonHandle, std::move(OnClick)));
return true;
}

LRESULT MainAppWinForm::WindowProcedure(UINT msg, WPARAM wp, LPARAM lp)
{
if (msg == WM_DESTROY)
{
PostQuitMessage(0);
return 0;
}
return WinForm::WindowProcedure(msg, wp, lp);
}

int WINAPI WinMain(HINSTANCE, HINSTANCE, LPSTR, int)
{
MainAppWinForm Form("Class", "Title", false);

POINT pt = { 50, 50 };
Form.AddButton("NewButton", pt, 80, 50,
(const std::string &ButtonName){ MessageBox(NULL, ButtonName.c_str(), "button clicked", MB_OK); }
);

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

return 0;
}


Also see



Class method for WndProc



Win32: More "object oriented" window message handling system



Best method for storing this pointer for use in WndProc






share|improve this answer


























  • I couldn't compile your code on VS2017. I made some changes but there is a main issue, in that when This->WindowProcedure(msg, wp, lp); is called at first, there is no WindowHandle, subsequently CreateWindowEx fails

    – Barmak Shemirani
    Nov 17 '18 at 6:55






  • 1





    @BarmakShemirani I wrote the code from memory, I didn't try to compile it. Wouldn't surprise me if it needs further tweaking. But it should be clear what the intent of the code is trying to demonstate. I fixed the WindowHandle issue, though.

    – Remy Lebeau
    Nov 17 '18 at 7:11








  • 1





    for top-level windows first message is WM_GETMINMAXINFO (before WM_NCCREATE), as result will be ignored. if want handle it, and any possible messages before WM_NCCREATE, too save pointer to WinForm in tls, in StaticWindowProcedure - get it from tls, assign to window via SetWindowLongPtr and change GWLP_WNDPROC to say StaticWindowProcedure2. inside this we already unconditionally got pointer to WinForm via GetWindowLongPtr - never will be 0.

    – RbMm
    Nov 17 '18 at 9:53











  • I see in order for this to work you would have to use WndClass.cbWndExtra = sizeof(WinForm*) or it will not work. Now that's an issue because when you close one window with this method every window you have will also close.

    – Joshua
    Nov 17 '18 at 21:16






  • 1





    @Joshua you don't have to use cbWndExtra, GWLP_USERDATA will work, too. All HWNDs have GWLP_USERDATA available for free, which makes it appealing for users to use. In this case, I chose to use cbWndExtra to keep the WinForm* pointer private and leave GWLP_USERDATA free for the caller to use for its own purposes. And no, closing one window does not affect other windows. Every individual WinForm HWND allocated in this code has its own distinct WinForm* pointer assigned to it. There is no linkage between multiple WinForm instances

    – Remy Lebeau
    Nov 17 '18 at 22:21













Your Answer






StackExchange.ifUsing("editor", function () {
StackExchange.using("externalEditor", function () {
StackExchange.using("snippets", function () {
StackExchange.snippets.init();
});
});
}, "code-snippets");

StackExchange.ready(function() {
var channelOptions = {
tags: "".split(" "),
id: "1"
};
initTagRenderer("".split(" "), "".split(" "), channelOptions);

StackExchange.using("externalEditor", function() {
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled) {
StackExchange.using("snippets", function() {
createEditor();
});
}
else {
createEditor();
}
});

function createEditor() {
StackExchange.prepareEditor({
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: true,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: 10,
bindNavPrevention: true,
postfix: "",
imageUploader: {
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
},
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
});


}
});














draft saved

draft discarded


















StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53347631%2fc-winapi-dynamic-wrapper-class%23new-answer', 'question_page');
}
);

Post as a guest















Required, but never shown

























1 Answer
1






active

oldest

votes








1 Answer
1






active

oldest

votes









active

oldest

votes






active

oldest

votes









3














The parent window of the button is going to receive a BN_CLICKED notification via WM_COMMAND when the button is clicked. If the parent window doesn't handle the message, it ends up going to DefWindowProc() and gets ignored. So, the parent window needs to handle WM_COMMAND. When the notification code is BN_CLICKED, simply lookup the provided button HWND in your list of buttons, and if found then call the corresponding function, if one has been assigned.



#include <Windows.h>
#include <vector>
#include <thread>
#include <algorithm>
#include <functional>

class WinForm
{
public:
using ControlActionFunc = std::function<void(const std::string &)>;

WinForm(std::string ClassName, std::string WindowName, bool Threaded = false, int Width = CW_USEDEFAULT, int Height = CW_USEDEFAULT);

~WinForm();

bool AddButton(const std::string &ButtonName, POINT Location, int Width, int Height, ControlActionFunc OnClick);

private:
HWND WindowHandle;
std::thread Thread;

using ControlInfo = std::tuple<std::string, std::size_t, HWND, ControlActionFunc>;
using ControlInfoVector = std::vector<ControlInfo>;
ControlInfoVector Controls;

static LRESULT __stdcall StaticWindowProcedure(HWND window, UINT msg, WPARAM wp, LPARAM lp);

protected:
virtual LRESULT WindowProcedure(UINT msg, WPARAM wp, LPARAM lp);
};

class MainAppWinForm : public WInForm
{
public:
using WinForm::WinForm;

protected:
LRESULT WindowProcedure(UINT msg, WPARAM wp, LPARAM lp) override;
};

WinForm::WinForm(std::string ClassName, std::string WindowName, bool Threaded, int Width, int Height)
: WindowHandle(nullptr)
{
HINSTANCE hInstance = GetModuleHandle(nullptr);
WNDCLASSEX WndClass = {};

bool isRegistered = GetClassInfoEx(hInstance, ClassName.c_str(), &WndClass);
if ((!isRegistered) || (WndClass.lpfnWndProc != &WinForm::StaticWindowProcedure))
{
if (isRegistered)
UnregisterClass(ClassName.c_str(), hInstance);

WndClass.cbSize = sizeof(WNDCLASSEX);
WndClass.style = CS_DBLCLKS;
WndClass.lpfnWndProc = &WinForm::StaticWindowProcedure;
WndClass.cbClsExtra = 0;
WndClass.cbWndExtra = sizeof(WinForm*);
WndClass.hInstance = hInstance;
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))
return;
}

if (Threaded)
{
// can't do that!
return;
}

WindowHandle = CreateWindowEx(0, ClassName.c_str(), WindowName.c_str(), WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, Width, Height, nullptr, nullptr, hInstance, this);
if (!WindowHandle)
return;

ShowWindow(WindowHandle, SW_SHOWDEFAULT);

// don't put message loop here!
}

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

LRESULT __stdcall WinForm::StaticWindowProcedure(HWND window, UINT msg, WPARAM wp, LPARAM lp)
{
WinForm *This;
if (msg == WM_NCCREATE)
{
This = static_cast<WinForm*>(reinterpret_cast<CREATESTRUCT*>(lp)->lpCreateParams);
This->WindowHandle = window;
SetWindowLongPtr(window, 0, reinterpret_cast<LONG_PTR>(This));
}
else
This = reinterpret_cast<WinForm*>(GetWindowLongPtr(window, 0));

if (This)
return This->WindowProcedure(msg, wp, lp);

return DefWindowProc(window, msg, wp, lp);
}

LRESULT WinForm::WindowProcedure(UINT msg, WPARAM wp, LPARAM lp)
{
switch (msg)
{
/*
case WM_PAINT:
break;
*/

case WM_COMMAND:
{
if (lp != 0)
{
if (HIWORD(wp) == BN_CLICKED)
{
HWND ControlWindow = reinterpret_cast<HWND>(lp);

auto it = std::find_if(Controls.begin(), Controls.end(),
(ControlInfo &info){ return (std::get<2>(info) == ControlWindow); }
);

if (it != Controls.end())
{
auto &tu = *it;
auto actionFunc = std::get<3>(tu);
if (actionFunc) actionFunc(std::get<0>(tu));
return 0;
}
}
}
break;
}

case WM_CREATE:
break;
}

return DefWindowProc(WindowHandle, msg, wp, lp);
}

bool WinForm::AddButton(const std::string &ButtonName, POINT Location, int Width, int Height, ControlActionFunc OnClick)
{
auto it = std::find_if(Controls.begin(), Controls.end(),
[&](ControlInfo &info){ return (std::get<0>(info).compare(ButtonName) == 0); }
);

if (it != Controls.end()) {
return false;
}

std::size_t ID = 1;
auto matchesID = [&](ControlInfo &info){ return (std::get<1>(tu) == ID); };
while (std::find_if(Controls.begin(), Controls.end(), matchesID) != Controls.end()) {
++ID;
}

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);
if (!ButtonHandle)
return false;

Controls.push_back(std::make_tuple(ButtonName, ID, ButtonHandle, std::move(OnClick)));
return true;
}

LRESULT MainAppWinForm::WindowProcedure(UINT msg, WPARAM wp, LPARAM lp)
{
if (msg == WM_DESTROY)
{
PostQuitMessage(0);
return 0;
}
return WinForm::WindowProcedure(msg, wp, lp);
}

int WINAPI WinMain(HINSTANCE, HINSTANCE, LPSTR, int)
{
MainAppWinForm Form("Class", "Title", false);

POINT pt = { 50, 50 };
Form.AddButton("NewButton", pt, 80, 50,
(const std::string &ButtonName){ MessageBox(NULL, ButtonName.c_str(), "button clicked", MB_OK); }
);

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

return 0;
}


Also see



Class method for WndProc



Win32: More "object oriented" window message handling system



Best method for storing this pointer for use in WndProc






share|improve this answer


























  • I couldn't compile your code on VS2017. I made some changes but there is a main issue, in that when This->WindowProcedure(msg, wp, lp); is called at first, there is no WindowHandle, subsequently CreateWindowEx fails

    – Barmak Shemirani
    Nov 17 '18 at 6:55






  • 1





    @BarmakShemirani I wrote the code from memory, I didn't try to compile it. Wouldn't surprise me if it needs further tweaking. But it should be clear what the intent of the code is trying to demonstate. I fixed the WindowHandle issue, though.

    – Remy Lebeau
    Nov 17 '18 at 7:11








  • 1





    for top-level windows first message is WM_GETMINMAXINFO (before WM_NCCREATE), as result will be ignored. if want handle it, and any possible messages before WM_NCCREATE, too save pointer to WinForm in tls, in StaticWindowProcedure - get it from tls, assign to window via SetWindowLongPtr and change GWLP_WNDPROC to say StaticWindowProcedure2. inside this we already unconditionally got pointer to WinForm via GetWindowLongPtr - never will be 0.

    – RbMm
    Nov 17 '18 at 9:53











  • I see in order for this to work you would have to use WndClass.cbWndExtra = sizeof(WinForm*) or it will not work. Now that's an issue because when you close one window with this method every window you have will also close.

    – Joshua
    Nov 17 '18 at 21:16






  • 1





    @Joshua you don't have to use cbWndExtra, GWLP_USERDATA will work, too. All HWNDs have GWLP_USERDATA available for free, which makes it appealing for users to use. In this case, I chose to use cbWndExtra to keep the WinForm* pointer private and leave GWLP_USERDATA free for the caller to use for its own purposes. And no, closing one window does not affect other windows. Every individual WinForm HWND allocated in this code has its own distinct WinForm* pointer assigned to it. There is no linkage between multiple WinForm instances

    – Remy Lebeau
    Nov 17 '18 at 22:21


















3














The parent window of the button is going to receive a BN_CLICKED notification via WM_COMMAND when the button is clicked. If the parent window doesn't handle the message, it ends up going to DefWindowProc() and gets ignored. So, the parent window needs to handle WM_COMMAND. When the notification code is BN_CLICKED, simply lookup the provided button HWND in your list of buttons, and if found then call the corresponding function, if one has been assigned.



#include <Windows.h>
#include <vector>
#include <thread>
#include <algorithm>
#include <functional>

class WinForm
{
public:
using ControlActionFunc = std::function<void(const std::string &)>;

WinForm(std::string ClassName, std::string WindowName, bool Threaded = false, int Width = CW_USEDEFAULT, int Height = CW_USEDEFAULT);

~WinForm();

bool AddButton(const std::string &ButtonName, POINT Location, int Width, int Height, ControlActionFunc OnClick);

private:
HWND WindowHandle;
std::thread Thread;

using ControlInfo = std::tuple<std::string, std::size_t, HWND, ControlActionFunc>;
using ControlInfoVector = std::vector<ControlInfo>;
ControlInfoVector Controls;

static LRESULT __stdcall StaticWindowProcedure(HWND window, UINT msg, WPARAM wp, LPARAM lp);

protected:
virtual LRESULT WindowProcedure(UINT msg, WPARAM wp, LPARAM lp);
};

class MainAppWinForm : public WInForm
{
public:
using WinForm::WinForm;

protected:
LRESULT WindowProcedure(UINT msg, WPARAM wp, LPARAM lp) override;
};

WinForm::WinForm(std::string ClassName, std::string WindowName, bool Threaded, int Width, int Height)
: WindowHandle(nullptr)
{
HINSTANCE hInstance = GetModuleHandle(nullptr);
WNDCLASSEX WndClass = {};

bool isRegistered = GetClassInfoEx(hInstance, ClassName.c_str(), &WndClass);
if ((!isRegistered) || (WndClass.lpfnWndProc != &WinForm::StaticWindowProcedure))
{
if (isRegistered)
UnregisterClass(ClassName.c_str(), hInstance);

WndClass.cbSize = sizeof(WNDCLASSEX);
WndClass.style = CS_DBLCLKS;
WndClass.lpfnWndProc = &WinForm::StaticWindowProcedure;
WndClass.cbClsExtra = 0;
WndClass.cbWndExtra = sizeof(WinForm*);
WndClass.hInstance = hInstance;
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))
return;
}

if (Threaded)
{
// can't do that!
return;
}

WindowHandle = CreateWindowEx(0, ClassName.c_str(), WindowName.c_str(), WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, Width, Height, nullptr, nullptr, hInstance, this);
if (!WindowHandle)
return;

ShowWindow(WindowHandle, SW_SHOWDEFAULT);

// don't put message loop here!
}

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

LRESULT __stdcall WinForm::StaticWindowProcedure(HWND window, UINT msg, WPARAM wp, LPARAM lp)
{
WinForm *This;
if (msg == WM_NCCREATE)
{
This = static_cast<WinForm*>(reinterpret_cast<CREATESTRUCT*>(lp)->lpCreateParams);
This->WindowHandle = window;
SetWindowLongPtr(window, 0, reinterpret_cast<LONG_PTR>(This));
}
else
This = reinterpret_cast<WinForm*>(GetWindowLongPtr(window, 0));

if (This)
return This->WindowProcedure(msg, wp, lp);

return DefWindowProc(window, msg, wp, lp);
}

LRESULT WinForm::WindowProcedure(UINT msg, WPARAM wp, LPARAM lp)
{
switch (msg)
{
/*
case WM_PAINT:
break;
*/

case WM_COMMAND:
{
if (lp != 0)
{
if (HIWORD(wp) == BN_CLICKED)
{
HWND ControlWindow = reinterpret_cast<HWND>(lp);

auto it = std::find_if(Controls.begin(), Controls.end(),
(ControlInfo &info){ return (std::get<2>(info) == ControlWindow); }
);

if (it != Controls.end())
{
auto &tu = *it;
auto actionFunc = std::get<3>(tu);
if (actionFunc) actionFunc(std::get<0>(tu));
return 0;
}
}
}
break;
}

case WM_CREATE:
break;
}

return DefWindowProc(WindowHandle, msg, wp, lp);
}

bool WinForm::AddButton(const std::string &ButtonName, POINT Location, int Width, int Height, ControlActionFunc OnClick)
{
auto it = std::find_if(Controls.begin(), Controls.end(),
[&](ControlInfo &info){ return (std::get<0>(info).compare(ButtonName) == 0); }
);

if (it != Controls.end()) {
return false;
}

std::size_t ID = 1;
auto matchesID = [&](ControlInfo &info){ return (std::get<1>(tu) == ID); };
while (std::find_if(Controls.begin(), Controls.end(), matchesID) != Controls.end()) {
++ID;
}

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);
if (!ButtonHandle)
return false;

Controls.push_back(std::make_tuple(ButtonName, ID, ButtonHandle, std::move(OnClick)));
return true;
}

LRESULT MainAppWinForm::WindowProcedure(UINT msg, WPARAM wp, LPARAM lp)
{
if (msg == WM_DESTROY)
{
PostQuitMessage(0);
return 0;
}
return WinForm::WindowProcedure(msg, wp, lp);
}

int WINAPI WinMain(HINSTANCE, HINSTANCE, LPSTR, int)
{
MainAppWinForm Form("Class", "Title", false);

POINT pt = { 50, 50 };
Form.AddButton("NewButton", pt, 80, 50,
(const std::string &ButtonName){ MessageBox(NULL, ButtonName.c_str(), "button clicked", MB_OK); }
);

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

return 0;
}


Also see



Class method for WndProc



Win32: More "object oriented" window message handling system



Best method for storing this pointer for use in WndProc






share|improve this answer


























  • I couldn't compile your code on VS2017. I made some changes but there is a main issue, in that when This->WindowProcedure(msg, wp, lp); is called at first, there is no WindowHandle, subsequently CreateWindowEx fails

    – Barmak Shemirani
    Nov 17 '18 at 6:55






  • 1





    @BarmakShemirani I wrote the code from memory, I didn't try to compile it. Wouldn't surprise me if it needs further tweaking. But it should be clear what the intent of the code is trying to demonstate. I fixed the WindowHandle issue, though.

    – Remy Lebeau
    Nov 17 '18 at 7:11








  • 1





    for top-level windows first message is WM_GETMINMAXINFO (before WM_NCCREATE), as result will be ignored. if want handle it, and any possible messages before WM_NCCREATE, too save pointer to WinForm in tls, in StaticWindowProcedure - get it from tls, assign to window via SetWindowLongPtr and change GWLP_WNDPROC to say StaticWindowProcedure2. inside this we already unconditionally got pointer to WinForm via GetWindowLongPtr - never will be 0.

    – RbMm
    Nov 17 '18 at 9:53











  • I see in order for this to work you would have to use WndClass.cbWndExtra = sizeof(WinForm*) or it will not work. Now that's an issue because when you close one window with this method every window you have will also close.

    – Joshua
    Nov 17 '18 at 21:16






  • 1





    @Joshua you don't have to use cbWndExtra, GWLP_USERDATA will work, too. All HWNDs have GWLP_USERDATA available for free, which makes it appealing for users to use. In this case, I chose to use cbWndExtra to keep the WinForm* pointer private and leave GWLP_USERDATA free for the caller to use for its own purposes. And no, closing one window does not affect other windows. Every individual WinForm HWND allocated in this code has its own distinct WinForm* pointer assigned to it. There is no linkage between multiple WinForm instances

    – Remy Lebeau
    Nov 17 '18 at 22:21
















3












3








3







The parent window of the button is going to receive a BN_CLICKED notification via WM_COMMAND when the button is clicked. If the parent window doesn't handle the message, it ends up going to DefWindowProc() and gets ignored. So, the parent window needs to handle WM_COMMAND. When the notification code is BN_CLICKED, simply lookup the provided button HWND in your list of buttons, and if found then call the corresponding function, if one has been assigned.



#include <Windows.h>
#include <vector>
#include <thread>
#include <algorithm>
#include <functional>

class WinForm
{
public:
using ControlActionFunc = std::function<void(const std::string &)>;

WinForm(std::string ClassName, std::string WindowName, bool Threaded = false, int Width = CW_USEDEFAULT, int Height = CW_USEDEFAULT);

~WinForm();

bool AddButton(const std::string &ButtonName, POINT Location, int Width, int Height, ControlActionFunc OnClick);

private:
HWND WindowHandle;
std::thread Thread;

using ControlInfo = std::tuple<std::string, std::size_t, HWND, ControlActionFunc>;
using ControlInfoVector = std::vector<ControlInfo>;
ControlInfoVector Controls;

static LRESULT __stdcall StaticWindowProcedure(HWND window, UINT msg, WPARAM wp, LPARAM lp);

protected:
virtual LRESULT WindowProcedure(UINT msg, WPARAM wp, LPARAM lp);
};

class MainAppWinForm : public WInForm
{
public:
using WinForm::WinForm;

protected:
LRESULT WindowProcedure(UINT msg, WPARAM wp, LPARAM lp) override;
};

WinForm::WinForm(std::string ClassName, std::string WindowName, bool Threaded, int Width, int Height)
: WindowHandle(nullptr)
{
HINSTANCE hInstance = GetModuleHandle(nullptr);
WNDCLASSEX WndClass = {};

bool isRegistered = GetClassInfoEx(hInstance, ClassName.c_str(), &WndClass);
if ((!isRegistered) || (WndClass.lpfnWndProc != &WinForm::StaticWindowProcedure))
{
if (isRegistered)
UnregisterClass(ClassName.c_str(), hInstance);

WndClass.cbSize = sizeof(WNDCLASSEX);
WndClass.style = CS_DBLCLKS;
WndClass.lpfnWndProc = &WinForm::StaticWindowProcedure;
WndClass.cbClsExtra = 0;
WndClass.cbWndExtra = sizeof(WinForm*);
WndClass.hInstance = hInstance;
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))
return;
}

if (Threaded)
{
// can't do that!
return;
}

WindowHandle = CreateWindowEx(0, ClassName.c_str(), WindowName.c_str(), WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, Width, Height, nullptr, nullptr, hInstance, this);
if (!WindowHandle)
return;

ShowWindow(WindowHandle, SW_SHOWDEFAULT);

// don't put message loop here!
}

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

LRESULT __stdcall WinForm::StaticWindowProcedure(HWND window, UINT msg, WPARAM wp, LPARAM lp)
{
WinForm *This;
if (msg == WM_NCCREATE)
{
This = static_cast<WinForm*>(reinterpret_cast<CREATESTRUCT*>(lp)->lpCreateParams);
This->WindowHandle = window;
SetWindowLongPtr(window, 0, reinterpret_cast<LONG_PTR>(This));
}
else
This = reinterpret_cast<WinForm*>(GetWindowLongPtr(window, 0));

if (This)
return This->WindowProcedure(msg, wp, lp);

return DefWindowProc(window, msg, wp, lp);
}

LRESULT WinForm::WindowProcedure(UINT msg, WPARAM wp, LPARAM lp)
{
switch (msg)
{
/*
case WM_PAINT:
break;
*/

case WM_COMMAND:
{
if (lp != 0)
{
if (HIWORD(wp) == BN_CLICKED)
{
HWND ControlWindow = reinterpret_cast<HWND>(lp);

auto it = std::find_if(Controls.begin(), Controls.end(),
(ControlInfo &info){ return (std::get<2>(info) == ControlWindow); }
);

if (it != Controls.end())
{
auto &tu = *it;
auto actionFunc = std::get<3>(tu);
if (actionFunc) actionFunc(std::get<0>(tu));
return 0;
}
}
}
break;
}

case WM_CREATE:
break;
}

return DefWindowProc(WindowHandle, msg, wp, lp);
}

bool WinForm::AddButton(const std::string &ButtonName, POINT Location, int Width, int Height, ControlActionFunc OnClick)
{
auto it = std::find_if(Controls.begin(), Controls.end(),
[&](ControlInfo &info){ return (std::get<0>(info).compare(ButtonName) == 0); }
);

if (it != Controls.end()) {
return false;
}

std::size_t ID = 1;
auto matchesID = [&](ControlInfo &info){ return (std::get<1>(tu) == ID); };
while (std::find_if(Controls.begin(), Controls.end(), matchesID) != Controls.end()) {
++ID;
}

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);
if (!ButtonHandle)
return false;

Controls.push_back(std::make_tuple(ButtonName, ID, ButtonHandle, std::move(OnClick)));
return true;
}

LRESULT MainAppWinForm::WindowProcedure(UINT msg, WPARAM wp, LPARAM lp)
{
if (msg == WM_DESTROY)
{
PostQuitMessage(0);
return 0;
}
return WinForm::WindowProcedure(msg, wp, lp);
}

int WINAPI WinMain(HINSTANCE, HINSTANCE, LPSTR, int)
{
MainAppWinForm Form("Class", "Title", false);

POINT pt = { 50, 50 };
Form.AddButton("NewButton", pt, 80, 50,
(const std::string &ButtonName){ MessageBox(NULL, ButtonName.c_str(), "button clicked", MB_OK); }
);

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

return 0;
}


Also see



Class method for WndProc



Win32: More "object oriented" window message handling system



Best method for storing this pointer for use in WndProc






share|improve this answer















The parent window of the button is going to receive a BN_CLICKED notification via WM_COMMAND when the button is clicked. If the parent window doesn't handle the message, it ends up going to DefWindowProc() and gets ignored. So, the parent window needs to handle WM_COMMAND. When the notification code is BN_CLICKED, simply lookup the provided button HWND in your list of buttons, and if found then call the corresponding function, if one has been assigned.



#include <Windows.h>
#include <vector>
#include <thread>
#include <algorithm>
#include <functional>

class WinForm
{
public:
using ControlActionFunc = std::function<void(const std::string &)>;

WinForm(std::string ClassName, std::string WindowName, bool Threaded = false, int Width = CW_USEDEFAULT, int Height = CW_USEDEFAULT);

~WinForm();

bool AddButton(const std::string &ButtonName, POINT Location, int Width, int Height, ControlActionFunc OnClick);

private:
HWND WindowHandle;
std::thread Thread;

using ControlInfo = std::tuple<std::string, std::size_t, HWND, ControlActionFunc>;
using ControlInfoVector = std::vector<ControlInfo>;
ControlInfoVector Controls;

static LRESULT __stdcall StaticWindowProcedure(HWND window, UINT msg, WPARAM wp, LPARAM lp);

protected:
virtual LRESULT WindowProcedure(UINT msg, WPARAM wp, LPARAM lp);
};

class MainAppWinForm : public WInForm
{
public:
using WinForm::WinForm;

protected:
LRESULT WindowProcedure(UINT msg, WPARAM wp, LPARAM lp) override;
};

WinForm::WinForm(std::string ClassName, std::string WindowName, bool Threaded, int Width, int Height)
: WindowHandle(nullptr)
{
HINSTANCE hInstance = GetModuleHandle(nullptr);
WNDCLASSEX WndClass = {};

bool isRegistered = GetClassInfoEx(hInstance, ClassName.c_str(), &WndClass);
if ((!isRegistered) || (WndClass.lpfnWndProc != &WinForm::StaticWindowProcedure))
{
if (isRegistered)
UnregisterClass(ClassName.c_str(), hInstance);

WndClass.cbSize = sizeof(WNDCLASSEX);
WndClass.style = CS_DBLCLKS;
WndClass.lpfnWndProc = &WinForm::StaticWindowProcedure;
WndClass.cbClsExtra = 0;
WndClass.cbWndExtra = sizeof(WinForm*);
WndClass.hInstance = hInstance;
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))
return;
}

if (Threaded)
{
// can't do that!
return;
}

WindowHandle = CreateWindowEx(0, ClassName.c_str(), WindowName.c_str(), WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, Width, Height, nullptr, nullptr, hInstance, this);
if (!WindowHandle)
return;

ShowWindow(WindowHandle, SW_SHOWDEFAULT);

// don't put message loop here!
}

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

LRESULT __stdcall WinForm::StaticWindowProcedure(HWND window, UINT msg, WPARAM wp, LPARAM lp)
{
WinForm *This;
if (msg == WM_NCCREATE)
{
This = static_cast<WinForm*>(reinterpret_cast<CREATESTRUCT*>(lp)->lpCreateParams);
This->WindowHandle = window;
SetWindowLongPtr(window, 0, reinterpret_cast<LONG_PTR>(This));
}
else
This = reinterpret_cast<WinForm*>(GetWindowLongPtr(window, 0));

if (This)
return This->WindowProcedure(msg, wp, lp);

return DefWindowProc(window, msg, wp, lp);
}

LRESULT WinForm::WindowProcedure(UINT msg, WPARAM wp, LPARAM lp)
{
switch (msg)
{
/*
case WM_PAINT:
break;
*/

case WM_COMMAND:
{
if (lp != 0)
{
if (HIWORD(wp) == BN_CLICKED)
{
HWND ControlWindow = reinterpret_cast<HWND>(lp);

auto it = std::find_if(Controls.begin(), Controls.end(),
(ControlInfo &info){ return (std::get<2>(info) == ControlWindow); }
);

if (it != Controls.end())
{
auto &tu = *it;
auto actionFunc = std::get<3>(tu);
if (actionFunc) actionFunc(std::get<0>(tu));
return 0;
}
}
}
break;
}

case WM_CREATE:
break;
}

return DefWindowProc(WindowHandle, msg, wp, lp);
}

bool WinForm::AddButton(const std::string &ButtonName, POINT Location, int Width, int Height, ControlActionFunc OnClick)
{
auto it = std::find_if(Controls.begin(), Controls.end(),
[&](ControlInfo &info){ return (std::get<0>(info).compare(ButtonName) == 0); }
);

if (it != Controls.end()) {
return false;
}

std::size_t ID = 1;
auto matchesID = [&](ControlInfo &info){ return (std::get<1>(tu) == ID); };
while (std::find_if(Controls.begin(), Controls.end(), matchesID) != Controls.end()) {
++ID;
}

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);
if (!ButtonHandle)
return false;

Controls.push_back(std::make_tuple(ButtonName, ID, ButtonHandle, std::move(OnClick)));
return true;
}

LRESULT MainAppWinForm::WindowProcedure(UINT msg, WPARAM wp, LPARAM lp)
{
if (msg == WM_DESTROY)
{
PostQuitMessage(0);
return 0;
}
return WinForm::WindowProcedure(msg, wp, lp);
}

int WINAPI WinMain(HINSTANCE, HINSTANCE, LPSTR, int)
{
MainAppWinForm Form("Class", "Title", false);

POINT pt = { 50, 50 };
Form.AddButton("NewButton", pt, 80, 50,
(const std::string &ButtonName){ MessageBox(NULL, ButtonName.c_str(), "button clicked", MB_OK); }
);

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

return 0;
}


Also see



Class method for WndProc



Win32: More "object oriented" window message handling system



Best method for storing this pointer for use in WndProc







share|improve this answer














share|improve this answer



share|improve this answer








edited Nov 18 '18 at 21:17

























answered Nov 17 '18 at 4:55









Remy LebeauRemy Lebeau

339k19262456




339k19262456













  • I couldn't compile your code on VS2017. I made some changes but there is a main issue, in that when This->WindowProcedure(msg, wp, lp); is called at first, there is no WindowHandle, subsequently CreateWindowEx fails

    – Barmak Shemirani
    Nov 17 '18 at 6:55






  • 1





    @BarmakShemirani I wrote the code from memory, I didn't try to compile it. Wouldn't surprise me if it needs further tweaking. But it should be clear what the intent of the code is trying to demonstate. I fixed the WindowHandle issue, though.

    – Remy Lebeau
    Nov 17 '18 at 7:11








  • 1





    for top-level windows first message is WM_GETMINMAXINFO (before WM_NCCREATE), as result will be ignored. if want handle it, and any possible messages before WM_NCCREATE, too save pointer to WinForm in tls, in StaticWindowProcedure - get it from tls, assign to window via SetWindowLongPtr and change GWLP_WNDPROC to say StaticWindowProcedure2. inside this we already unconditionally got pointer to WinForm via GetWindowLongPtr - never will be 0.

    – RbMm
    Nov 17 '18 at 9:53











  • I see in order for this to work you would have to use WndClass.cbWndExtra = sizeof(WinForm*) or it will not work. Now that's an issue because when you close one window with this method every window you have will also close.

    – Joshua
    Nov 17 '18 at 21:16






  • 1





    @Joshua you don't have to use cbWndExtra, GWLP_USERDATA will work, too. All HWNDs have GWLP_USERDATA available for free, which makes it appealing for users to use. In this case, I chose to use cbWndExtra to keep the WinForm* pointer private and leave GWLP_USERDATA free for the caller to use for its own purposes. And no, closing one window does not affect other windows. Every individual WinForm HWND allocated in this code has its own distinct WinForm* pointer assigned to it. There is no linkage between multiple WinForm instances

    – Remy Lebeau
    Nov 17 '18 at 22:21





















  • I couldn't compile your code on VS2017. I made some changes but there is a main issue, in that when This->WindowProcedure(msg, wp, lp); is called at first, there is no WindowHandle, subsequently CreateWindowEx fails

    – Barmak Shemirani
    Nov 17 '18 at 6:55






  • 1





    @BarmakShemirani I wrote the code from memory, I didn't try to compile it. Wouldn't surprise me if it needs further tweaking. But it should be clear what the intent of the code is trying to demonstate. I fixed the WindowHandle issue, though.

    – Remy Lebeau
    Nov 17 '18 at 7:11








  • 1





    for top-level windows first message is WM_GETMINMAXINFO (before WM_NCCREATE), as result will be ignored. if want handle it, and any possible messages before WM_NCCREATE, too save pointer to WinForm in tls, in StaticWindowProcedure - get it from tls, assign to window via SetWindowLongPtr and change GWLP_WNDPROC to say StaticWindowProcedure2. inside this we already unconditionally got pointer to WinForm via GetWindowLongPtr - never will be 0.

    – RbMm
    Nov 17 '18 at 9:53











  • I see in order for this to work you would have to use WndClass.cbWndExtra = sizeof(WinForm*) or it will not work. Now that's an issue because when you close one window with this method every window you have will also close.

    – Joshua
    Nov 17 '18 at 21:16






  • 1





    @Joshua you don't have to use cbWndExtra, GWLP_USERDATA will work, too. All HWNDs have GWLP_USERDATA available for free, which makes it appealing for users to use. In this case, I chose to use cbWndExtra to keep the WinForm* pointer private and leave GWLP_USERDATA free for the caller to use for its own purposes. And no, closing one window does not affect other windows. Every individual WinForm HWND allocated in this code has its own distinct WinForm* pointer assigned to it. There is no linkage between multiple WinForm instances

    – Remy Lebeau
    Nov 17 '18 at 22:21



















I couldn't compile your code on VS2017. I made some changes but there is a main issue, in that when This->WindowProcedure(msg, wp, lp); is called at first, there is no WindowHandle, subsequently CreateWindowEx fails

– Barmak Shemirani
Nov 17 '18 at 6:55





I couldn't compile your code on VS2017. I made some changes but there is a main issue, in that when This->WindowProcedure(msg, wp, lp); is called at first, there is no WindowHandle, subsequently CreateWindowEx fails

– Barmak Shemirani
Nov 17 '18 at 6:55




1




1





@BarmakShemirani I wrote the code from memory, I didn't try to compile it. Wouldn't surprise me if it needs further tweaking. But it should be clear what the intent of the code is trying to demonstate. I fixed the WindowHandle issue, though.

– Remy Lebeau
Nov 17 '18 at 7:11







@BarmakShemirani I wrote the code from memory, I didn't try to compile it. Wouldn't surprise me if it needs further tweaking. But it should be clear what the intent of the code is trying to demonstate. I fixed the WindowHandle issue, though.

– Remy Lebeau
Nov 17 '18 at 7:11






1




1





for top-level windows first message is WM_GETMINMAXINFO (before WM_NCCREATE), as result will be ignored. if want handle it, and any possible messages before WM_NCCREATE, too save pointer to WinForm in tls, in StaticWindowProcedure - get it from tls, assign to window via SetWindowLongPtr and change GWLP_WNDPROC to say StaticWindowProcedure2. inside this we already unconditionally got pointer to WinForm via GetWindowLongPtr - never will be 0.

– RbMm
Nov 17 '18 at 9:53





for top-level windows first message is WM_GETMINMAXINFO (before WM_NCCREATE), as result will be ignored. if want handle it, and any possible messages before WM_NCCREATE, too save pointer to WinForm in tls, in StaticWindowProcedure - get it from tls, assign to window via SetWindowLongPtr and change GWLP_WNDPROC to say StaticWindowProcedure2. inside this we already unconditionally got pointer to WinForm via GetWindowLongPtr - never will be 0.

– RbMm
Nov 17 '18 at 9:53













I see in order for this to work you would have to use WndClass.cbWndExtra = sizeof(WinForm*) or it will not work. Now that's an issue because when you close one window with this method every window you have will also close.

– Joshua
Nov 17 '18 at 21:16





I see in order for this to work you would have to use WndClass.cbWndExtra = sizeof(WinForm*) or it will not work. Now that's an issue because when you close one window with this method every window you have will also close.

– Joshua
Nov 17 '18 at 21:16




1




1





@Joshua you don't have to use cbWndExtra, GWLP_USERDATA will work, too. All HWNDs have GWLP_USERDATA available for free, which makes it appealing for users to use. In this case, I chose to use cbWndExtra to keep the WinForm* pointer private and leave GWLP_USERDATA free for the caller to use for its own purposes. And no, closing one window does not affect other windows. Every individual WinForm HWND allocated in this code has its own distinct WinForm* pointer assigned to it. There is no linkage between multiple WinForm instances

– Remy Lebeau
Nov 17 '18 at 22:21







@Joshua you don't have to use cbWndExtra, GWLP_USERDATA will work, too. All HWNDs have GWLP_USERDATA available for free, which makes it appealing for users to use. In this case, I chose to use cbWndExtra to keep the WinForm* pointer private and leave GWLP_USERDATA free for the caller to use for its own purposes. And no, closing one window does not affect other windows. Every individual WinForm HWND allocated in this code has its own distinct WinForm* pointer assigned to it. There is no linkage between multiple WinForm instances

– Remy Lebeau
Nov 17 '18 at 22:21






















draft saved

draft discarded




















































Thanks for contributing an answer to Stack Overflow!


  • Please be sure to answer the question. Provide details and share your research!

But avoid



  • Asking for help, clarification, or responding to other answers.

  • Making statements based on opinion; back them up with references or personal experience.


To learn more, see our tips on writing great answers.




draft saved


draft discarded














StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53347631%2fc-winapi-dynamic-wrapper-class%23new-answer', 'question_page');
}
);

Post as a guest















Required, but never shown





















































Required, but never shown














Required, but never shown












Required, but never shown







Required, but never shown

































Required, but never shown














Required, but never shown












Required, but never shown







Required, but never shown







這個網誌中的熱門文章

Hercules Kyvelos

Tangent Lines Diagram Along Smooth Curve

Yusuf al-Mu'taman ibn Hud