I am making a dll that controls a dialogue box. I like to get a certain area to have red text. This code does compile, but the effect is not seen. Here is the area where the dialogProc is done:
LRESULT CALLBACK DialogProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
switch(message)
{
case WM_INITDIALOG:
CheckDlgButton(hDlg, IDC_CHECK, FALSE);
EnableWindow(GetDlgItem(hDlg, IDOK), FALSE);
return TRUE;
case WM_COMMAND:
switch (LOWORD(wParam))
{
case IDC_CHECK:
if (IsDlgButtonChecked(hDlg, IDC_CHECK))
{
EnableWindow(GetDlgItem(hDlg, IDOK), TRUE);
EnableWindow(GetDlgItem(hDlg, IDCANCEL), FALSE);
}
else
{
EnableWindow(GetDlgItem(hDlg, IDOK), FALSE);
EnableWindow(GetDlgItem(hDlg, IDCANCEL), TRUE);
}
break;
case IDOK:
{
EndDialog(hDlg, TRUE);
return FALSE;
}
case IDCANCEL:
{
EndDialog(hDlg, FALSE);
return FALSE;
}
case WM_CTLCOLORSTATIC:
// Set the colour of the text for our URL
if ((HWND)lParam == GetDlgItem(hDlg,IDC_WARNING))
{
// we're about to draw the static
// set the text colour in (HDC)lParam
SetBkMode((HDC)wParam,TRANSPARENT);
SetTextColor((HDC)wParam, RGB(255,0,0));
return (BOOL)CreateSolidBrush (GetSysColor(COLOR_MENU));
}
return TRUE;
}
}
return FALSE;
}
WM_CTLCOLORSTATIC is a separate message from WM_COMMAND. Your desired handling of the message appears to be correct except that the check for the message is inside your check for WM_COMMAND specific items. Try reorganizing your outer switch statement. Perhaps something like the following:
LRESULT CALLBACK DialogProc(HWND hDlg, UINT message,
WPARAM wParam, LPARAM lParam)
{
switch(message)
{
case WM_INITDIALOG:
// ...
break;
case WM_COMMAND:
switch (LOWORD(wParam))
{
case IDC_CHECK:
// ...
break;
case IDOK:
// ...
break;
case IDCANCEL:
// ...
break;
}
break;
case WM_CTLCOLORSTATIC:
// Set the colour of the text for our URL
if ((HWND)lParam == GetDlgItem(hDlg, IDC_WARNING))
{
// we're about to draw the static
// set the text colour in (HDC)lParam
SetBkMode((HDC)wParam,TRANSPARENT);
SetTextColor((HDC)wParam, RGB(255,0,0));
// NOTE: per documentation as pointed out by selbie, GetSolidBrush would leak a GDI handle.
return (BOOL)GetSysColorBrush(COLOR_MENU);
}
break;
}
return FALSE;
}
Also note that it would be kinda weird to filter WM_COMMAND's wParam argument when wParam is supposed to provide the HDC for WM_CTLCOLORSTATIC.
WM_CTLCOLORSTATIC Notification at MSDN
Related
I use SetWindowsHookEx to catch keyboard events
SetWindowsHookEx(WH_KEYBOARD_LL, HookCallback, NULL, 0)
Here is HookCallback
LRESULT __stdcall HookCallback(int nCode, WPARAM wParam, LPARAM lParam)
{
if (nCode >= 0)
{
// the action is valid: HC_ACTION.
if (wParam == WM_KEYDOWN)
{
kbdStruct = *((KBDLLHOOKSTRUCT*)lParam);
printf("%ld\n", kbdStruct.vkCode);
}
}
}
When press "left shift" output is
160
when press "search" button (button with loupe icon on notebooks) output is
160
91
132
How to check if "left shift" or "search" button is pressed inside HookCallback ?
160 is VK_LSHIFT, 91 is VK_LWIN, and 132 is VK_F21. See Virtual-Key Codes. The only standardized Search key is VK_BROWSER_SEARCH, so clearly your keyboard manufacturer is using a non-standard key for its Search key.
You need to remember the keys you see from one event to the next as needed, eg:
bool LeftShiftIsDown = false;
bool LeftWinIsDown = false;
bool F21IsDown = false;
bool SearchIsDown = false;
LRESULT __stdcall HookCallback(int nCode, WPARAM wParam, LPARAM lParam)
{
if (nCode == HC_ACTION)
{
KBDLLHOOKSTRUCT *kbdStruct = (KBDLLHOOKSTRUCT*) lParam;
if (wParam == WM_KEYDOWN)
{
printf("%ld is down\n", kbdStruct->vkCode);
switch (kbdStruct->vkCode)
{
case VK_LSHIFT:
LeftShiftIsDown = true;
break;
case VK_LWIN:
LeftWinIsDown = true;
break;
case VK_F21:
F21IsDown = true;
break;
}
if (LeftShiftIsDown && LeftWinIsDown && F21IsDown)
{
if (!SearchIsDown)
{
SearchIsDown = true;
printf("Search is down\n");
}
}
}
else if (wParam == WM_KEYUP)
{
printf("%ld is up\n", kbdStruct->vkCode);
switch (kbdStruct->vkCode)
{
case VK_LSHIFT:
LeftShiftIsDown = false;
break;
case VK_LWIN:
LeftWinIsDown = false;
break;
case VK_F21:
F21IsDown = false;
break;
}
if (!(LeftShiftIsDown && LeftWinIsDown && F21IsDown))
{
if (SearchIsDown)
{
SearchIsDown = false;
printf("Search is up\n");
}
}
}
}
return CallNextHookEx(0, nCode, wParam, lParam);
}
I am trying to send a custom message (WM_APP + 1) when WM_SIZE is send to the window procedure. I want to be able to catch it from an other function using PeekMessage and do somethings. But when I test it the messages seem to not be send to the queue. Adding some printf statements shows me that it goes to the window procedure. The weird thing is that when I step through the code in the debugger it works fine but when I'm running normally, it goes back to not working.
Working example program with the problem, resize the window to test:
#include <windows.h>
#include <stdio.h>
#pragma comment(lib, "user32.lib")
#define OBG_EVENT_QUIT 0
#define OBG_EVENT_RESIZE 1
#define OBG_EVENT_NO -1
#define OBG_EVENT_UNKNOWN -2
//user defined event
#define OBG_WM_RESIZE (WM_APP + 1)
typedef union
{
int type;
struct
{
int type;
int width;
int height;
} resizeEvent;
} obg_event;
LRESULT CALLBACK obgpf_DefaultWindowCallback(HWND window, UINT message, WPARAM wParam, LPARAM lParam)
{
LRESULT result = 0;
switch(message)
{
case WM_CLOSE:
{
PostMessageA(window, WM_QUIT, 0, 0);
} break;
//this should be handled by OBGGetEvent
case OBG_WM_RESIZE:
{
printf("MESSAGE WENT THROUGH. DON'T WANT THIS\n");
} break;
case WM_SIZE:
{
PostMessageA(window, OBG_WM_RESIZE, wParam, lParam);
} break;
default:
{
result = DefWindowProc(window, message, wParam, lParam);
} break;
}
return result;
}
int OBGGetEvent(obg_event *event)
{
int moreMessages = 0;
MSG message;
if(PeekMessage(&message, 0, 0, 0, PM_REMOVE))
{
moreMessages = 1;
switch(message.message)
{
case WM_QUIT:
{
event->type = OBG_EVENT_QUIT;
} break;
case OBG_WM_RESIZE:
{
event->type = OBG_EVENT_RESIZE;
event->resizeEvent.type = OBG_EVENT_RESIZE;
event->resizeEvent.width = LOWORD(message.lParam);
event->resizeEvent.height = HIWORD(message.lParam);
} break;
default:
{
event->type = OBG_EVENT_UNKNOWN;
TranslateMessage(&message);
DispatchMessage(&message);
} break;
}
}
else
{
event->type = OBG_EVENT_NO;
}
return moreMessages;
}
int main()
{
HINSTANCE instance = GetModuleHandleA(0);
WNDCLASSEX windowClass = {0};
windowClass.cbSize = sizeof(windowClass);
windowClass.style = CS_HREDRAW | CS_VREDRAW;
windowClass.lpfnWndProc = obgpf_DefaultWindowCallback;
windowClass.hInstance = instance;
windowClass.lpszClassName = "testClass";
windowClass.hIcon = LoadIcon(0, IDI_APPLICATION);
windowClass.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
windowClass.hIconSm = LoadIcon(0, IDI_APPLICATION);
windowClass.hCursor = LoadCursorA(0, IDC_ARROW);
HWND window;
if(RegisterClassEx(&windowClass))
{
window = CreateWindowEx(0,
windowClass.lpszClassName,
"test window",
WS_OVERLAPPEDWINDOW | WS_VISIBLE,
CW_USEDEFAULT,
CW_USEDEFAULT,
500,
300,
0,
0,
instance,
0);
if(window)
{
int appIsRunning = 1;
obg_event event = {0};
event.type = -1;
while(appIsRunning)
{
while(OBGGetEvent(&event))
{
if(event.type == OBG_EVENT_QUIT)
{
printf("event quit\n");
appIsRunning = 0;
break;
}
else if(event.type == OBG_EVENT_RESIZE)
{
printf("window resized: width %d height %d\n", event.resizeEvent.width, event.resizeEvent.height);
}
}
Sleep(33);
}
}
else
{
printf("window error\n");
}
}
else
{
printf("windowClass error\n");
}
return 0;
}
I tried doing this with SendMessage instead of PeekMessage but the same thing happened. Not sure what I'm missing or misunderstanding but any help is appreciated!
EDIT: added a complete working program that reproduces the problem
Thank you for the example code.
The behaviour you're seeing is because when the window is being resized using the mouse, the OS enters a modal message loop to process mouse input. During this loop, your message loop doesn't run - which means your PeekMessage() and the special message handling doesn't run either. Instead, messages are simply dispatched as normal to your window procedure.
There are two solutions that come to mind immediately but how you deal with this really depends on the design of your program and why you want to process size events in this way.
The first idea I had is to keep track of whether you're in a modal sizing loop or not, and defer posting the notification message until the loop is finished. An example of how to do that using your provided window procedure is below.
The second solution is to simply call your resize event handler directly whenever you get WM_SIZE (or, if you must go through the event system, put the handler for it in the window procedure rather than using PostMessage).
Example code for the first suggestion:
LRESULT CALLBACK obgpf_DefaultWindowCallback(HWND window, UINT message, WPARAM wParam, LPARAM lParam)
{
LRESULT result = 0;
static bool fResized = false;
static bool fInSizeLoop = false;
switch(message)
{
case WM_CLOSE:
{
PostMessageA(window, WM_QUIT, 0, 0);
} break;
//this should be handled by OBGGetEvent
case OBG_WM_RESIZE:
{
printf("MESSAGE WENT THROUGH. DON'T WANT THIS\n");
} break;
case WM_SIZE:
{
if (fInSizeLoop) // in modal size loop, defer notification
fResized = true;
else
PostMessageA(window, OBG_WM_RESIZE, wParam, lParam);
} break;
case WM_ENTERSIZEMOVE:
fInSizeLoop = true; // begin modal size loop
break;
case WM_EXITSIZEMOVE:
fInSizeLoop = false; // left modal size loop
// post resize notification now
if (fResized) {
RECT rc;
GetClientRect(window, &rc);
PostMessageA(window, OBG_WM_RESIZE, 0, MAKELPARAM(rc.right - rc.left, rc.bottom - rc.top));
fResized = false;
}
break;
default:
{
result = DefWindowProc(window, message, wParam, lParam);
} break;
}
return result;
}
(sorry, this is C++ code and I just noticed you had tagged as C - but the principle is the same).
If i use the win32 api,how to respond WM_RBUTTONUP?
case WM_RBUTTONUP:
{
break;
}
case WM_COMMAND:
wmId = LOWORD(wParam);
wmEvent = HIWORD(wParam);
switch (wmId) {
case IDE_FRIEND:
{
switch (wmEvent) {
case WM_RBUTTONUP:
{
POINT pt;
GetCursorPos(&pt);
TrackPopupMenu(g_hMenu, TPM_RIGHTBUTTON, pt.x, pt.y, 0, hWnd, nullptr);
break;
}
default:
return DefWindowProc(hWnd, msg, wParam, lParam);
}
break;
}
When I click the right mouse button,it will not enter 'case WM_RBUTTONUP'
'IDE_FRIEND' is associated with listbox.
Right-click on listbox will generate context message for parent window. You should be able to catch it like this:
case WM_CONTEXTMENU:
{
if (hListBox == (HWND)wParam)
{
int x = LOWORD(lParam);
int y = HIWORD(lParam);
TrackPopupMenu(g_hMenu, TPM_RIGHTBUTTON, x, y, 0, hWnd, nullptr);
}
break;
}
case WM_RBUTTONUP:
{
//main window's right-click message
break;
}
case WM_COMMAND:
{
//...
}
Edit:
To do the same thing with subclass
#include "CommCtrl.h" //may need to add this
#pragma comment(lib, "comctl32.lib")
LRESULT CALLBACK ListBoxProc(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp,
UINT_PTR, DWORD_PTR)
{
switch (msg){
case WM_RBUTTONUP:
{
//add listbox menu here
break;
}
case WM_NCDESTROY://safely remove subclass
{
RemoveWindowSubclass(hwnd, ListBoxProc, 0);
return DefSubclassProc(hwnd, msg, wp, lp);
}
}
return DefSubclassProc(hwnd, msg, wp, lp);
}
LRESULT CALLBACK MainWindowProc(HWND hWnd, UINT msg, WPARAM wp, LPARAM lp) {
//...
case WM_CREATE:
ListBox = CreateWindow(L"listbox", ... )
SetWindowSubclass(ListBox, ListBoxProc, 0, 0);
//...
}
This is my main loop:
while(TRUE)
{
PeekMessage(&msg,hWnd,0,0,PM_REMOVE);
if (msg.message==WM_QUIT)
break;
TranslateMessage(&msg);
DispatchMessage(&msg);
}
and this is my callback procedure:
LRESULT CALLBACK WinProc(HWND hWnd,UINT msg1,WPARAM wParam,LPARAM lParam)
{
switch(msg1)
{
case WM_DESTROY :
{
PostQuitMessage(0);
return 0;
}
break;
}
return DefWindowProc(hWnd,msg1,wParam,lParam);
}
I found out that when I press Close button WM_NCLBUTTONDOWN will be returned by the PeekMessage function in the next loop, and no WM_QUIT!
The correct way to do a message loop is
BOOL bRet;
MSG msg;
while ((bRet = GetMessage(&msg, hWnd, 0, 0)) != 0)
{
if (bRet == -1)
{
// handle the error and possibly exit
}
else
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
You can use PeekMessage if you really need to... but why are you ignoring the return value?
Also, note that this is specific to a window. I believe PostQuitMessage is for a thread... I don't remember it off the top of my head, but you might need to pass NULL instead of hWnd.
If you have any other windows, that may hijack their message loop as well -- I don't think it's usually an issue, but it might potentially be one; keep that in mind.
Here's some code I found. It should give you something to work with.
// Main message loop:
do
{
while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
{
if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
// Run game code here.
gTradeApp->ExecuteNextAction();
}
while (msg.message != WM_QUIT);
and the WndProc
LRESULT CALLBACK WndProc(HWND aHWnd, UINT aMessage, WPARAM aWParam, LPARAM aLParam)
{
switch (aMessage)
{
case WM_COMMAND:
return HandleCommand(aHWnd, aMessage, aWParam, aLParam);
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(aHWnd, aMessage, aWParam, aLParam);
}
return 0;
}
I recommend sticking with this, to ensure errors (-1) returned by GetMessage can be handled properly:
while(GetMessage(&Msg, NULL, 0, 0) > 0) {
TranslateMessage(&Msg);
DispatchMessage(&Msg);
}
Also, another error is not handling WM_CLOSE properly. Try this instead to make your program actually listen to WM_CLOSE (the close button):
LRESULT CALLBACK WndProc(HWND hwnd, UINT Message, WPARAM wParam, LPARAM lParam) {
switch(Message) {
case WM_CLOSE: {
DestroyWindow(hwnd); // this
break;
}
case WM_DESTROY: {
PostQuitMessage(0);
break;
}
default:
return DefWindowProc(hwnd, Message, wParam, lParam);
}
return 0;
}
I have created a simple win 32 application..in which it has a textbox and a button in a dialog window..first when I created this..it didnt display the dialog window and then what I did is added the code below to handle the close(WM_CLOSE) of the dialog window...but I want to know, how to handle the button click event..
void ValidatePassword(CString encryptedPassword)
{
//create password dialog window
CreateEvent(NULL,true,false,L"TestEvent");
MSG msg;
HWND hwnd = CreateWindowEx(0,WC_DIALOG,L"Security Alert",WS_OVERLAPPEDWINDOW|WS_VISIBLE,
600,300,300,200,NULL,NULL,NULL,NULL);
//create label
CreateWindowEx(NULL,L"Static",L"Requires Password to Run the File:", WS_CHILD|WS_VISIBLE,
10,25,300,20,hwnd,(HMENU)label_id,NULL,NULL);
//create textboxcontrol within the dialog
CreateWindowEx(WS_EX_CLIENTEDGE,L"EDIT",L"",WS_CHILD|WS_VISIBLE | ES_PASSWORD,
10,50,125,25,hwnd,(HMENU)textbox_id,NULL,NULL);
//create button
HWND button = CreateWindowEx(WS_EX_CLIENTEDGE,L"Button",L"OK",WS_CHILD|WS_VISIBLE,
10,100,100,25,hwnd,(HMENU)button_id,NULL,NULL);
ShowWindow (hwnd, SW_SHOW);
UpdateWindow(hwnd);
//SetWindowLong(button,DWL_DLGPROC, (long)myProc);
while(GetMessage(&msg,NULL,0,0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
LRESULT WINAPI myProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
HWND hwndButton;
switch (message)
{
/* Handles all Windows Messages */
case WM_COMMAND:
{
if(((HWND)lParam) && (HIWORD(wParam) == BN_CLICKED))
{
int iMID;
iMID = LOWORD(wParam);
switch(iMID)
{
case button_id:
{
MessageBox(hwnd, (LPCTSTR)"You just pushed me!", (LPCTSTR) "My Program!", MB_OK|MB_ICONEXCLAMATION);
break;
}
default:
break;
}
}
break;
}
case WM_DESTROY:
{
PostQuitMessage (0); /* send a WM_QUIT to Message Queue, to shut off program */
break;
}
}
return 0;
}
Yikes.
It should not be necessary to call SetWindowLong to set the dialog proc for a dialog. Your "simple" program should look something like
#include <windows.h>
#include "resource.h"
BOOL CALLBACK myProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch(uMsg)
{
case WM_INITDIALOG:
return TRUE;
case WM_COMMAND:
if( LOWORD(wParam) == IDCLOSE) // close button click
EndDialog(hwnd,0);
return TRUE;
}
return FALSE;
}
int CALLBACK WinMain(HINSTANCE hExe,HINSTANCE,LPCSTR,INT)
{
return DialogBox(hExe,MAKEINTRESOURCE(IDD_DIALOG),NULL,myProc);
}
Check for WM_COMMAND. LOWORD(wParam) will be your control ID and lParam will be your hWnd for the button.