I am trying to create a simple window but it does not work for some reason. I have actually tried this for the past few months. Even used some old code and it does not run at all for whatever reason.
Actually, the registerclass function does not return anything ever but this happened in the past too but I was still able to create a window. Nothing works this time
I tried:
Providing a valid value for all of the members of WNDCLASS
Running Debug/Release
Running as administrator
Using GetLastError (always returns 0)
Providing different class names, because I know some don't work for some reason, and also window names
This is my code:
WNDCLASSEXW lpClass = WNDCLASSEXW{ 0x00 };
lpClass.cbSize = sizeof(decltype(lpClass));
lpClass.style = (CS_HREDRAW | CS_VREDRAW);
lpClass.lpfnWndProc = ScreenProcess;
lpClass.hInstance = GetModuleHandleW(nullptr);
lpClass.lpszClassName = L"__TEST__";
lpClass.hbrBackground = CreateSolidBrush(RGB(0, 0, 0));
RegisterClassExW(&lpClass);
if (HWND hwnd = CreateWindowExW(WS_EX_APPWINDOW, lpClass.lpszClassName, L"abc", WS_POPUP,
CW_USEDEFAULT, CW_USEDEFAULT, 500, 500, nullptr, nullptr, lpClass.hInstance, nullptr))
{
ShowWindow(hwnd, SW_SHOW);
UpdateWindow(hwnd);
}
printf("%d", GetLastError());
for (;;) {};
Did you define your window procedure as something like this?
LRESULT CALLBACK ScreenProcess(
HWND hWnd,
UINT msg,
WPARAM wParam,
LPARAM lParam
)
{
switch (msg)
{
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hWnd, msg, wParam, lParam);
}
return 0;
}
And instead of the infinite loop, place this:
MSG msg{0};
while(GetMessage(&msg, nullptr, 0, 0)){
TranslateMessage(&msg);
DispatchMessage(&msg);
}
Also, the cbSize of the WNDCLASSEXW should be sizeof(WNDCLASSEXW). Also change the CreateSolidBrush(RGB(0, 0, 0)); to GetStockObject(BLACK_BRUSH);
Hope this helps!
Here's a version of your code that's working in VS2017 (using C++17). I've added debug prints so that you can see ScreenProcess() receiving mouse movements etc. I've also added a class (WindowsClassRegistrator) for handling one of the resources you allocate to show how you can extend the existing C structs to handle releasing of resources automatically.
I added a mapping between common Windows messages and their macro names to make it easier to follow what you actually get into your WndProc. Unknown Windows messages are collected and displayed when you click close on the app (on the task bar) so you can extend the messages you'd like to handle/display as you go.
I also added assertion function templates for throwing exceptions with proper errors messages that you can use around all WinAPI functions that have an easy way of checking if they've succeeded or not.
#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://learn.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
Here's a modified, single source file version of the default Win32 project template that Visual Studio generates for you. I've just stripped out the stuff related to resources, accelerators, menus, and the About box thing.
#include <windows.h>
#define MAX_LOADSTRING 100
// Global Variables:
HINSTANCE hInst; // current instance
WCHAR szTitle[] = L"Test App"; // The title bar text
WCHAR szWindowClass[] = L"__TestAppWindowClass__"; // the main window class name
// Forward declarations of functions included in this code module:
ATOM MyRegisterClass(HINSTANCE hInstance);
BOOL InitInstance(HINSTANCE, int);
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
INT_PTR CALLBACK About(HWND, UINT, WPARAM, LPARAM);
int APIENTRY wWinMain(_In_ HINSTANCE hInstance,
_In_opt_ HINSTANCE hPrevInstance,
_In_ LPWSTR lpCmdLine,
_In_ int nCmdShow)
{
UNREFERENCED_PARAMETER(hPrevInstance);
UNREFERENCED_PARAMETER(lpCmdLine);
// TODO: Place code here.
// Initialize global strings
MyRegisterClass(hInstance);
// Perform application initialization:
if (!InitInstance(hInstance, nCmdShow))
{
return FALSE;
}
MSG msg;
// Main message loop:
while (GetMessage(&msg, nullptr, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return (int)msg.wParam;
}
//
// FUNCTION: MyRegisterClass()
//
// PURPOSE: Registers the window class.
//
ATOM MyRegisterClass(HINSTANCE hInstance)
{
WNDCLASSEXW wcex = { 0 };
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(NULL, IDI_APPLICATION);
wcex.hCursor = LoadCursor(nullptr, IDC_ARROW);
wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
wcex.lpszClassName = szWindowClass;
return RegisterClassExW(&wcex);
}
//
// FUNCTION: InitInstance(HINSTANCE, int)
//
// PURPOSE: Saves instance handle and creates main window
//
// COMMENTS:
//
// In this function, we save the instance handle in a global variable and
// create and display the main program window.
//
BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
{
hInst = hInstance; // Store instance handle in our global variable
HWND hWnd = CreateWindowW(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, nullptr, nullptr, hInstance, nullptr);
if (!hWnd)
{
return FALSE;
}
ShowWindow(hWnd, nCmdShow);
UpdateWindow(hWnd);
return TRUE;
}
//
// FUNCTION: WndProc(HWND, UINT, WPARAM, LPARAM)
//
// PURPOSE: Processes messages for the main window.
//
// WM_COMMAND - process the application menu
// WM_PAINT - Paint the main window
// WM_DESTROY - post a quit message and return
//
//
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_PAINT:
{
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hWnd, &ps);
// TODO: Add any drawing code that uses hdc here...
EndPaint(hWnd, &ps);
}
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}
I'm writing a simple keylogger/mouselogger in C/C++ for Windows. To do that, I use the Win32 functions LowLevelMouseProc and LowLevelKeyboardProc.
If relevant, here is a GitHub gist with my code, which is ultra-elementary: define the event callback and register it along with a callback for SIGINT. I'll add a summarized version at the end of the question.
My question is the following: in order to minimize overhead, how should I save these events to disk?
Answers in both C or C++ are welcome.
Is it a good practice to simply write to a buffered file each time I get a new event and let the file handle flushing when the buffer is full? I heard about non-blocking I/O but microsoft's doc says that there is an additional overhead. And finally, I'm not sure wether I should create a second thread for this.
I'd like to use some sort of buffering to avoid many little disk I/O. Ideally I would write to disk once before my process is killed. But I have no idea how to achieve this.
CODE:
#include "pch.h"
#include <stdio.h>
#include <Windows.h>
HHOOK handle;
LRESULT CALLBACK lowLevelMouseProc(
_In_ int nCode,
_In_ WPARAM wParam,
_In_ LPARAM lParam
)
{
MSLLHOOKSTRUCT* lp = (MSLLHOOKSTRUCT*)lParam;
if (wParam == WM_MOUSEMOVE) {
// Best way to save pt.x and pt.y to disk?
printf("%d %d \n", lp->pt.x, lp->pt.y);
}
return CallNextHookEx(0, nCode, wParam, lParam);
}
int main()
{
handle = SetWindowsHookExA(WH_MOUSE_LL, &lowLevelMouseProc, NULL, 0);
MSG msg;
while (GetMessage(&msg, NULL, 0, 0));
UnhookWindowsHookEx(handle)
return 0;
}
Use 2 buffers. One for writing, one for reading (flushing to disk). Once some condition is met (buffer full, program shutdown, ...), swap the buffers and start flushing to disk in a seperate thread.
This might look something like:
#include <Windows.h>
#include <vector>
#include <thread>
#include <fstream>
#include <atomic>
struct Point
{
long x, y;
};
class Buffer
{
public:
Buffer(std::string _file = "log.txt", const size_t _buffer_size = 100000) : buffer_size(_buffer_size), file(_file)
{
points1.reserve(_buffer_size);
points2.reserve(_buffer_size);
}
void write(Point p)
{
buf->push_back(p);
if (buf->size() >= buffer_size && !thread_running.load())
to_disk();
}
private:
const size_t buffer_size;
const std::string file;
std::atomic<bool> thread_running{ false };
std::vector<Point> points1, points2;
std::vector<Point> *buf = &points1, *other = &points2;
void swap_buffer()
{
std::swap(buf, other);
}
void to_disk()
{
swap_buffer();
auto tmp_buf = other;
auto tmp_file = file;
auto tmp_flag = &thread_running;
auto fn = [tmp_buf, tmp_file, tmp_flag]() {
tmp_flag->store(true);
std::fstream f(tmp_file, std::ios::app);
for (auto &v : *tmp_buf)
f << v.x << ' ' << v.y << '\n';
tmp_buf->clear();
tmp_flag->store(false);
};
std::thread t(fn);
t.detach();
}
};
Buffer buffer("log.txt");
HHOOK handle;
LRESULT CALLBACK lowLevelMouseProc(
_In_ int nCode,
_In_ WPARAM wParam,
_In_ LPARAM lParam
)
{
MSLLHOOKSTRUCT* lp = (MSLLHOOKSTRUCT*)lParam;
if (wParam == WM_MOUSEMOVE) {
buffer.write({ lp->pt.x, lp->pt.y });
}
return CallNextHookEx(0, nCode, wParam, lParam);
}
int main()
{
handle = SetWindowsHookExA(WH_MOUSE_LL, &lowLevelMouseProc, NULL, 0);
MSG msg;
while (GetMessage(&msg, NULL, 0, 0));
UnhookWindowsHookEx(handle);
return 0;
}
In this case, the buffer gets written to disk when a certain size limit is reached. This could be further optimized, by not checking the size on every write for example.
Note: In this example, error handling is omitted and the lifetime of the internal buffers should be managed accordingly.
I have two classes defined as:
class Control
{
private:
std::vector<int> Info;
public:
Control(..);
virtual ~Control();
LRESULT __stdcall SubClass(HWND Window, UINT Msg, WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData);
};
class Button: public Control
{
//...
};
Control::Control(..)
{
SetWindowSubclass(..., SubClass, ...); //Need to pass member function as callback..
}
LRESULT __stdcall Control::SubClass(HWND Window, UINT Msg, WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData)
{
//Use "Info" data member in here.. thus I cannot have static callback in order to use it.
}
And so I came up with:
class Control
{
private:
std::vector<int> Info;
static LRESULT __stdcall SetCallback(void* ThisPtr) {return static_cast<Control*>(ThisPtr)->SubClass;};
public:
//all the same stuff..
};
Control::Control(..)
{
SetWindowSubclass(..., SetCallback, ...);
}
But the above throws a whole bunch of errors. Is there anyway to either have my static callback access other datamembers OR have my callback non-static?
I do not want to have to do something like the following for every instance created (which I've seen as suggestions all over the internet):
Control F;
F.SetCallback(&F::SubClass, &F); //Externally sets member as callback which I don't want.
I'm trying to keep everything in the constructor or the class itself.
This is probably the most common question when it comes to Win32 API UI programming. See:
http://msdn.microsoft.com/en-us/library/windows/desktop/ff381400(v=vs.85).aspx
Basically the trick is to call SetWindowLongPtr with GWLP_USERDATA as the first parameter and this as the second. Then in the WindowProc callback use GetWindowLongPtr to get it from the HWND.
The following shows how to pass calls to the freestanding message handler function, on to a member function of your C++ object.
It's a lot of code, but normally you would abstract this away in some reusable module, and the most basic stuff is just the little class gui::api_level::window_subclasser_t.
I haven't shown very much error handling, and neither does this code support programmatic destruction of the C++ object via external delete (I think the sane way to do this is to just DestroyWindow the API level window and let that propagate up to self-destruction of the C++ object, but it's many years since last time I did this).
#undef UNICODE
#define UNICODE
#undef NOMINMAX
#define NOMINMAX
#undef STRICT
#define STRICT
#undef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
//#include <windowsx.h>
#include <commctrl.h> // SetWindowSubclass
#include <assert.h> // assert
#include <stdexcept> // std::exception, std::runtime_error
#include <stdlib.h> // EXIT_SUCCESS, EXIT_FAILURE
#include <string> // std::string
#ifndef IS_DELETED
# define IS_DELETED = delete // C++11
#endif
namespace cpp {
using namespace std;
bool hopefully( bool condition ) { return condition; }
bool throw_x( string const& s ) { throw runtime_error( s ); }
} // namespace cpp
namespace winapi {
using cpp::hopefully;
using cpp::throw_x;
bool get( MSG& m )
{
int const code = ::GetMessage( &m, 0, 0, 0 );
hopefully( code >= 0 )
|| throw_x( "winapi::get( MSG ): GetMessage failed" );
return !!code;
}
} // namespace winapi
namespace gui {
using cpp::hopefully;
using cpp::throw_x;
namespace api_level
{
class message_handler_t
{
public:
virtual LRESULT window_proc( MSG const& message ) = 0;
};
LRESULT CALLBACK main_window_subclassproc(
HWND const window,
UINT const message_id,
WPARAM const w_param,
LPARAM const l_param,
UINT_PTR const subclass_id,
DWORD_PTR const data
)
{
(void) subclass_id; struct subclass_id;
auto const p_handler = reinterpret_cast< message_handler_t* >( data );
MSG const message = { window, message_id, w_param, l_param, DWORD(), POINT() };
return p_handler->window_proc( message );
}
class window_subclasser_t
{
private:
enum { subclass_id = 1 };
HWND window_handle_;
window_subclasser_t( window_subclasser_t const& ) IS_DELETED;
window_subclasser_t& operator=( window_subclasser_t const& ) IS_DELETED;
public:
HWND handle() const { return window_handle_; }
LRESULT pass_to_superclass( MSG const& m )
{
return ::DefSubclassProc( m.hwnd, m.message, m.wParam, m.lParam );
}
~window_subclasser_t()
{
::RemoveWindowSubclass(
window_handle_,
&main_window_subclassproc,
subclass_id
)
|| throw_x( "gui::api_level::window_subclass_t::<destroy>(): RemoveWindowSubclass failed" );
}
window_subclasser_t(
HWND const api_window,
message_handler_t* cpp_window
)
: window_handle_( api_window )
{
assert( cpp_window != 0 );
::SetWindowSubclass(
window_handle_,
main_window_subclassproc,
subclass_id,
reinterpret_cast<DWORD_PTR>( cpp_window )
)
|| throw_x( "gui::api_level::window_subclass_t::<init>(): SetWindowSubclass failed" );
}
};
ATOM create_main_window_class()
{
WNDCLASS params = {};
params.hbrBackground = reinterpret_cast<HBRUSH>( COLOR_WINDOW + 1 );
params.hCursor = ::LoadCursor( 0, IDC_ARROW );
params.hIcon = ::LoadIcon( 0, IDI_APPLICATION );
params.hInstance = ::GetModuleHandle( nullptr );
params.lpfnWndProc = &::DefWindowProc;
params.lpszClassName = L"MainWindow";
ATOM const result = ::RegisterClass( ¶ms );
hopefully( result != 0 )
|| throw_x( "gui::api_level::create_main_window_class: RegisterClass failed" );
return result;
}
ATOM main_window_class()
{
static ATOM const the_class = create_main_window_class();
return the_class;
}
HWND create_main_window()
{
HWND const window = ::CreateWindow(
MAKEINTATOM( main_window_class() ),
L"My main window",
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, CW_USEDEFAULT,
400, 300,
HWND(), // Parent.
HMENU(), // Menu.
::GetModuleHandle( nullptr ),
nullptr // Param.
);
hopefully( window != 0 )
|| throw_x( "gui::api_level::create_main_window: CreateWindow failed" );
return window;
}
} // api_level
class window_t
: private api_level::message_handler_t
{
private:
window_t( window_t const& ) IS_DELETED;
window_t& operator=( window_t const& ) IS_DELETED;
api_level::window_subclasser_t subclasser_;
virtual LRESULT window_proc( MSG const& m ) override
{
switch( m.message )
{
case WM_DESTROY:
delete this;
::PostQuitMessage( 0 );
return 0;
default:
return subclasser_.pass_to_superclass( m );
}
}
protected:
struct api_object_factory_t
{
virtual HWND create() const
{
return api_level::create_main_window();
}
};
virtual ~window_t() {}
window_t( api_object_factory_t const& factory )
: subclasser_( factory.create(), this )
{}
public:
HWND handle() const { return subclasser_.handle(); }
void show() { ::ShowWindow( handle(), SW_SHOW ); }
};
} // namespage gui
// ---------------------------------------------------------------------------------------
// Usage:
class main_window_t
: public gui::window_t
{
public:
main_window_t()
: gui::window_t( api_object_factory_t() )
{}
};
void cpp_main()
{
auto const main_window = new main_window_t();
main_window->show();
MSG msg;
while( winapi::get( msg ) )
{
::TranslateMessage( &msg );
::DispatchMessage( &msg );
}
assert( msg.message == WM_QUIT );
}
#include <iostream>
auto main() -> int
{
using namespace std;
try { cpp_main(); return EXIT_SUCCESS; }
catch( exception const& x ) { wcerr << "!" << x.what() << endl; }
return EXIT_FAILURE;
}
To compile this with Visual C++ 11.0, define the preprocessor symbol IS_DELETED as nothing.
i want to know if there's a way to print a jpg onto a picture control rectangle (that i build with ResEdit) the action that should print the image is case IDC_BUTTON1: and the target i want to view the image is in a picture control with the id: IDC_STATIC
BOOL CALLBACK AppDlgProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch(uMsg)
{
case WM_INITDIALOG:
DragAcceptFiles(hDlg,true);
SetClassLongPtr(hDlg, GCLP_HICON, (long)LoadIcon(0, IDI_APPLICATION));
return 1;
case WM_COMMAND:
switch(wParam)
{
case IDOK:
return 0;
case IDCANCEL:
EndDialog(hDlg, 0);
}
switch(wParam)
{
case IDC_BUTTON1:
ShellExecute(hDlg,
"open",
"C:\immagine1.jpg",
NULL,
NULL,
SW_SHOWDEFAULT);
break;
}
switch(wParam)
{
case IDC_BUTTON4:
ShellExecute(hDlg,
"open",
"C:\log.txt",
NULL,
NULL,
SW_SHOWDEFAULT);
break;
}
}
return 0;
}
instead of using shell execute that open the default viewer thank you all
The OleLoadPicturePath API function can load a JPEG file.
Then it's just a matter of accessing the bits.
There's also the Windows Imaging Component API, but I haven't used that.
I suspect that it also works, though, and it may be simpler than dealing with the OLE stuff, but here I exemplify OleLoadPicturePath.
Before trying to adapt the code below, you should:
Make sure that the type of the picture control resource is set to BITMAP (essentially, at the .rc text level, that it has the SS_BITMAP style).
Change the ID to something unique, instead of IDC_STATIC.
#include <header_wrapper/olectl_h.h> // IPicture
#include <header_wrapper/windows_h.h>
#include "resource.h" // IDD_DEMO_DIALOG, IDC_PICTURE
#include <progrock/cppx/throwx.h> // hopefully, throwX, std::exception
#include <progrock/cppx/size.h> // size
#include <progrock/winapi/path.h> // *
#include <progrock/winapi/ComPointer.h> // ComPointer
using namespace progrock;
#include <assert.h> // assert
#include <iostream>
#include <stdlib.h> // EXIT_FAILURE, EXIT_SUCCESS
using namespace std;
using cppx::hopefully;
using cppx::size;
using cppx::throwX;
using winapi::String;
struct IsHrSuccess
{
friend bool operator>>( HRESULT const hr, IsHrSuccess const& )
{
return SUCCEEDED( hr );
}
};
IsHrSuccess const isHrSuccess = IsHrSuccess();
short kindOf( IPicture const& pic )
{
short kind = 0;
const_cast< IPicture& >( pic ).get_Type( &kind )
>> isHrSuccess || throwX( "kindOf: IPicture::get_Type failed" );
return kind;
}
bool isBitmap( IPicture const& pic )
{
return (kindOf( pic ) == PICTYPE_BITMAP);
}
OLE_HANDLE handleOf( IPicture const& pic )
{
OLE_HANDLE result = 0;
const_cast< IPicture& >( pic ).get_Handle( &result )
>> isHrSuccess || throwX( "handleOf: IPicture::get_Handle failed" );
return result;
}
HBITMAP bmpHandle( IPicture const& pic )
{
assert( isBitmap( pic ) );
return reinterpret_cast< HBITMAP >( handleOf( pic ) );
}
namespace g {
winapi::ComPointer<IPicture> pPicture;
} // namespace g
INT_PTR CALLBACK demoDialogProc(
HWND const window,
UINT const messageId,
WPARAM const wParam,
LPARAM const lParam
)
{
switch( messageId )
{
case WM_INITDIALOG:
::SendDlgItemMessage(
window, IDC_PICTURE, STM_SETIMAGE,
IMAGE_BITMAP,
reinterpret_cast< LPARAM >( bmpHandle( *g::pPicture ) )
);
break;
case WM_CLOSE:
::EndDialog( window, IDCANCEL );
break;
case WM_COMMAND:
::EndDialog( window, wParam );
break;
}
return 0;
}
struct Com
{
Com() { ::CoInitialize( 0 ) >> isHrSuccess || throwX( "::CoInitialize failed" ); }
~Com() { ::CoUninitialize(); }
};
void cppMain()
{
Com usingCom;
String const picFileName = L"image.jpg";
String const picFilePath = winapi::path::combine( winapi::exeFolder(), picFileName );
::OleLoadPicturePath(
const_cast< wchar_t* >( picFilePath.c_str() ),
nullptr, 0, 0,
IID_IPicture,
g::pPicture.asOutArgument()
)
>> isHrSuccess || throwX( "OleLoadPicturePath failed" );
assert( isBitmap( *g::pPicture ) );
::DialogBox( ::GetModuleHandle( 0 ), MAKEINTRESOURCE( IDD_DEMO_DIALOG ), 0, demoDialogProc );
}
int main()
{
try
{
cppMain();
return EXIT_SUCCESS;
}
catch( exception const& x )
{
wcout << "!" << x.what() << endl;
}
return EXIT_FAILURE;
}