Standard Windows message boxes have a functionality of copying their contents on pressing Ctrl+C to clipboard.
Ok, I decided to try doing this myself.
Here is my dialogue resource:
IDD_MAIN DIALOGEX 0,0,500,250 STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU | WS_VISIBLE
CAPTION "Information"
FONT 8, "MS Shell Dlg",0,0,0x1
{ LTEXT "I strongly recommend you to move all your user folders to disk D\:",ID_INFOTEXT,10,10,100,100
CHECKBOX "&Do not show this dialogue again",ID_SHOWSTATE,10,120,100,15
DEFPUSHBUTTON "&Ok",IDOK,273, 148, 100, 15
}
Dialogue is created quite fine, everything works fine too.
Checkbox is checked/unchecked as expected, and Ok closes the dialogue.
Now when you press Ctrl+C you hear an ugly beep.
I have tryed to handle the Ctrl+C in dialogue proc, but keystrokes do not go there at all.
Now I have used a WM_GETDLGCODE in a subclassed control window proc, but also with no result - could not get rid that ugly beep and Ctrl+C does not go there too.
Ok, I have replaced a windowProc for a dialogue, but still no result - the ugly beep is in place and no Ctrl+C there.
So the question at the very end is where does that ugly beep come from by default? Any special message handle somewhere in DefWindowProc?
Where can I handle it finally and replace handler?
P. S. No MFC, no startup code, no .net, pure C and WinAPI only.
The dialog manager treats MessageBox based dialogs in a special way and sends them a WM_COPY message so it can fill the clipboard.
The beep comes from IsDialogMessage because the child control with the keyboard focus does not want the keyboard input.
To catch the Ctrl+C keypress and avoid the beep you must process keyboard messages before calling IsDialogMessage. You could call TranslateAccelerator or do your own custom handling:
INT_PTR CALLBACK DlgProc(HWND hDlg, UINT Msg, WPARAM Wp, LPARAM Lp)
{
switch(Msg)
{
case WM_CLOSE:
DestroyWindow(hDlg);
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
case WM_COPY:
MessageBoxA(hDlg, "TODO: Set clipboard content here", 0, 0);
break;
}
return FALSE;
}
BOOL IsKbdCopyMessage(HWND hDlg, MSG*pMsg)
{
if (pMsg->message == WM_CHAR && pMsg->wParam == VK_CANCEL)
{
LRESULT dlgcod = SendMessage(pMsg->hwnd, WM_GETDLGCODE, pMsg->wParam, 0);
if (!(dlgcod & (DLGC_WANTMESSAGE|DLGC_WANTCHARS)))
{
SendMessage(hDlg, WM_COPY, 0, 0);
return TRUE;
}
}
return FALSE;
}
...WinMain(...)
{
MSG msg;
HWND hDlg = CreateDialog(GetModuleHandle(NULL), MAKEINTRESOURCE(IDD_MAIN), NULL, DlgProc);
while(GetMessage(&msg, 0, 0, 0))
{
if (IsKbdCopyMessage(hDlg, &msg)) continue;
if (IsDialogMessage(hDlg, &msg)) continue;
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return 0;
}
One way to detect Ctrl-C could be to on receiving a WM_KEYDOWN passing a 0x43 (virtual key-code for the C-key) as wParam call GetAsyncKeyState() and test its result for having VK_CONTROL set.
You could avoid calling call GetAsyncKeyState() by tracking the Ctrl-keys' states yourself by also monitoring WM_KEYUP messages, for the relevant "virtual key-code".
Related
I'm trying to build a simple graph plotting application with C and Windows API for studying purposes. It is supposed to take user's input containing a math function of one real variable and then to plot it. I tried to implement user's input capabilities by creating textbox and submit button through calling CreateWindow() from the callback window function. However, when I attempted to test the textbox by evaluating input text size, I received 0 under any conditions (I also made sure that it is not an ASCII/UNICODE issue). GetLastError() got 1400 error code, i.e. invalid window handle. What could be possible reasons for this error and why respective window handle is considered invalid?
I'm using Bloodshed Dev-C++ 5.11 on Windows 10 x64 with 64-bit TDM-GCC 4.9.2.
Please find below the piece of callback function that is causing problems:
LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam){
int len;
(...other declarations go here...)
HWND text_box;
HWND plot_button;
switch (message){
case WM_CREATE:
text_box = CreateWindow("EDIT",
"",
WS_BORDER | WS_CHILD | WS_VISIBLE,
10, 5, 390, 20,
hwnd, (HMENU) 0, NULL, NULL);
plot_button = CreateWindow("BUTTON",
"Plot",
WS_VISIBLE | WS_CHILD | WS_BORDER,
420, 5, 165, 20,
hwnd, (HMENU) 1, NULL, NULL);
break;
case WM_COMMAND:
switch(LOWORD(wParam)){
case 1:
len = GetWindowTextLength(text_box);
printf("%d", len); // a simple console output for testing purposes only, returns 0 disregarding the text length
(...other irrelevant code...)
break;
}
break;
(...other irrelevant code...)
}
Based on a comment by rafix07:
You need to make text_box static. Every time the window procedure is called this variable is created, so when the WM_COMMAND message is processed this variable is uninitialized.
To make text_box static, do this:
static HWND text_box;
I am trying to create a window with behavior similar to cmd.exe, specifically whereby I don't want to support maximizing the window, since I only show fully visible lines of text (vertically). I have come up with two solutions so far:
Solution 1:
case WM_SYSCOMMAND:
if (wParam == SC_MAXIMIZE) {
return 0;
}
return DefWindowProc(hWnd, message, wParam, lParam);
break;
Solution 2:
case WM_SIZE:
if (wParam == SIZE_MAXIMIZED) {
SendMessage(hWnd, WM_SYSCOMMAND, SC_RESTORE, 0);
return 0;
}
break;
Unfortunately, the former is only effective if the user explicitly clicks the maximize button in the title bar, or in a context menu. It won't block it if the user simply double clicks the title bar for example.
The problem with the latter solution, for me, is that it causes scrollbars to disappear until you resize the window manually (by dragging the sides). Also, you can sometimes see the window flash before the window size is restored (I did try disabling redrawing before sending WM_SYSCOMMAND/SC_RESTORE, but unfortunately it did not help much).
Is there a better solution that I'm missing?
case WM_SYSCOMMAND:
UINT SysCommandCode = wParam & 0xFFF0;
if (SysCommandCode == SC_MAXIMIZE) {
return 0;
}
return DefWindowProc(hWnd, message, wParam, lParam);
break;
Also it is recommended to remove WS_MAXIMIZEBOX from the windows style (when creating).
I'm trying to make a program that sends key presses to an inactive window, I'm using "SendMessageCallback()" but it isn't giving an error, or sending the key press.
typedef enum {false,true} bool;
bool KeyDown(HWND hWnd, WORD dKey){
if(!SendMessageCallback(hWnd, WM_KEYDOWN, 0, dKey, (SENDASYNCPROC)NULL, (ULONG_PTR)NULL))
return false;
return true;
}
bool KeyUp(HWND hWnd, WORD uKey){
if(!SendMessageCallback(hWnd, WM_KEYDOWN, 0, uKey, (SENDASYNCPROC)NULL, (ULONG_PTR)NULL))
return false;
return true;
}
then, I simply call it like this:
HWND FOO = FindWindow("FOO", NULL );
if( FOO == NULL )
MessageBox(NULL, "FOO Not Found", "ERROR", MB_OK);
if (!KeyDown(FOO, VK_SPACE))
MessageBox(NULL, "KeyDown failed to execute!", "ERROR", MB_OK);
if (!KeyUp(FOO, VK_SPACE))
MessageBox(NULL, "KeyUp failed to execute!", "ERROR", MB_OK);
I substituted What the real HWND is for FOO, If more information is needed please tell me.
The problem is it doesn't send the key press or show a message box.
Edit: I tried adding a delay between pressing the key down and releasing it and that didn't solve it.
Without knowing anything about the window you are working with, I can only surmise that it is not coded to process keystrokes in its WndProc. You can use Spy++ or similar tool to verify that the messages are actually being sent, but that is no guarantee that they will be processed. You cannot send keystrokes to arbitrary windows that are not expecting them. And BTW, keystroke messages are typically posted to a window's message queue, not sent directly to the window's WndProc, so try using PostMessage() instead.
First of all, sorry for my ignorance of the window creation process. Today is actually the first day of my experimenting with it.
I started to code a text based game a few days ago and I have the main menu, and 3 or 4 different functions that control various things with text. I was then advised to look into Windows API and create a window for the program. I have created the window which can be seen here:
#include <Windows.h>
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE, PWSTR nCmdLine, int nCmdShow)
{
const wchar_t CLASS_NAME[] = L"WindowClass";
WNDCLASS wc = { };
wc.lpfnWndProc = WindowProc;
wc.lpszClassName = CLASS_NAME;
wc.hInstance = hInstance;
RegisterClass(&wc);
HWND hwnd = CreateWindowEx( //This creats a new instance of a window
0,
CLASS_NAME,
L"MyFirstWindow",
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT,
CW_USEDEFAULT,
500,
500,
NULL,
NULL,
hInstance,
NULL);
if(hwnd == 0)
return 0;
ShowWindow(hwnd,nCmdShow);
nCmdShow = 1;
MSG msg = { };
while(GetMessage(&msg,NULL,0,0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
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+5));
EndPaint(hwnd, &ps);
}return 0;
case WM_CLOSE:
{
if(MessageBox(hwnd,L"Do you want to exit?",L"Exit",MB_OKCANCEL)==IDOK)
DestroyWindow(hwnd);
} return 0;
}
return DefWindowProc(hwnd,uMsg,wParam,lParam);
}
This looks a bit messy, but you probably will not need it anyway.
So at this point I have my original program and this program that creates a window. My question is how, or even where, do I put my original program's code so that it can be incorporated into the window.
If you are reading this and thinking I'm a total moron for doing it this way, I'm open to ideas that are a lot simpler than what I'm doing right now.
Your code is the standard boilerplate for creating a window using c and win32 API functions. I recommend that you modify the message pump (it's the while loop in middle calling GetMessage). Instead do this:
Run an infinite loop
Peek a message
If the message is not there, execute your code
Else process messages including the quit message
Here's what the code should look like:
while (1)
{
if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
{
if (msg.message == WM_QUIT)
break;
TranslateMessage(&msg);
DispatchMessage(&msg);
}
else
{
//Your game code
}
}
I also want to point, that while learning game programming using C and calling Win32 API is a worthy goal, you might want to look at XNA game studio.
The reason I am recommending it is because it is easier to learn and you can make much more interesting games faster. Here are a few links to get you started if you are interested.
http://joshua-w-wise78.livejournal.com/
http://www.krissteele.net/blogdetails.aspx?id=72
http://www.codeproject.com/KB/game/xna1.aspx
http://msdn.microsoft.com/en-us/library/bb203894.aspx
If your original program was a console app, that read input and printed output, then you will probably want to get input from your window to implement your game.
Instead of looking at it from the perspective of read user input from stdin then generate output to stdout, you have to think of it from the view of window messaging. So you need to process the WM_KEYDOWN messages, you can then use DrawText() to show the user input in your client area, or you could use a c++ RichEdit control. Once you process the WM_KEYDOWN messages you know what the user has pressed and then your program can do it's thing (maybe being triggered to process an accumalated buffer of characters whenever the WM_KEYDOWN key is equal to the enter key?) and write the output to your client area using DrawText() or send WM_KEYDOWN messages to your richedit window using SendMessage().
I think this is what you meant by a text based game, anyway, you just have to start thinking of doing everything by monitoring windows messages. Anything that the user does to your window, Windows will send a message to it to let it know.
I'm writing an application following this tutorial. I'm aware that this tutorial dates, and as such, I have adapted the code to take into consideration the unicode.
I have a main window which looks like an MDI. Then, I have a View menu which toggles a Toolbar dialog as to be shown and hidden.
When I show the dialog, it is displayed, but the PUSHBUTTONs are not displayed correctly. They only appear when I click my main window again.
Plus, I don't seem to be able to click neither of the PUSHBUTTONs into my toolbar dialog.
The resources (resource.h) are defined as follows (only showing what is relevant to this question):
#define IDD_TOOLBAR 102
#define IDC_PRESS 1000
#define IDC_OTHER 1001
#define ID_VIEW_SHOWTOOLBAR 40002
#define ID_VIEW_HIDETOOLBAR 40003
And the dialog as follows in my .rc file:
IDD_TOOLBAR DIALOGEX 0, 0, 85, 50
STYLE DS_FIXEDSYS | DS_MODALFRAME | WS_CAPTION | WS_POPUP
EXSTYLE WS_EX_TOOLWINDOW
CAPTION L"Toolbar"
FONT 8, "MS Shell Dlg"
BEGIN
PUSHBUTTON L"&Press this button", IDC_PRESS, 7, 7, 70, 14
PUSHBUTTON L"&Or this one", IDC_OTHER, 7, 28, 70, 14
END
And showing it as follows in my WndProc function:
// As a global variable I have my toolbar handler.
HWND g_hToolbar = NULL;
BOOL CALLBACK ToolbarDlgProc(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam) {
switch (Msg) {
case IDC_OTHER:
MessageBoxW(hWnd, L"You just clicked IDC_OTHER!", L"Information", MB_OK | MB_ICONINFORMATION);
break;
case IDC_PRESS:
MessageBoxW(hWnd, L"You just clicked ODC_PRESS!", L"Information", MB_OK | MB_ICONINFORMATION);
break;
default:
return FALSE;
}
return TRUE;
}
LRESULT CALLBACK WndProc(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam) {
switch (Msg) {
case WM_COMMAND:
switch (LOWORD(wParam)) {
case ID_VIEW_HIDETOOLBAR:
ShowWindow(g_hToolbar, SW_HIDE);
break;
case ID_VIEW_SHOWTOOLBAR:
if (NULL == g_hToolbar)
g_hToolbar = CreateDialogW(GetModuleHandle(NULL)
, MAKEINTRESOURCE(IDD_TOOLBAR)
, hWnd
, ToolbarDlgProc);
ShowWindow(g_hToolbar, SW_SHOW);
break;
}
break;
default:
return DefWindowProcW(hWnd, Msg, wParam, lParam);
}
}
And here's the way I handle the different messages for my main window and my dialog in my message loop in my WinMain function.
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd) {
// Declaring, registring and creating my main window to hWnd here...
MSG Msg;
ShowWindow(hWnd, nShowCmd);
UpdateWindow(hWnd);
while (GetMessageW(&Msg, hWnd, 0, 0) > 0) {
if (!IsDialogMessageW(g_hToolbar, &Msg)) {
TranslateMessage(&Msg);
DispatchMessageW(&Msg);
}
}
}
My problem is:
I don't seem to be able to click on my dialog's buttons.
When I attempt to click on my dialog's buttons, my main window becomes very slow to respond to its own messages.
That is, when I want to show my Toolbar dialog as a modeless dialog, because when I show it modal, it works perfectly!
Any clue to solve this issue?
Thanks!
The problem is, as DReJ said in the above comment, in my message pump.
The trouble is that I write:
while (GetMessageW(&Msg, hWnd, 0, 0) > 0) {
// Processing message here...
}
And that I shall write:
while (GetMessageW(&Msg, NULL, 0, 0) > 0) {
// Processing message here...
}
So, because I was getting the messages for a given window, the hWnd instance, my ToolbarDialog seemed to lack of time to draw itself completely or something like it. Replacing hWnd for NULL in that scenario solved the problem entirely.