C++ WINAPI Dynamic Wrapper Class
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
add a comment |
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
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 aBN_CLICKED
notification viaWM_COMMAND
when the button is clicked. If the parent window doesn't handle the message, it ends up going toDefWindowProc()
and gets ignored. So, the parent window needs to handleWM_COMMAND
. When the notification code isBN_CLICKED
, the parent can look up the providedHWND
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 itsC
-based roots. That means having concreteC
-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
add a comment |
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
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
c++ class winapi
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 aBN_CLICKED
notification viaWM_COMMAND
when the button is clicked. If the parent window doesn't handle the message, it ends up going toDefWindowProc()
and gets ignored. So, the parent window needs to handleWM_COMMAND
. When the notification code isBN_CLICKED
, the parent can look up the providedHWND
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 itsC
-based roots. That means having concreteC
-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
add a comment |
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 aBN_CLICKED
notification viaWM_COMMAND
when the button is clicked. If the parent window doesn't handle the message, it ends up going toDefWindowProc()
and gets ignored. So, the parent window needs to handleWM_COMMAND
. When the notification code isBN_CLICKED
, the parent can look up the providedHWND
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 itsC
-based roots. That means having concreteC
-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
add a comment |
1 Answer
1
active
oldest
votes
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
I couldn't compile your code on VS2017. I made some changes but there is a main issue, in that whenThis->WindowProcedure(msg, wp, lp);
is called at first, there is noWindowHandle
, subsequentlyCreateWindowEx
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 theWindowHandle
issue, though.
– Remy Lebeau
Nov 17 '18 at 7:11
1
for top-level windows first message isWM_GETMINMAXINFO
(beforeWM_NCCREATE
), as result will be ignored. if want handle it, and any possible messages beforeWM_NCCREATE
, too save pointer toWinForm
in tls, inStaticWindowProcedure
- get it from tls, assign to window viaSetWindowLongPtr
and changeGWLP_WNDPROC
to sayStaticWindowProcedure2
. inside this we already unconditionally got pointer toWinForm
viaGetWindowLongPtr
- 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 usecbWndExtra
,GWLP_USERDATA
will work, too. All HWNDs haveGWLP_USERDATA
available for free, which makes it appealing for users to use. In this case, I chose to usecbWndExtra
to keep theWinForm*
pointer private and leaveGWLP_USERDATA
free for the caller to use for its own purposes. And no, closing one window does not affect other windows. Every individualWinForm
HWND allocated in this code has its own distinctWinForm*
pointer assigned to it. There is no linkage between multipleWinForm
instances
– Remy Lebeau
Nov 17 '18 at 22:21
|
show 2 more comments
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
});
}
});
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
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
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
I couldn't compile your code on VS2017. I made some changes but there is a main issue, in that whenThis->WindowProcedure(msg, wp, lp);
is called at first, there is noWindowHandle
, subsequentlyCreateWindowEx
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 theWindowHandle
issue, though.
– Remy Lebeau
Nov 17 '18 at 7:11
1
for top-level windows first message isWM_GETMINMAXINFO
(beforeWM_NCCREATE
), as result will be ignored. if want handle it, and any possible messages beforeWM_NCCREATE
, too save pointer toWinForm
in tls, inStaticWindowProcedure
- get it from tls, assign to window viaSetWindowLongPtr
and changeGWLP_WNDPROC
to sayStaticWindowProcedure2
. inside this we already unconditionally got pointer toWinForm
viaGetWindowLongPtr
- 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 usecbWndExtra
,GWLP_USERDATA
will work, too. All HWNDs haveGWLP_USERDATA
available for free, which makes it appealing for users to use. In this case, I chose to usecbWndExtra
to keep theWinForm*
pointer private and leaveGWLP_USERDATA
free for the caller to use for its own purposes. And no, closing one window does not affect other windows. Every individualWinForm
HWND allocated in this code has its own distinctWinForm*
pointer assigned to it. There is no linkage between multipleWinForm
instances
– Remy Lebeau
Nov 17 '18 at 22:21
|
show 2 more comments
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
I couldn't compile your code on VS2017. I made some changes but there is a main issue, in that whenThis->WindowProcedure(msg, wp, lp);
is called at first, there is noWindowHandle
, subsequentlyCreateWindowEx
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 theWindowHandle
issue, though.
– Remy Lebeau
Nov 17 '18 at 7:11
1
for top-level windows first message isWM_GETMINMAXINFO
(beforeWM_NCCREATE
), as result will be ignored. if want handle it, and any possible messages beforeWM_NCCREATE
, too save pointer toWinForm
in tls, inStaticWindowProcedure
- get it from tls, assign to window viaSetWindowLongPtr
and changeGWLP_WNDPROC
to sayStaticWindowProcedure2
. inside this we already unconditionally got pointer toWinForm
viaGetWindowLongPtr
- 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 usecbWndExtra
,GWLP_USERDATA
will work, too. All HWNDs haveGWLP_USERDATA
available for free, which makes it appealing for users to use. In this case, I chose to usecbWndExtra
to keep theWinForm*
pointer private and leaveGWLP_USERDATA
free for the caller to use for its own purposes. And no, closing one window does not affect other windows. Every individualWinForm
HWND allocated in this code has its own distinctWinForm*
pointer assigned to it. There is no linkage between multipleWinForm
instances
– Remy Lebeau
Nov 17 '18 at 22:21
|
show 2 more comments
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
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
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 whenThis->WindowProcedure(msg, wp, lp);
is called at first, there is noWindowHandle
, subsequentlyCreateWindowEx
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 theWindowHandle
issue, though.
– Remy Lebeau
Nov 17 '18 at 7:11
1
for top-level windows first message isWM_GETMINMAXINFO
(beforeWM_NCCREATE
), as result will be ignored. if want handle it, and any possible messages beforeWM_NCCREATE
, too save pointer toWinForm
in tls, inStaticWindowProcedure
- get it from tls, assign to window viaSetWindowLongPtr
and changeGWLP_WNDPROC
to sayStaticWindowProcedure2
. inside this we already unconditionally got pointer toWinForm
viaGetWindowLongPtr
- 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 usecbWndExtra
,GWLP_USERDATA
will work, too. All HWNDs haveGWLP_USERDATA
available for free, which makes it appealing for users to use. In this case, I chose to usecbWndExtra
to keep theWinForm*
pointer private and leaveGWLP_USERDATA
free for the caller to use for its own purposes. And no, closing one window does not affect other windows. Every individualWinForm
HWND allocated in this code has its own distinctWinForm*
pointer assigned to it. There is no linkage between multipleWinForm
instances
– Remy Lebeau
Nov 17 '18 at 22:21
|
show 2 more comments
I couldn't compile your code on VS2017. I made some changes but there is a main issue, in that whenThis->WindowProcedure(msg, wp, lp);
is called at first, there is noWindowHandle
, subsequentlyCreateWindowEx
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 theWindowHandle
issue, though.
– Remy Lebeau
Nov 17 '18 at 7:11
1
for top-level windows first message isWM_GETMINMAXINFO
(beforeWM_NCCREATE
), as result will be ignored. if want handle it, and any possible messages beforeWM_NCCREATE
, too save pointer toWinForm
in tls, inStaticWindowProcedure
- get it from tls, assign to window viaSetWindowLongPtr
and changeGWLP_WNDPROC
to sayStaticWindowProcedure2
. inside this we already unconditionally got pointer toWinForm
viaGetWindowLongPtr
- 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 usecbWndExtra
,GWLP_USERDATA
will work, too. All HWNDs haveGWLP_USERDATA
available for free, which makes it appealing for users to use. In this case, I chose to usecbWndExtra
to keep theWinForm*
pointer private and leaveGWLP_USERDATA
free for the caller to use for its own purposes. And no, closing one window does not affect other windows. Every individualWinForm
HWND allocated in this code has its own distinctWinForm*
pointer assigned to it. There is no linkage between multipleWinForm
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
|
show 2 more comments
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.
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
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
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
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
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 viaWM_COMMAND
when the button is clicked. If the parent window doesn't handle the message, it ends up going toDefWindowProc()
and gets ignored. So, the parent window needs to handleWM_COMMAND
. When the notification code isBN_CLICKED
, the parent can look up the providedHWND
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 concreteC
-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