C++ Code:
#define _UNICODE
#define UNICODE
#include <tchar.h>
#include <Windows.h>
#include <strsafe.h>
LRESULT CALLBACK WndProc(
_In_ HWND hWnd,
_In_ UINT message,
_In_ WPARAM wParam,
_In_ LPARAM lParam
);
void ErrorExit(
LPTSTR lpszFunction
);
HINSTANCE hInst;
WCHAR szTitle[] = TEXT("Dummy");
WCHAR szMenuName[] = TEXT("Dummy");
WCHAR szWindowClass[] = TEXT("Dummy");
int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
LPWSTR *szArgList;
int narg;
WNDCLASSEXW wcex;
HWND hWnd;
HACCEL hAccelTable;
MSG msg;
FILE *fConsole;
UNREFERENCED_PARAMETER(hPrevInstance);
szArgList = CommandLineToArgvW(GetCommandLineW(), &narg);
if (!szArgList) {
ErrorExit(L"CommandLineToArgvW");
}
// TODO: Add custom icon or use default Visual Studio icon.
wcex.cbSize = sizeof(WNDCLASSEX);
wcex.style = CS_HREDRAW | CS_VREDRAW;;
wcex.lpfnWndProc = WndProc;
wcex.cbClsExtra = 0;
wcex.cbWndExtra = 0;
wcex.hInstance = hInstance;
wcex.hIcon = LoadIcon(hInstance, IDI_APPLICATION);
wcex.hCursor = LoadCursor(nullptr, IDC_ARROW);
if (narg > 1)
wcex.hbrBackground = CreateSolidBrush(_wtoi(szArgList[1]));
else
wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
wcex.lpszMenuName = szMenuName;
wcex.lpszClassName = szWindowClass;
wcex.hIconSm = LoadIcon(wcex.hInstance, IDI_APPLICATION);
if (!RegisterClassEx(&wcex)) {
ErrorExit(TEXT("RegisterClassExW"));
}
hWnd = CreateWindowEx(WS_EX_CLIENTEDGE, szWindowClass, szTitle, WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, nullptr, nullptr, hInstance, nullptr);
if (!hWnd) {
ErrorExit(TEXT("CreateWindowExW"));
}
LONG style = GetWindowLong(hWnd, GWL_STYLE);
if (!SetWindowLong(hWnd, GWL_STYLE, (style & ~WS_OVERLAPPEDWINDOW) | WS_BORDER | WS_THICKFRAME)) {
ErrorExit(TEXT("SetWindowLong"));
}
style = GetWindowLong(hWnd, GWL_EXSTYLE);
if (!SetWindowLong(hWnd, GWL_EXSTYLE, style & ~WS_EX_CLIENTEDGE)) {
ErrorExit(TEXT("SetWindowLong"));
}
// Necessary per API reference for SetWindowLong. If left out,
// there will be a persistent rendering artifact after the first
// window move if the window has not been resized.
if (!SetWindowPos(hWnd, HWND_TOP, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_FRAMECHANGED)) {
ErrorExit(TEXT("SetWindowPos"));
}
ShowWindow(hWnd, nCmdShow);
if (!UpdateWindow(hWnd)) {
ErrorExit(TEXT("UpdateWindow"));
}
hAccelTable = LoadAccelerators(hInstance, 0);
while (GetMessage(&msg, nullptr, 0, 0)) {
if (!TranslateAccelerator(hWnd, nullptr, &msg)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
return (int)msg.wParam;
}
LRESULT CALLBACK
WndProc(
_In_ HWND hWnd,
_In_ UINT message,
_In_ WPARAM wParam,
_In_ LPARAM lParam
)
{
switch (message) {
case WM_DESTROY: {
PostQuitMessage(0);
break;
}
default: {
return DefWindowProc(hWnd, message, wParam, lParam);
}
}
return 0;
}
void
ErrorExit(
LPTSTR lpszFunction
)
{
// Retrieve the system error message for the last error code.
LPVOID lpMsgBuf;
LPVOID lpDisplayBuf;
DWORD lerr = GetLastError();
DWORD size = FormatMessage(
FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_FROM_SYSTEM |
FORMAT_MESSAGE_IGNORE_INSERTS,
NULL,
lerr,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
(LPTSTR)&lpMsgBuf,
0, NULL);
// Print the error message to the console.
//wprintf(TEXT("%s: %s"), lpszFunction, (wcharTEXT *)lpMsgBuf);
// Display the error message in a message box.
lpDisplayBuf = (LPVOID)LocalAlloc(LMEM_ZEROINIT,
(lstrlen((LPCTSTR)lpMsgBuf) + lstrlen((LPCTSTR)lpszFunction) + 40) * sizeof(TCHAR));
StringCchPrintf((LPTSTR)lpDisplayBuf,
LocalSize(lpDisplayBuf) / sizeof(TCHAR),
TEXT("%s failed with error %d: %s"),
lpszFunction, lerr, lpMsgBuf);
MessageBox(NULL, (LPCTSTR)lpDisplayBuf, TEXT("Error"), MB_OK);
LocalFree(lpMsgBuf);
ExitProcess(lerr);
}
C++ Code:
#include <iostream>
#include <windows.h>
#include <functional>
#include <vector>
#include <cstdint>
#include <tuple>
#include <algorithm>
/**
Utilities
**/
#ifndef UNREFERENCED_PARAMETER
#define UNREFERENCED_PARAMETER(P) {(P) = (P);}
#endif // UNREFERENCED_PARAMETER
enum class WindowStyle : std::uint32_t
{
NONE = 0L,
BORDER = WS_BORDER,
CHILD = WS_CHILD,
CLIPCHILDREN = WS_CLIPCHILDREN,
CLIPSIBLINGS = WS_CLIPSIBLINGS,
GROUP = WS_GROUP,
CAPTION = WS_CAPTION,
DISABLED = WS_DISABLED,
DLGFRAME = WS_DLGFRAME,
HSCROLL = WS_HSCROLL,
VSCROLL = WS_VSCROLL,
MINIMIZED = WS_MINIMIZE,
MAXIMIZED = WS_MAXIMIZE,
MAXIMIZE_BOX = WS_MAXIMIZEBOX,
MINIMIZE_BOX = WS_MINIMIZEBOX,
OVERLAPPED = WS_OVERLAPPED,
OVERLAPPED_WINDOW = WS_OVERLAPPEDWINDOW,
POPUP = WS_POPUP,
POPUP_WINDOW = WS_POPUPWINDOW,
THICK_FRAME = WS_THICKFRAME,
SYSMENU = WS_SYSMENU,
TABSTOP = WS_TABSTOP,
EX_ACCEPTFILES = WS_EX_ACCEPTFILES,
EX_APPWINDOW = WS_EX_APPWINDOW,
EX_CLIENTEDGE = WS_EX_CLIENTEDGE,
EX_COMPOSITED = WS_EX_COMPOSITED,
EX_CONTEXTHELP = WS_EX_CONTEXTHELP,
EX_CONTROLPARENT = WS_EX_CONTROLPARENT,
EX_DLGMODALFRAME = WS_EX_DLGMODALFRAME,
EX_LAYERED = WS_EX_LAYERED,
EX_LAYOUTRTL = WS_EX_LAYOUTRTL,
EX_LEFT = WS_EX_LEFT,
EX_LEFTSCROLLBAR = WS_EX_LEFTSCROLLBAR,
EX_LTRREADING = WS_EX_LTRREADING,
EX_MDICHILD = WS_EX_MDICHILD,
EX_NOACTIVATE = WS_EX_NOACTIVATE,
EX_NOINHERITLAYOUT = WS_EX_NOINHERITLAYOUT,
EX_NOPARENTNOTIFY = WS_EX_NOPARENTNOTIFY,
EX_OVERLAPPEDWINDOW = WS_EX_OVERLAPPEDWINDOW,
EX_PALETTEWINDOW = WS_EX_PALETTEWINDOW,
EX_RIGHT = WS_EX_RIGHT,
EX_RIGHTSCROLLBAR = WS_EX_RIGHTSCROLLBAR,
EX_RTLREADING = WS_EX_RTLREADING,
EX_STATICEDGE = WS_EX_STATICEDGE,
EX_TOOLWINDOW = WS_EX_TOOLWINDOW,
EX_TOPMOST = WS_EX_TOPMOST,
EX_TRANSPARENT = WS_EX_TRANSPARENT,
EX_WINDOWEDGE = WS_EX_WINDOWEDGE
};
inline WindowStyle operator ~ (WindowStyle a){return static_cast<WindowStyle>(~static_cast<std::uint32_t>(a));}
inline WindowStyle operator | (WindowStyle a, WindowStyle b) {return static_cast<WindowStyle>(static_cast<std::uint32_t>(a) | static_cast<std::uint32_t>(b));}
inline WindowStyle operator & (WindowStyle a, WindowStyle b) {return static_cast<WindowStyle>(static_cast<std::uint32_t>(a) & static_cast<std::uint32_t>(b));}
void TrackMouse(HWND hwnd)
{
TRACKMOUSEEVENT tme;
tme.cbSize = sizeof(TRACKMOUSEEVENT);
tme.dwFlags = TME_HOVER | TME_LEAVE;
tme.dwHoverTime = 1;
tme.hwndTrack = hwnd;
TrackMouseEvent(&tme);
}
void SetWindowTransparency(HWND hwnd, std::uint8_t Transperancy)
{
long wAttr = GetWindowLong(hwnd, GWL_EXSTYLE);
SetWindowLong(hwnd, GWL_EXSTYLE, wAttr | WS_EX_LAYERED);
SetLayeredWindowAttributes(hwnd, 0, Transperancy, 2);
}
void EnableVisualStyles()
{
wchar_t sys_dir[MAX_PATH] = {0};
std::uint32_t len = GetSystemDirectoryW(sys_dir, sizeof(sys_dir) / sizeof(sys_dir[0]));
if (len < sizeof(sys_dir) / sizeof(sys_dir[0]))
{
ACTCTXW actCtx =
{
sizeof(ACTCTX), ACTCTX_FLAG_RESOURCE_NAME_VALID | ACTCTX_FLAG_SET_PROCESS_DEFAULT |
ACTCTX_FLAG_ASSEMBLY_DIRECTORY_VALID, L"shell32.dll", 0, 0, sys_dir, reinterpret_cast<wchar_t*>(0x7C)
};
ULONG_PTR ulpActivationCookie = false;
ActivateActCtx(CreateActCtxW(&actCtx), &ulpActivationCookie);
}
}
void SetWindowStyle(HWND hwnd, WindowStyle style, bool remove_style = false)
{
LONG_PTR current_style = GetWindowLongPtr(hwnd, GWL_STYLE);
SetWindowLongPtrW(hwnd, GWL_STYLE, remove_style ? (current_style & ~static_cast<std::uint32_t>(style)) : (current_style | static_cast<std::uint32_t>(style)));
SetWindowPos(hwnd, nullptr, 0, 0, 0, 0, SWP_FRAMECHANGED | SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_NOOWNERZORDER);
}
void SetExtendedWindowStyle(HWND hwnd, WindowStyle style, bool remove_style = false)
{
LONG_PTR current_style = GetWindowLongPtr(hwnd, GWL_EXSTYLE);
SetWindowLongPtrW(hwnd, GWL_EXSTYLE, remove_style ? (current_style & ~static_cast<std::uint32_t>(style)) : (current_style | static_cast<std::uint32_t>(style)));
SetWindowPos(hwnd, nullptr, 0, 0, 0, 0, SWP_FRAMECHANGED | SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_NOOWNERZORDER);
}
/**
Class for storing events and invoking them..
**/
template<typename T>
class EventManager;
template<typename R, typename... Args>
class EventManager<R(Args...)>
{
private:
std::vector<std::tuple<std::uint64_t, std::uint64_t, std::function<R(Args...)>>> Events;
public:
std::uint64_t Subscribe(std::uint64_t Notification, std::function<R(Args...)>&& Func)
{
static std::uint64_t ID = 0;
Events.emplace_back(std::make_tuple(!Notification ? 0 : ++ID, Notification, std::forward<std::function<R(Args...)>>(Func)));
return ID;
}
void UnSubscribe(std::uint64_t EventID)
{
auto it = std::find_if(Events.begin(), Events.end(), [&](typename decltype(Events)::value_type &it) {
return std::get<0>(it) == EventID;
});
if (it != Events.end())
{
Events.erase(it);
}
}
bool Notify(std::uint64_t Notification, Args... args)
{
auto it = std::find_if(Events.begin(), Events.end(), [&](typename decltype(Events)::value_type &it) {
return std::get<1>(it) == Notification;
});
if (it != Events.end())
{
std::get<2>(*it)(args...);
return true;
}
return false;
}
bool NotifyAll(std::uint64_t Notification, Args... args)
{
if (Notify(Notification, args...))
{
return true;
}
auto it = std::find_if(Events.begin(), Events.end(), [&](typename decltype(Events)::value_type &it) {
return !std::get<1>(it);
});
if (it != Events.end())
{
std::get<2>(*it)(args...);
return true;
}
return false;
}
};
/**
Class for creating Windows.
**/
class Form
{
public:
Form(const wchar_t* Title, POINT Location, std::uint16_t Width, std::uint16_t Height, Form* Parent = nullptr);
~Form() {}
Form(Form&& form) = delete;
Form(const Form& form) = delete;
Form& operator = (const Form& form) = delete;
std::uint64_t AddListener(std::uint64_t Notification, std::function<void(UINT, WPARAM, LPARAM)>&& listener);
void RemoveListener(std::uint64_t ID);
void Center();
int MessageLoop();
void Close() {PostMessage(WindowHandle, WM_CLOSE, 0, 0);}
void Dispose() {DestroyWindow(WindowHandle);}
operator HWND() { return WindowHandle; }
private:
bool MouseTracking = false;
HWND WindowHandle = nullptr;
EventManager<void(UINT, WPARAM, LPARAM)> Events;
LRESULT __stdcall HandleMessages(HWND Hwnd, UINT Msg, WPARAM wParam, LPARAM lParam);
static LRESULT __stdcall WindowProcedure(HWND Hwnd, UINT Msg, WPARAM wParam, LPARAM lParam);
};
std::uint64_t Form::AddListener(std::uint64_t Notification, std::function<void(UINT, WPARAM, LPARAM)>&& listener)
{
return Events.Subscribe(Notification, std::forward<std::function<void(UINT, WPARAM, LPARAM)>>(listener));
}
void Form::RemoveListener(std::uint64_t Notification)
{
Events.UnSubscribe(Notification);
}
void Form::Center()
{
RECT rect = {0};
RECT frame = {0};
GetClientRect(GetDesktopWindow(), &rect);
GetWindowRect(this->WindowHandle, &frame);
int centerX = ((rect.right - rect.left) / 2) - ((frame.right - frame.left) / 2);
int centerY = ((rect.bottom - rect.top) /2 ) - ((frame.bottom - frame.top) / 2);
SetWindowPos(this->WindowHandle, nullptr, centerX, centerY, 0, 0, SWP_NOSIZE | SWP_NOZORDER);
}
int Form::MessageLoop()
{
MSG Messages = {0};
ShowWindow(WindowHandle, SW_SHOW);
while(GetMessageW(&Messages, nullptr, 0, 0))
{
TranslateMessage(&Messages);
DispatchMessageW(&Messages);
}
return Messages.wParam;
}
Form::Form(const wchar_t* Title, POINT Location, std::uint16_t Width, std::uint16_t Height, Form* Parent) : WindowHandle(nullptr)
{
WNDCLASSEXW WndClass =
{
sizeof(WNDCLASSEXW), CS_DBLCLKS, WindowProcedure,
0, 0, GetModuleHandleW(nullptr), LoadIconW(nullptr, reinterpret_cast<wchar_t*>(IDI_APPLICATION)),
LoadCursorW(nullptr, reinterpret_cast<wchar_t*>(IDC_ARROW)), HBRUSH(COLOR_BACKGROUND),
nullptr, L"WIN_FORM_CLS", LoadIconW(nullptr, reinterpret_cast<wchar_t*>(IDI_APPLICATION))
};
RegisterClassExW(&WndClass);
WindowHandle = CreateWindowExW(0, L"WIN_FORM_CLS", Title, WS_OVERLAPPEDWINDOW, Location.x, Location.y, Width, Height, Parent ? Parent->WindowHandle : nullptr, NULL, GetModuleHandleW(nullptr), this);
}
LRESULT __stdcall Form::HandleMessages(HWND Hwnd, UINT Msg, WPARAM wParam, LPARAM lParam)
{
switch (Msg)
{
case WM_MOUSEMOVE:
{
if (!MouseTracking)
{
TrackMouse(Hwnd);
MouseTracking = true;
}
Events.Notify(WM_MOUSEMOVE, Msg, wParam, lParam);
}
break;
case WM_MOUSELEAVE:
{
MouseTracking = false;
Events.Notify(WM_MOUSELEAVE, Msg, wParam, lParam);
}
break;
case WM_MOUSEHOVER:
{
Events.Notify(WM_MOUSEHOVER, Msg, wParam, lParam);
}
break;
case WM_CLOSE:
{
if (!Events.Notify(WM_CLOSE, Msg, wParam, lParam))
{
return DefWindowProcW(Hwnd, Msg, wParam, lParam);
}
Events.NotifyAll(Msg, Msg, wParam, lParam);
}
break;
case WM_DESTROY:
{
Events.Notify(WM_DESTROY, Msg, wParam, lParam);
PostQuitMessage(0);
}
return 0;
default:
{
Events.NotifyAll(Msg, Msg, wParam, lParam);
}
return DefWindowProcW(Hwnd, Msg, wParam, lParam);
}
return true;
}
LRESULT __stdcall Form::WindowProcedure(HWND Hwnd, UINT Msg, WPARAM wParam, LPARAM lParam)
{
Form* frm = nullptr;
switch(Msg)
{
case WM_NCCREATE:
{
CREATESTRUCTW* ptr = reinterpret_cast<CREATESTRUCTW*>(lParam);
frm = reinterpret_cast<Form*>(ptr->lpCreateParams);
SetWindowLongPtrW(Hwnd, GWLP_USERDATA, reinterpret_cast<LONG_PTR>(frm));
frm->WindowHandle = Hwnd;
break;
}
case WM_DESTROY:
{
PostQuitMessage(0);
}
return 0;
default:
{
frm = reinterpret_cast<Form*>(GetWindowLongPtrW(Hwnd, GWLP_USERDATA));
break;
}
}
return frm ? frm->HandleMessages(Hwnd, Msg, wParam, lParam) : DefWindowProcW(Hwnd, Msg, wParam, lParam);
}
int __stdcall WinMain(HINSTANCE hThisInstance, HINSTANCE hPrevInstance, LPSTR lpszArgument, int nCmdShow)
{
UNREFERENCED_PARAMETER(hThisInstance);
UNREFERENCED_PARAMETER(hPrevInstance);
UNREFERENCED_PARAMETER(lpszArgument);
UNREFERENCED_PARAMETER(nCmdShow);
EnableVisualStyles();
Form Win(L"Window", {CW_USEDEFAULT, CW_USEDEFAULT}, 855, 470);
SetWindowStyle(Win, WindowStyle::THICK_FRAME);
SetWindowStyle(Win, WindowStyle::OVERLAPPED_WINDOW, true);
SetExtendedWindowStyle(Win, WindowStyle::EX_CLIENTEDGE);
Win.AddListener(WM_KEYDOWN, [&](UINT msg, WPARAM wp, LPARAM lp)
{
UNREFERENCED_PARAMETER(msg);
UNREFERENCED_PARAMETER(wp);
UNREFERENCED_PARAMETER(lp);
if (wp == VK_ESCAPE)
{
Win.Close();
}
});
Win.AddListener(WM_CLOSE, [&](UINT msg, WPARAM wp, LPARAM lp)
{
UNREFERENCED_PARAMETER(msg);
UNREFERENCED_PARAMETER(wp);
UNREFERENCED_PARAMETER(lp);
Win.Dispose();
});
return Win.MessageLoop();
}