Вот версия вашего кода, которая работает в VS2017 (с использованием C ++ 17). Я добавил отладочные отпечатки, чтобы вы могли видеть ScreenProcess()
получение движений мыши и т. Д. Я также добавил класс (WindowsClassRegistrator
) для обработки одного из ресурсов, выделенных вами, чтобы показать, как можно расширить существующие структуры C до обрабатывать освобождение ресурсов автоматически.
Я добавил отображение между обычными сообщениями Windows и их именами макросов, чтобы упростить отслеживание того, что вы фактически получаете в своем WndProc
. Неизвестные сообщения Windows собираются и отображаются при нажатии кнопки «Закрыть» в приложении (на панели задач), поэтому вы можете расширять сообщения, которые вы хотите обрабатывать / отображать по ходу работы.
Я также добавил шаблоны функций утверждений для выдачи исключений с надлежащими сообщениями об ошибках, которые вы можете использовать для всех функций WinAPI, которые имеют простой способ проверки их успешности или нет.
#include "pch.h" // if you use precompiled headers
#include <Windows.h>
#include <Windowsx.h> // GET_X_LPARAM, GET_Y_LPARAM
#include <Olectl.h> // OCM_BASE
#include <iostream>
#include <iomanip>
#include <sstream>
#include <string>
#include <stdexcept>
#include <map>
#include <unordered_set>
// --- bug hunting support functions start ---
std::string GetLastErrorString() {
DWORD le = GetLastError();
LPSTR lpBuffer = nullptr;
if (FormatMessageA(
FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_FROM_SYSTEM |
FORMAT_MESSAGE_IGNORE_INSERTS,
0, le, 0,
reinterpret_cast<LPSTR>(&lpBuffer),
0, NULL
))
{
std::string rv(lpBuffer);
LocalFree(lpBuffer);
return rv;
}
else return std::to_string(le);
}
struct win_error : public std::runtime_error {
win_error(const std::string& prefix) :
std::runtime_error(prefix + ": " + GetLastErrorString())
{}
};
// assert that a function does NOT return a specific value
template<typename T>
inline T AssertNEQ(T value, const char* funcname, T got_value) {
if (value == got_value) throw win_error(funcname);
return got_value;
}
// assert that a function DOES return a specific value
template<typename T>
inline T AssertEQ(T value, const char* funcname, T got_value) {
if (value != got_value) throw win_error(funcname);
return got_value;
}
// --- bug hunting support functions end ---
class WindowsClassRegistrator : public WNDCLASSEXW {
ATOM wca;
public:
WindowsClassRegistrator(WNDPROC lpfnWndProc) :
WNDCLASSEXW{ 0 }, wca{}
{
this->cbSize = sizeof(WNDCLASSEXW);
this->style = CS_SAVEBITS | CS_DBLCLKS | CS_HREDRAW | CS_VREDRAW;
this->lpfnWndProc = lpfnWndProc;
this->hInstance =
AssertNEQ<HMODULE>(NULL, "GetModuleHandleW", GetModuleHandleW(nullptr));
this->lpszClassName = L"__TEST__";
this->hbrBackground = static_cast<HBRUSH>(GetStockObject(BLACK_BRUSH));
this->wca =
AssertNEQ<ATOM>(NULL, "RegisterClassExW", RegisterClassExW(this));
}
WindowsClassRegistrator(const WindowsClassRegistrator&) = delete;
WindowsClassRegistrator(WindowsClassRegistrator&&) = delete;
WindowsClassRegistrator& operator=(const WindowsClassRegistrator&) = delete;
WindowsClassRegistrator& operator=(WindowsClassRegistrator&&) = delete;
~WindowsClassRegistrator() {
AssertNEQ<BOOL>(FALSE,
"UnregisterClassW", UnregisterClassW(GetAtomAsStr(), this->hInstance));
}
inline LPCWSTR GetAtomAsStr() const noexcept {
return reinterpret_cast<LPCWSTR>(this->wca);
}
inline HINSTANCE GetInstance() const noexcept {
return this->hInstance;
}
inline LPCWSTR GetClassName() const noexcept {
return this->lpszClassName;
}
};
std::multimap<UINT, std::string> messages = {
{WM_NULL, "WM_NULL"},
{WM_CREATE, "WM_CREATE"},
{WM_DESTROY, "WM_DESTROY"},
{WM_MOVE, "WM_MOVE"},
{WM_SIZE, "WM_SIZE"},
{WM_ACTIVATE, "WM_ACTIVATE"},
{WM_SETFOCUS, "WM_SETFOCUS"},
{WM_KILLFOCUS, "WM_KILLFOCUS"},
{WM_PAINT, "WM_PAINT"},
{WM_CLOSE, "WM_CLOSE"},
{WM_QUIT, "WM_QUIT"},
{WM_ERASEBKGND, "WM_ERASEBKGND"},
{WM_SHOWWINDOW, "WM_SHOWWINDOW"},
{WM_ACTIVATEAPP, "WM_ACTIVATEAPP"},
{WM_CANCELMODE, "WM_CANCELMODE"},
{WM_SETCURSOR, "WM_SETCURSOR"},
{WM_MOUSEACTIVATE, "WM_MOUSEACTIVATE"},
{WM_VKEYTOITEM, "WM_VKEYTOITEM"},
{WM_CHARTOITEM, "WM_CHARTOITEM"},
{WM_WINDOWPOSCHANGING, "WM_WINDOWPOSCHANGING"},
{WM_WINDOWPOSCHANGED, "WM_WINDOWPOSCHANGED"},
{SPI_SETDRAGHEIGHT, "SPI_SETDRAGHEIGHT"},
{WM_HELP, "WM_HELP"},
{WM_CONTEXTMENU, "WM_CONTEXTMENU"},
{WM_GETICON, "WM_GETICON"},
{WM_NCCREATE, "WM_NCCREATE"},
{WM_NCDESTROY, "WM_NCDESTROY"},
{WM_NCCALCSIZE, "WM_NCCALCSIZE"},
{WM_NCHITTEST, "WM_NCHITTEST"},
{WM_NCPAINT, "WM_NCPAINT"},
{WM_NCACTIVATE, "WM_NCACTIVATE"},
{SPI_GETDOCKMOVING, "SPI_GETDOCKMOVING"},
{WM_KEYDOWN, "WM_KEYDOWN"},
{WM_KEYUP, "WM_KEYUP"},
{WM_CHAR, "WM_CHAR"},
{WM_SYSKEYDOWN, "WM_SYSKEYDOWN"},
{WM_SYSKEYUP, "WM_SYSKEYUP"},
{WM_SYSCHAR, "WM_SYSCHAR"},
{WM_SYSCOMMAND, "WM_SYSCOMMAND"},
{WM_MOUSEMOVE, "WM_MOUSEMOVE"},
{WM_LBUTTONDOWN, "WM_LBUTTONDOWN"},
{WM_LBUTTONUP, "WM_LBUTTONUP"},
{WM_LBUTTONDBLCLK, "WM_LBUTTONDBLCLK"},
{WM_RBUTTONDOWN, "WM_RBUTTONDOWN"},
{WM_RBUTTONUP, "WM_RBUTTONUP"},
{WM_RBUTTONDBLCLK, "WM_RBUTTONDBLCLK"},
{WM_MBUTTONDOWN, "WM_MBUTTONDOWN"},
{WM_MBUTTONUP, "WM_MBUTTONUP"},
{WM_MBUTTONDBLCLK, "WM_MBUTTONDBLCLK"},
{WM_MOUSEWHEEL, "WM_MOUSEWHEEL"},
{WM_XBUTTONDOWN, "WM_XBUTTONDOWN"},
{WM_XBUTTONUP, "WM_XBUTTONUP"},
{WM_IME_SETCONTEXT, "WM_IME_SETCONTEXT"},
{WM_IME_NOTIFY, "WM_IME_NOTIFY"},
{WM_HOTKEY, "WM_HOTKEY"},
{0x0313, ": https://stackoverflow.com/questions/10430377/winapi-undocumented-windows-message-0x0313-stable"},
{WM_PRINT, "WM_PRINT"},
{WM_APPCOMMAND, "WM_APPCOMMAND"},
};
std::unordered_set<UINT> unmapped_messages;
std::map<WPARAM, std::string> mk_down = {
{MK_CONTROL, "MK_CONTROL"},
{MK_LBUTTON,"MK_LBUTTON"},
{MK_MBUTTON,"MK_MBUTTON"},
{MK_RBUTTON,"MK_RBUTTON"},
{MK_SHIFT,"MK_SHIFT"},
{MK_XBUTTON1,"MK_XBUTTON1"},
{MK_XBUTTON2,"MK_XBUTTON2"}
};
constexpr int colw = 40;
std::string message_maker(const char* macro, UINT uMsg, UINT offset) {
std::stringstream ss;
ss << macro << " + " << std::hex << (uMsg - offset) << " (" << uMsg << ")";
return ss.str();
}
inline void DisplayMsg(UINT uMsg, WPARAM wParam, LPARAM lParam) {
std::string message;
if (uMsg < WM_USER) {
// there may be duplicate macros for some messages, so show all of them
auto[rangefirst, rangelast] = messages.equal_range(uMsg);
if (rangefirst == rangelast) {
// unmapped message found, store it
unmapped_messages.emplace(uMsg);
rangefirst = messages.emplace(uMsg, ": " + std::to_string(uMsg) + " -- UNMAPPED MESSAGE");
rangelast = rangefirst;
++rangelast;
}
message = rangefirst->second;
while (++rangefirst != rangelast) message += " " + rangefirst->second;
}
else {
// https://docs.microsoft.com/en-us/windows/desktop/winmsg/ocm--base
#define REGISTERED_WINDOWS_MESSAGE_BASE (0xC000)
#define SYSRESERVED_BASE (0x10000)
if (uMsg < OCM__BASE)
message = message_maker("WM_USER", uMsg, WM_USER);
else if (uMsg < WM_APP)
message = message_maker("(WM_USER) OCM__BASE", uMsg, OCM__BASE);
else if (uMsg < REGISTERED_WINDOWS_MESSAGE_BASE)
message = message_maker("WM_APP", uMsg, WM_APP);
else if (uMsg < SYSRESERVED_BASE)
message = message_maker("Registered Window Message", uMsg, REGISTERED_WINDOWS_MESSAGE_BASE);
else
message = message_maker("Reserved by the system", uMsg, SYSRESERVED_BASE);
}
std::cout << std::setw(colw) << std::hex << message << std::setw(18) << wParam
<< std::setw(12) << lParam << "\n";
}
LRESULT CALLBACK ScreenProcess(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
static std::string old_mouse_message;
switch (uMsg) {
case WM_MOUSEMOVE:
{
std::stringstream ss;
std::string new_mouse_message;
int xPos, yPos;
xPos = GET_X_LPARAM(lParam);
yPos = GET_Y_LPARAM(lParam);
ss << std::setw(colw) << "WM_MOUSEMOVE" << std::dec
<< " x=" << std::setw(6) << xPos << " y=" << std::setw(6) << yPos;
for (auto&[wp, key] : mk_down)
if (wp&wParam) ss << " " << key;
new_mouse_message = ss.str();
if (new_mouse_message != old_mouse_message) {
std::cout << new_mouse_message << "\n";
old_mouse_message = std::move(new_mouse_message);
}
}
return 0;
case WM_NCHITTEST:
return HTCLIENT;
case WM_SETCURSOR:
return TRUE;
case WM_DESTROY:
std::cout << std::setw(colw) << "WM_DESTROY" << " ";
PostQuitMessage(0);
std::cout << "PostQuitMessage() done\n";
return 0;
default:
DisplayMsg(uMsg, wParam, lParam);
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
}
int main() {
try {
WindowsClassRegistrator wcr(ScreenProcess);
// use WS_VISIBLE so that you don't have to call ShowWindow()
HWND hWnd =
AssertNEQ<HWND>(NULL, "CreateWindowExW",
CreateWindowExW(
WS_EX_APPWINDOW,
wcr.GetAtomAsStr(),
L"Title string",
WS_POPUP | WS_VISIBLE | WS_CLIPCHILDREN | WS_CLIPSIBLINGS,
CW_USEDEFAULT, // x
CW_USEDEFAULT, // y
0, // width
0, // height
nullptr,
nullptr,
wcr.GetInstance(),
nullptr
)
);
MONITORINFO mi = { sizeof(mi) }; // mi.cbSize = sizeof(mi);
AssertNEQ<BOOL>(FALSE, "GetMonitorInfo",
GetMonitorInfo(MonitorFromWindow(hWnd, MONITOR_DEFAULTTOPRIMARY), &mi)
);
AssertNEQ<BOOL>(FALSE, "SetWindowPos",
SetWindowPos(hWnd, HWND_TOP,
mi.rcMonitor.left, mi.rcMonitor.top,
(mi.rcMonitor.right - mi.rcMonitor.left) / 4, // 1/4 of the screen width
(mi.rcMonitor.bottom - mi.rcMonitor.top), // height
SWP_NOOWNERZORDER | SWP_FRAMECHANGED)
);
// paint a rectangle in the window
AssertNEQ<BOOL>(FALSE, "Rectangle", Rectangle(
AssertNEQ<HDC>(NULL, "GetDC", GetDC(hWnd)),
10, 10, 100, 100)
);
MSG uMsg;
while (AssertNEQ<BOOL>(-1, "GetMessage", GetMessage(&uMsg, nullptr, 0, 0))) {
TranslateMessage(&uMsg); // assertion would depend on message type
DispatchMessage(&uMsg); // assertion would depend on message type
}
DisplayMsg(uMsg.message, uMsg.wParam, uMsg.lParam); // WM_QUIT
if (unmapped_messages.size()) {
std::cout << "\nYou have collected unmapped messages: \n";
for (const auto&[msg, text] : messages) {
std::cout << "/* 0x" << std::setw(4) << std::setfill('0') << msg << " */ ";
if (unmapped_messages.count(msg) || text[0] == ':') {
std::cout << "{0x" << std::setw(4) << std::setfill('0') << msg;
}
else {
std::cout << "{" << text;
}
std::cout << ", \"" << text << "\"},\n";
}
}
return static_cast<int>(uMsg.wParam);
}
catch (const std::exception& ex) {
std::cerr << "Exception: " << ex.what() << "\n";
}
return 1;
} // the registered windows class will be unregistered here, when wcr goes out of
// scope