Get HMENU from HWND within a Hook - winapi

I'm installing a hook within my application to get the standard EDIT context menu (with undo/copy/edit/paste/etc.). I need to insert a new menu item for my application.
I've set a windows hook, but I can't seem to get the HMENU for the context menu. This is where I set the hook:
g_hHook = SetWindowsHookEx(WH_CALLWNDPROC, HookCallWndProc, NULL, GetCurrentThreadId());
Here is my callback function:
LRESULT CALLBACK HookCallWndProc(int nCode, WPARAM wParam, LPARAM lParam)
{
if (nCode == HC_ACTION)
{
LPCWPSTRUCT cwps = (LPCWPSTRUCT)lParam;
switch(cwps->message)
{
case WM_CREATE:
{
WCHAR szClass[128];
GetClassName(cwps->hwnd, szClass, 127);
if (wcscmp(szClass, L"#32768") == 0)
{
LPCREATESTRUCT lpcs = (LPCREATESTRUCT)cwps->lParam;
HMENU hMenu = GetMenu(cwps->hwnd);
// hMenu is 0x0
//MENUINFO info;
//ZeroMemory(&info, sizeof(MENUINFO));
//info.cbSize = sizeof(info);
//GetMenuInfo(hMenu, &info);
MessageBox(NULL, L"Test", L"Test", NULL);
}
break;
}
}
}
return CallNextHookEx(g_hHook, nCode, wParam, lParam);
}
I also tried setting the hook with WH_CALLWNDPROCRET, but this one doesn't even capture the WM_CREATE message for the menu.
Does anyone know how to obtain the HMENU for this particular situation?
Thanks,
Kevin

You can send the MN_GETHMENU message to get the HMENU:
case WM_CREATE:
{
WCHAR szClass[128];
GetClassName(cwps->hwnd, szClass, 127);
if (wcscmp(szClass, L"#32768") == 0)
{
// Must delay MN_GETHMENU...
PostMessage(g_hDlg,WM_APP,(WPARAM)cwps->hwnd,(LPARAM)HookCallWndProc);
}
break;
}
...
LRESULT CALLBACK MyWindow(HWND hwnd,UINT msg,WPARAM wp,LPARAM lp)
{
switch(msg)
{
case WM_APP:
if (lp == (LPARAM)HookCallWndProc) // Just making sure it is our special message
{
HMENU hMenu = (HMENU) SendMessage((HWND)wp,MN_GETHMENU,0,0);
if (hMenu)
{
AppendMenu(hMenu,MF_STRING,666,L"Hello SO");
}
}
break;
This is a bit hacky but hacks are pretty much unavoidable when customizing controls like this...

Related

How do I force a redraw of WIN32 scrolled window while holding scrollbar down

We've noticed an issue with ScrollWindowEx on our Windows app. Because there are a lot of child windows, if you hold down the scrollbar and move around, it causes a huge stall in DesktopWindowManager.
This is documented here
Why is my MFC application hanging after interacting with both scroll-bars?
So, I've taken the call to ScrollWindowEx out and now I can move my scrollbar with no issues, however the window and all its children only get drawn at the new position when I let go of the scrollbar.
It seems that my entire program stalls and gets stuck in the message loop when the scroll bar is held down. This isnt a problem if we can force the window that the scrollbar is attached to to update. But how do I do that ?
I've tried this
UpdateWindow(pPane);
RedrawWindow(pPane,NULL,NULL,RDW_ALLCHILDREN|RDW_INVALIDATE|RDW_ERASE | RDW_INTERNALPAINT | RDW_UPDATENOW);
And it does update and redraw - you can see the glitching - BUT it redraws it at the position that it was last left at. Not where the new position of the scroll bar would set it.
Im calling
SetScrollInfo(...)
with the correct parameters, otherwise it wouldn't work normally
How can I get it to redraw the window and its children with the correct parameters, whilst the scroll bar is held down?
This is my scroll message handler
case WM_VSCROLL:
{
int iVScrollPos;
SCROLLINFO vsi;
ZeroMemory(&vsi, sizeof(SCROLLINFO));
vsi.cbSize=sizeof(SCROLLINFO);
vsi.fMask=SIF_ALL;
GetScrollInfo(hWnd,SB_VERT,&vsi);
iVScrollPos=vsi.nPos;
switch(LOWORD(wParam))
{
case SB_LINEUP:
if(vsi.nPos>vsi.nMin)
vsi.nPos=vsi.nPos-10;
break;
case SB_PAGEUP:
vsi.nPos = vsi.nPos - vsi.nPage;
break;
case SB_LINEDOWN:
if(vsi.nPos<vsi.nMax)
vsi.nPos=vsi.nPos+10;
break;
case SB_PAGEDOWN:
vsi.nPos = vsi.nPos + vsi.nPage;
break;
case SB_THUMBTRACK:
vsi.nPos=vsi.nTrackPos;
break;
}
vsi.nMin=0;
vsi.nMax=pOW->dialogysize;
vsi.nPage=sy;
SetScrollInfo(hWnd,SB_VERT,&vsi,TRUE);
GetScrollInfo(hWnd, SB_VERT, &vsi);
if(vsi.nPos != iVScrollPos)
{
RedrawWindow(hWnd,NULL,NULL,RDW_ALLCHILDREN|RDW_INVALIDATE|RDW_ERASE | RDW_INTERNALPAINT | RDW_UPDATENOW);
}
The window redraws as you move the scroll bar, but in the same place it was when you held the mouse down on the scrollbar. It onlyupdates position when you let go of the mouse button.
Thanks
Shaun
EDIT - Ive created a sample that reproduces the behaviour. Note we have a separate window class to hold the multiple edit windows. I havent handled the WM_SIZE and WM_MOVE messages in this demo but the code should work with the window as it is.
IF USESCROLL is defined in and you move the scrollbar up and down it casues a lockup in DesktopWindowManager
IF USESCROLL is not commented in, then the trackPos in the structure still gets updated and I call reDrawWindow, but the child edit windows dont move as I scroll the scrollbar.
As I dont want to have the lockup, how can I get them to move please ?
Thanks again
Shaun
#include <windows.h>
#include <stdio.h>
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
LRESULT CALLBACK PaneProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE, PWSTR pCmdLine, int nCmdShow)
{
// Register the window class.
WNDCLASS wc = { };
wc.lpfnWndProc = WindowProc;
wc.hInstance = hInstance;
wc.lpszClassName = "Class";
RegisterClass(&wc);
wc.lpfnWndProc = PaneProc;
wc.hInstance = hInstance;
wc.lpszClassName = "Pane";
RegisterClass(&wc);
// Create the window.
HWND hwnd = CreateWindowEx(
0, // Optional window styles.
(LPCSTR)"Class", // Window class
(LPCSTR)"Test", // Window text
WS_OVERLAPPEDWINDOW| WS_BORDER | WS_CAPTION | WS_CLIPSIBLINGS | WS_THICKFRAME | WS_SYSMENU, // Window style
200,200,400,400,
NULL,NULL,hInstance,NULL);
if (hwnd == NULL)
{
return 0;
}
HWND hwndPane = CreateWindowEx(
0, // Optional window styles.
(LPCSTR)"Pane", // Window class
(LPCSTR)"Test", // Window text
WS_OVERLAPPEDWINDOW|WS_VISIBLE|WS_VSCROLL, // Window style
220,220,400,400,
hwnd,NULL,hInstance,NULL);
if (hwndPane == NULL)
{
return 0;
}
for(int i=0;i<200;i++)
{
HWND eb = CreateWindowEx(WS_EX_CLIENTEDGE, TEXT("Edit"), NULL,ES_AUTOHSCROLL | WS_CHILD| WS_VISIBLE, 16, 16+24*i, 64, 24, hwndPane, (HMENU)i+10000, hInstance, NULL);
char tmp[64];
sprintf(tmp,"%d",i);
SetWindowText(eb,tmp);
}
ShowWindow(hwnd, nCmdShow);
ShowWindow(hwndPane, nCmdShow);
// Run the message loop.
while(1)
{
MSG msg = { };
while (PeekMessage(&msg, NULL, 0,0,PM_REMOVE))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
Sleep(10);
}
return 0;
}
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch (uMsg)
{
case WM_DESTROY:
PostQuitMessage(0);
return 0;
case WM_PAINT:
{
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hwnd, &ps);
FillRect(hdc, &ps.rcPaint, (HBRUSH) (COLOR_WINDOW+1));
EndPaint(hwnd, &ps);
}
return 0;
}
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
LRESULT CALLBACK PaneProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch (uMsg)
{
case WM_CREATE:
int iVScrollPos;
SCROLLINFO vsi;
ZeroMemory(&vsi, sizeof(SCROLLINFO));
vsi.cbSize=sizeof(SCROLLINFO);
vsi.fMask=SIF_ALL;
GetScrollInfo(hwnd,SB_VERT,&vsi);
iVScrollPos=vsi.nPos;
vsi.nMin=0;
vsi.nMax=16+24*200+40;
vsi.nPage=400;
SetScrollInfo(hwnd,SB_VERT,&vsi,TRUE);
GetScrollInfo(hwnd, SB_VERT, &vsi);
break;
case WM_DESTROY:
PostQuitMessage(0);
return 0;
case WM_VSCROLL:
{
int iVScrollPos;
SCROLLINFO vsi;
ZeroMemory(&vsi, sizeof(SCROLLINFO));
vsi.cbSize=sizeof(SCROLLINFO);
vsi.fMask=SIF_ALL;
GetScrollInfo(hwnd,SB_VERT,&vsi);
iVScrollPos=vsi.nPos;
switch(LOWORD(wParam))
{
case SB_THUMBTRACK:
vsi.nPos=vsi.nTrackPos;
break;
}
vsi.nMin=0;
vsi.nMax=16+24*200+40;
vsi.nPage=400;
SetScrollInfo(hwnd,SB_VERT,&vsi,TRUE);
GetScrollInfo(hwnd, SB_VERT, &vsi);
if(vsi.nPos != iVScrollPos)
{
float ScrollAmtY=-(vsi.nPos - iVScrollPos);
#define USESCROLL
#ifdef USESCROLL
int ok=ScrollWindowEx(hwnd ,0,ScrollAmtY,NULL,NULL,NULL,NULL,SW_INVALIDATE|SW_ERASE|SW_SCROLLCHILDREN);
#else
UpdateWindow(hwnd);
RedrawWindow(hwnd,NULL,NULL,RDW_ALLCHILDREN|RDW_INVALIDATE|RDW_ERASE | RDW_INTERNALPAINT | RDW_UPDATENOW);
#endif
}
return 0;
}
break;
}
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}

how can I set static controls background color programmatically

I want to change label background color within a function, I tried this code but nothing changed after calling changecolor function
HWND hWndLabel;
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_CREATE:
hWndLabel = CreateWindowEx(WS_EX_TRANSPARENT,
L"STATIC", L"", WS_CHILD | WS_VISIBLE | SS_LEFT | WS_SYSMENU,
75, 75, 70, 70, hWnd, (HMENU)labelId, hInst, NULL);
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
case WM_COMMAND: // all events are handled here
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
break;
}
return 0;
}
DWORD WINAPI changecolor(){
HDC hdc = GetDC(hWndLabel); // get context
SetBkColor(hdc, RGB(0, 0, 230)); // Code Copied from the above answer by cpx.
return 0;
}
I read that Static controls send their parent a WM_CTLCOLORSTATIC message just before they paint themselves. code is implemented within CALLBACK function, but where this code is called (changing color)?, how can I call SetTextColor within a function
code example :
case WM_CTLCOLORSTATIC:
if (the_button_was_clicked) {
HDC hdc = reinterpret_cast<HDC>(wParam);
SetTextColor(hdc, COLORREF(0xFF, 0x00, 0x00));
}
return ::GetSysColorBrush(COLOR_WINDOW); // example color, adjust for your circumstance
Try something more like this:
HWND hWndLabel;
HBRUSH hBrushLabel;
COLORREF clrLabelText;
COLORREF clrLabelBkGnd;
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_CREATE:
hWndLabel = CreateWindowEx(0, L"STATIC", L"", WS_CHILD | WS_VISIBLE | SS_LEFT,
75, 75, 70, 70, hWnd, (HMENU)labelId, hInst, NULL);
hBrushLabel = NULL;
clrLabelText = GetSysColor(COLOR_WINDOWTEXT);
clrLabelBkGnd = GetSysColor(COLOR_WINDOW);
break;
case WM_DESTROY:
if (hBrushLabel) DeleteObject(hBrushLabel);
PostQuitMessage(0);
break;
case WM_CTLCOLORSTATIC: {
HDC hdc = reinterpret_cast<HDC>(wParam);
SetTextColor(hdc, clrLabelText);
SetBkColor(hdc, clrLabelBkGnd);
if (!hBrushLabel) hBrushLabel = CreateSolidBrush(clrLabelBkGnd);
return reinterpret_cast<LRESULT>(hBrushLabel);
}
case WM_COMMAND: // all events are handled here
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
break;
}
return 0;
}
DWORD WINAPI changecolor()
{
if (hBrushLabel) {
DeleteObject(hBrushLabel);
hBrushLabel = NULL;
}
clrLabelText = RGB(0xFF, 0x00, 0x00);
clrLabelBkGnd = RGB(0, 0, 230);
InvalidateRect(hWndLabel, NULL, TRUE);
return 0;
}
There is a similar example in the WM_CTLCOLORSTATIC documentation.
The following C++ example shows how to set the text foreground and background colors of a static control in response to the WM_CTLCOLORSTATIC message. The hbrBkgnd variable is a static HBRUSH variable that is initialized to NULL, and stores the background brush between calls to WM_CTLCOLORSTATIC. The brush must be destroyed by a call to the DeleteObject function when it is no longer needed, typically when the associated dialog box is destroyed.
case WM_CTLCOLORSTATIC:
{
HDC hdcStatic = (HDC) wParam;
SetTextColor(hdcStatic, RGB(255,255,255));
SetBkColor(hdcStatic, RGB(0,0,0));
if (hbrBkgnd == NULL)
{
hbrBkgnd = CreateSolidBrush(RGB(0,0,0));
}
return (INT_PTR)hbrBkgnd;
}

C++ CreateWindowEx() fails to create window

I've starting watching the handmade hero videos and I'm trying to make a win32 window but the CreateWindowEx() function keeps failing.
I checked the error code and I get 1407.
Code is below.
Thanks in advance.
#include <Windows.h>
LRESULT CALLBACK WindowProcedure(
HWND hwnd,
UINT uMsg,
WPARAM wParam,
LPARAM lParam
)
{
LRESULT result;
switch (uMsg)
{
case WM_ACTIVATEAPP:
{
OutputDebugStringA("The window is now active");
break;
}
case WM_SIZE:
{
OutputDebugStringA("The window is now being resized");
break;
}
case WM_CREATE:
{
OutputDebugStringA("The window has been created");
break;
}
default:
{
result = DefWindowProc(hwnd, uMsg, wParam, lParam);
break;
}
}
return result;
};
int CALLBACK WinMain(
HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpCmdLine,
int nCmdShow
)
{
WNDCLASS GameWindow;
GameWindow.style = CS_OWNDC|CS_HREDRAW|CS_VREDRAW;
GameWindow.lpfnWndProc = WindowProcedure;
GameWindow.hInstance = hInstance;
// HICON hIcon;
GameWindow.lpszClassName = "HandmadeHeroWindowClass";
RegisterClass(&GameWindow);
if (HWND GameWindowHandle = CreateWindowEx(
0,
GameWindow.lpszClassName,
"Handmade Hero",
WS_OVERLAPPEDWINDOW | WS_VISIBLE,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
0,
0,
hInstance,
0
))
{
for (;;)
{
MSG message;
BOOL messageResult = GetMessage(&message, GameWindowHandle, 0, 0);
if (messageResult != 0)
{
DispatchMessage(&message);
}
else if (messageResult == 0)
{
break;
}
else
{
// ERROR
}
}
}
else
{
OutputDebugStringA("Couldn't create window");
}
DWORD error = GetLastError();
return 0;
};
Your window procedure returns an uninitialized variable in every path except for default:, this is undefined behavior and failure of window creation is entirely possible.
For WM_CREATE, the documentation says:
If an application processes this message, it should return zero to continue creation of the window.
As Michael noted in the comments, RegisterClass is failing. Same category of mistake, you're passing a WNDCLASS structure leaving most members uninitialized.
Thanks to Remy Lebeau for the answer, the problem was that my WNDCLASS had uninitialized values for all fields except those I changed, this caused the RegisterClass() to fail and consequently the CreateWindowEx() to fail.
I changed WNDCLASS declaration to this:
WNDCLASS GameWindow = {0};
Thanks to everyone who helped.

How to call and use UnregisterClass?

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).

Detect CTRL+A in a texbox

I have a main window hwndMain and a multiline Edit textbox:
hwndEdit = CreateWindowEx(0, L"EDIT", NULL, WS_CHILD | WS_VISIBLE | ES_LEFT | ES_MULTILINE | ES_AUTOVSCROLL, 0, 0, 300, 200, hwndMain, 0, (HINSTANCE) GetWindowLong(hwndMain, GWL_HINSTANCE), NULL);
I use this to detect CTRL+A in the textbox (because strangely, it's not available out-of-the-box):
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch (uMsg)
{
case WM_KEYDOWN:
{
if (wParam == VK_CONTROL) // something here missing for detecting "A"
{
SendMessage(hwndEdit, EM_SETSEL, 0, -1); // select all ; this code works on its own, tested
}
...
Unfortunately, nothing happens when I do CTRL+A or CTRL+B or CTRL+ anything.
What is wrong?
Note: Ok the code for detecting A is still missing (i still don't know how to do it), but the code here should work for any CTRL+key...
Just check for WM_KEYDOWN for A and than use GetKeyState
case WM_KEYDOWN:
{
if (wParam=='A' && (::GetKeyState(VK_CONTROL) & 0x8000)!=0)
{
SendMessage(hwndEdit, EM_SETSEL, 0, -1); // select all
}
Remember that WM_KEYDOWN is only sent to the window that has the focus and not to parent windows.
After you create all your windows:
hwndEdit = CreateWindowEx(.....)
...
//Subbclassing
SetWindowSubclass(hwndEdit, (SUBCLASSPROC)EditWndProc, 0, 1);
LRESULT CALLBACK EditWndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwrefData){
switch(message){ //handle the messages
case WM_KEYDOWN:
//your code
break;
default: //for messages that we don't deal with
return DefSubclassProc(hwnd, message, wParam, lParam);
}
return DefSubclassProc(hwnd, message, wParam, lParam);
}

Resources