Making TAB key work in my win32 app - c++

I want to make the tab button work on my app , so when I press tab, it will change from one edit box to another, these are the edit box codes:
case WM_CREATE:
TextBox = CreateWindow("EDIT",
"",
WS_BORDER|WS_CHILD|WS_VISIBLE|WS_EX_LAYERED|WS_TABSTOP|WS_GROUP,
60,50,200,20,
hwnd,NULL,NULL,NULL);
DataBox = CreateWindow("EDIT",
"",
WS_BORDER|WS_CHILD|WS_VISIBLE|WS_TABSTOP|WS_GROUP,
60,72,200,20,
hwnd,NULL,NULL,NULL);
MotivBox = CreateWindow("EDIT",
"",
WS_BORDER|WS_CHILD|WS_VISIBLE|WS_TABSTOP|WS_GROUP,
60,92,200,20,
hwnd,NULL,NULL,NULL);
PretBox = CreateWindow("EDIT",
"",
WS_BORDER|WS_CHILD|WS_VISIBLE|WS_TABSTOP|WS_GROUP,
60,112,200,20,
hwnd,NULL,NULL,NULL);

The fix is quite simple. Given the fact you're handling the WM_CREATE message, rather than the WM_INITDIALOG message, it seems safe to assume that you're adding the controls to a 'standard' window, rather than a 'dialog'.
With that in mind, I expect you've got something like the following in your winmain:
/* Run the message loop. It will run until GetMessage() returns 0 */
while (GetMessage (&messages, NULL, 0, 0))
{
/* Translate virtual-key messages into character messages */
TranslateMessage(&messages);
/* Send message to WindowProcedure */
DispatchMessage(&messages);
}
However, the documentation for IsDialogMessage states:
"Although the IsDialogMessage function is intended for modeless dialog boxes, you can use it with any window that contains controls, enabling the windows to provide the same keyboard selection as is used in a dialog box.
When IsDialogMessage processes a message, it checks for keyboard messages and converts them into selection commands for the corresponding dialog box. For example, the TAB key, when pressed, selects the next control or group of controls, and the DOWN ARROW key, when pressed, selects the next control in a group.
Because the IsDialogMessage function performs all necessary translating and dispatching of messages, a message processed by IsDialogMessage must not be passed to the TranslateMessage or DispatchMessage function."
So, you can change your message pump to resemble the following:
/* Run the message loop. It will run until GetMessage() returns 0 */
while (GetMessage (&messages, NULL, 0, 0))
{
/* Translate virtual-key messages into character messages */
if (IsDialogMessage(hwnd, &messages) == 0)
{
TranslateMessage(&messages);
/* Send message to WindowProcedure */
DispatchMessage(&messages);
}
}

As far as I remember, you should use WS_GROUP only on the first entry. All following childwindows will be added to this group. When you create a new group, you use WS_GROUP again on the first window being created.
So keep the WS_GROUP on TextBox and remove it form the other windows.
You can read about this here on MSDN.
You also should use the IsDialogMessage in your message loop. You can see an example here.

I had a similar problem and found adding the IsDialogMessage statement before TramnslateMessage and DispatchMessage (as shown in the accepted answer) fixed my problem.

Related

Can't show/hide buttons in the main window of my C++ Win32 app [duplicate]

This question already has answers here:
Access a variable from a different switch case (from WM_CREATE to WM_CTLCOLORSTATIC in the WinApi)
(2 answers)
Closed last month.
I have created a basic button in case: WM_CREATE in the windows procedure with the following.
/*The "new_game_button" is declared as type HWND at the
start of the windows procedure function but not initialized.*/
new_game_button = CreateWindow ( "BUTTON", "New Game",
WS_CHILD | WS_BORDER ,
50, 50, 100, 100,
hwnd, NULL, NULL, NULL);
My intent is to create an instructions and "start new game" button as the first thing in my simple tictactoe app. It will immediately show as expected if I give the parameter WS_VISIBLE.
Further in the same case:WM_CREATE if I use the lines
if (!start_Game){ //global variable default is false
ShowWindow( new_game_button, SW_SHOW);
}
The button will show as expected.
Outside of those two cases I cannot get the button to show at a later stage.
Further, if I use one of those two methods to show the button I can never get it to go away using
ShowWindow ( new_game_button, SW_HIDE);
Once the button is showing, it stays for the duration of the programs execution. Doesn't matter which case. Command/Create/Paint
I have tried using
if (start_Game){
ShowWindow( new_game_button, SW_HIDE);
UpdateWindow ( new_game_button );
//UpdateWindow ( hwnd ); tried this as well
}
inside case WM_CREATE.
I have also tried the same SW_HIDE line inside case: WM_COMMAND where a new game is generated (compiles but doesn't hide the button.)
I have tried declaring the button child window outside the WM_CREATE inside the windows procedure function. Then using WM_CREATE to show the window - works -- still WM_COMMAND will not hide the window.
I have also tried creating the button window inside of case:WM_PAINT which works to show the button but not to get rid of it. I have even tried DestroyWindow which just fails. [returns 0]
In trying to understand the behaviour of the button window - I have found that I cannot get
ShowWindow( new_game_button, SW_SHOW);
to work in the case:WM_COMMAND.
You said (in a code comment in the question).
new_game_button is declared as type HWND at the start of the windows procedure function but not initialized.
Each incoming message means a new call to your window procedure. Variables which are local to a function don't retain their value between calls unless they are marked static.
When your window procedure returned from processing WM_CREATE, you lost the value of new_game_button. When you try to use it during WM_COMMAND processing later, it is uninitialized and your program causes undefined behavior by passing it to ShowWindow.
Every comment helped me solve this which I appreciate. I am too new to give reputation sadly.
case WM_CREATE:
{
new_game_button = CreateWindow ("BUTTON", "New Game",
WS_CHILD | WS_BORDER ,
50, 50, 100, 100,
hwnd, (HMENU) 1, NULL, NULL);
if (!start_Game){
ShowWindow( new_game_button, SW_SHOW);
}
}
break;
After I had an ID for the dialog I am able to use GetDlgItem function to show or hide as I please.
new_game_button = GetDlgItem (hwnd, 1);
ShowWindow( new_game_button, SW_HIDE);

Switch between edit controls using Tab?

The Window is non DialogBox based so WS_TABSTOP doesn't work. Moreover I don't want to Tab through all the controls, I just want to Tab through few Edit controls.
What I did is I superclassed the Edit control and handled the WM_KEYDOWN message, switching between edit controls, by getting next window in the line thorugh ::GetWindow(hwnd,GW_HWNDNEXT); Also I would like to switch focus back to the first Edit control when I have reached the last one.
The Code doesn't work for when I have reached the last Edit control, the ::GetWindow simply returns the next window in the line(?), which happens to be a non superclassed edit control. And there are more hidden child windows(SW_HIDE).
Maybe if I know how to know the class name of the window's HWND ?
Note: Pure Win32 api, c++ oop.
else if ( ( int ) wParam == VK_TAB )
{
HWND nextInLine;
nextInLine = ::GetWindow ( hwnd, GW_HWNDNEXT );
if ( hwnd == NULL ) nextInLine = ::GetWindow ( hwnd, GW_HWNDPREV );
::SendMessage ( nextInLine, EM_SETSEL, ( WPARAM ) 0, ( LPARAM ) -1 );
::SetFocus ( nextInLine );
return 0;
}
You get keyboard navigation for free in any window by using the IsDialogMessage API call. To consume the service a window message loop has to be modified to include a call to IsDialogMessage and only pass the message on to regular message handling if it hasn't been handled by the dialog manager already.
MSG msg = { 0 };
while (GetMessage(&msg, NULL, 0, 0)) {
if (IsDialogMessage(hwnd, &msg)) {
/* Already handled by dialog manager */
} else {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
Don't forget to set the WS_EX_CONTROLPARENT extended window style on the parent window, so that the dialog manager recurses into child windows.
It's possible to get away with just calling IsDialogMessage, but the result isn't quite 100% dialog-like. To make an ordinary window behave like a dialog:
Specify DLGWINDOWEXTRA as the cbWndExtra field of your WNDCLASS (don't forget to add on extra space you might already be using and offset your data's indexes)
Call DefDlgProc rather than DefWindowProc
Since this makes your window a dialog, you need to use the DWLP_USER window long instead of GWLP_USERDATA, if you're using that, when calling GetWindowLongPtr or SetWindowLongPtr.
(From memory, the main thing you get from doing the above is support for WM_NEXTDLGCTL, which I've found useful to use for supporting changing focus using the Enter key, using Method I described in http://support.microsoft.com/kb/102589.)
Then in your message pump, call IsDialogMessage for each dialog-like window in your message pump.
Finally, when creating controls for your dialog-like window, set the WS_TABSTOP window style for each window you want to participate in the tabbing, and set the WS_EX_CONTROLPARENT window exstyle (aka Control Parent in the resource editor) for child windows that contain dialog controls.

How do I detect the addition of a new monitor?

I have a windowless program that handles some window management hotkeys. I'd like to provide features such as the ability to move a window between monitors. I've used EnumDisplayMonitors to enumerate all existing monitors in the system, and I've written code to handle WM_DEVICECHANGE, but I'm not actually receiving the message.
Here's my message loop:
// I've tried GetMessage(&msg, (HWND) NULL, 0, 0) here too
while (GetMessage(&msg, (HWND) -1, 0, 0) > 0)
{
int key;
int mod;
MessageBox(NULL, (LPCWSTR) ((std::wostringstream&) (std::wostringstream() << L"You got a message: " << msg.message)).str().c_str(), L"Got Message", MB_OK);
switch (msg.message)
{
case WM_HOTKEY:
key = HIWORD(msg.lParam);
mod = LOWORD(msg.lParam);
if (mod != MOD_WIN) continue;
ProcessHotkey(key);
break;
case WM_DEVICECHANGE:
InitMonitorInfo();
}
}
The program compiles and runs fine, and the hotkeys work. Upon adding or removing a monitor though, nothing happens. The message box to indicate a message has been received never appears.
I suppose I could just poll the monitor configuration every 5 seconds, but that's not the right way to solve the problem.
Do I need to actually create a window to receive WM_DEVICECHANGE? Because I don't. The hotkeys post their messages to NULL when they fire since they're not bound to a window, to be handled by the main thread.
You must create a window to get the WM_DEVICECHANGE message.
WM_DEVICECHANGE is a message that's broadcast, SendMessage(HWND_BROADCAST,...) style. Only top-level windows can receive it. The window doesn't need to be visible so there's little reason to look for an alternative.
RegisterDeviceNotification() is an alternative. But that still needs a window. Or a service handle, but you don't want to move windows around from a service. They run in an isolated session with their own desktop. So creating a window is a hard requirement.

How to handle closing MessageBox

my environment is C++, MFC, compact-framework for WM 6.0+ devices.
In many places, I am showing pop-up messages using 'MessageBox()' to give a simple warning or get Yes/No repsonse from user. What I want to do is that whenever any message is closed, call some common function before I perform specific codes.
I tried WM_SHOWWINDOW in parent window but it doesn't seem to occur.
Any suggestion will be appreciated.
[Added] my screen has many buttons and I have to make sure only one button is focused all the time. When I show message box, button seems to loose its focus so I want to focus it back when message is closed. Of course, I can do it in every place where message is used but looking for a better way to handle this situation.
The MessageBox function returns specific return codes when it is closed, you can wrap the MessageBox function and check the return values and run some code based on that.
Here are the return codes from MSDN :
IDABORT 3 The Abort button was selected.
IDCANCEL 2 The Cancel button was selected.
IDCONTINUE 11 The Continue button was selected.
IDIGNORE 5 The Ignore button was selected.
IDNO 7 The No button was selected.
IDOK 1 The OK button was selected.
IDRETRY 4 The Retry button was selected.
IDTRYAGAIN 10 The Try Again button was selected.
IDYES 6 The Yes button was selected.
So the following code can be used to run different functions based on the return code.
void MyMessageBox(wstring title,wstring message)
{
int msgboxID = MessageBox(
NULL,
(LPCWSTR)message.c_str(),
(LPCWSTR)title.c_str(),
MB_ICONWARNING | MB_CANCELTRYCONTINUE | MB_DEFBUTTON2
);
switch (msgboxID)
{
case IDCANCEL:
// TODO: add code
break;
case IDTRYAGAIN:
// TODO: add code
break;
case IDCONTINUE:
// TODO: add code
break;
//so on
}
}
More info here :
http://msdn.microsoft.com/en-us/library/windows/desktop/ms645505(v=vs.85).aspx
You might try intercepting the WM_ACTIVATE message in the parent window.

"SendMessage" to 3 different processes in C++

I want to send keystrokes to multiple processes. For example, if I press “1”, then I want to send the “1” to 3 "Notepad windows". Frist I want to try to send a keystroke to notepad, but it fails on the HWND:
//HANDLE hWin;
HWND windowHandle = FindWindowA(NULL, "Notepad"); //Can’t find a proccess
//Send a key
if( windowHandle ) //This one fails
{
while(true)
{
if( GetAsyncKeyState(VK_F12) != 0 )
{
SendMessageA(windowHandle, WM_KEYDOWN, VK_NUMPAD1, 0);
Sleep(1000);
SendMessageA(windowHandle, WM_KEYUP, VK_NUMPAD1, 0);
}
Sleep(100);
}
}
But the "FindWindow" method is not good enough for my program. There is also no way to get 3 different processes with the same name. So how can I make 3 handles to 3 different processes with the same name? And how can I send key’s to the processes?
You can use EnumWindows for enumerating all the top level windows on the system. You then need to filter through these windows to get the ones you are interested in. Class name is probably a better choice for filtering rather than the window name though. Here is some example code (not tested) of what I have in mind:
BOOL CALLBACK BroadcastToNotepad(HWND hwnd, LPARAM lParam)
{
wchar_t lpClassName[16];
/*
* More reliable to filter by class name. We could additionally filter
* by caption name too if necessary.
*/
if(GetClassName(hwnd, lpClassName, _countof(lpClassName))) {
if(wcscmp(lpClassName, L"Notepad") == 0) {
SendMessage(hwnd, WM_KEYDOWN, (WPARAM)lParam, 0);
Sleep(1000);
SendMessage(hwnd, WM_KEYUP, (WPARAM)lParam, 0);
}
}
return TRUE;
}
// Some handler which gets invoked when your hotkey is hit.
void handlerKey1(...)
{
EnumWindows(BroadcastToNotepad, (lParam)VK_NUMPAD1)
}
Note the usage of BroadcastToNotepad and how you can have different handlers pass in a different lParam.
One final thing to note is that PostMessage/SendMessage is not a reliable way to simulate keyboard input. This is noted by Raymond Chen here. SendInput is the preferred way for injecting input. However, to use that you will need to ensure the window you want to send to has the keyboard focus.
I recall vaguely having played with something similar to what you are doing in the past. If I remember correctly, you need to send to Notepad's child window (class name = Edit). So the code above needs to be modified as so:
if(wcscmp(lpClassName, L"Notepad") == 0) {
HWND hwndChild = FindWindowEx(hwnd, NULL, L"Edit", NULL);
SendMessage(hwndChild, WM_KEYDOWN, (WPARAM)lParam, 0);
Sleep(1000);
SendMessage(hwndChild, WM_KEYUP, (WPARAM)lParam, 0);
}
Firstly install Spy++ from Visual Studio which lets you see all the HWND windows in hierarchy ( and which process owns them).
Then you'll see why your FindWindow is failing. You'll also know the exact hierarchy calls to make on FindWindow and GetWindow().
Be aware that since Vista some HWNDs are protected and you cant send to them - but notepad is probably fine.
For sending the key, you can probably just use PostMessage to fire and forget.
First of all, why is while(true) there? Wouldn't you rather want to activate your software on F12 key press than having an infinite loop? That handle is not valid forever, you know.
Second, you'd probably want to use EnumWindows to go through all the windows and find the one you're interested in. Then you'd implement a callback function that'll need to decide on some basis if it wants to act on some window or not (be it name or something else).
SendMessage/SendMessageA/SendMessageW should work just fine when you've found a proper handle for the window you want to target for (save for some special windows that are protected from this).