Trying Subclassing on Console - c

I want to trap keyboard messages in a console application, so I tried this:
HWND GetConsoleHwnd(void)
{
#define SIZEBUF 1024
char szBuffer[SIZEBUF];
GetConsoleTitle(szBuffer, SIZEBUF);
#undef SIZEBUF
return FindWindow(NULL, szBuffer);
}
LRESULT CALLBACK ConsoleProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch (msg)
{
case WM_KEYDOWN:
switch (wParam)
{
//VK Cases
}
break;
}
return CallWindowProc(OldConsoleProc, hwnd, msg, wParam, lParam);
}
this in main:
HWND hwndConsole = GetConsoleHwnd();
OldConsoleProc = (WNDPROC) SetWindowLong(hwndConsole, GWL_WNDPROC,
ConsoleProc);
and this Global Var: WNDPROC OldConsoleProc;
but it doesnt work, what I am doing wrong?

You can't subclass a window of another process this way. You can do it with hooks but I wouldn't recommend trying this on console window. ReadConsoleInput is low-level enough, and it's as far as you can get without ugly nonportable hacks (I'm not even sure there are some events reaching WndProc when the console window is full screen).

Related

Menus in WinAPI: Do I use LPARAM or WPARAM for WM_COMMAND?

I've recently started teaching myself WinAPI, so bear with me here.
Compiler: TDM-GCC 4.9.2
OS: Windows 8.1 64-bit
I've recently learned how to create menus using resources. In regards to that, I've noticed something odd about handling the WM_COMMAND message for menus. The MSDN documentation tells me that if I want to handle a message sent from a menu, the command item ID can be found in the low word of WPARAM; so I assumed that the code in my window procedure would look like this:
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, LPARAM lParam, WPARAM wParam)
{
switch (msg)
{
case WM_COMMAND:
switch (LOWORD(wParam)) //<--
{
case /*IDs go here*/:
break;
}
break;
//...
}
return 0;
}
However, I noticed that the command items in the menu weren't working. After some debugging, I figured out that wParam was always 0 and my IDs were actually in lParam! I made the quick change to the following code:
switch (lParam)
{
case /*IDs go here*/:
break;
}
And it works!
My question: why?
How come the documentation says it's in wParam when for me it's in lParam?
Also, here are my resource.rc and resource.h files in case it helps:
"resource.h":
#define IDR_MYMENU 101
//These are appearing in LPARAM instead of WPARAM
#define ID_FILE_EXIT 9001
#define ID_STUFF_GO 9002
"resource.rc":
#include "resource.h"
IDR_MYMENU MENU
BEGIN
POPUP "&File"
BEGIN
MENUITEM "E&xit", ID_FILE_EXIT
END
POPUP "&Stuff"
BEGIN
MENUITEM "&Go", ID_STUFF_GO
MENUITEM "G&o somewhere else", 0, GRAYED
END
END
Edit 7/23/15:
SOLVED. My window procedure had the wrong signature. Can't believe it was something so trivial! Thanks, cremno!
The WPARAM and LPARAM parameter are not in the correct order in your code:
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, LPARAM lParam, WPARAM wParam)
From the WindowProc callback function MSDN documentation:
LRESULT CALLBACK WindowProc(
_In_ HWND hwnd,
_In_ UINT uMsg,
_In_ WPARAM wParam,
_In_ LPARAM lParam
);
Just swap them to fix your code:
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
Your WM_COMMAND message handling should check the high-order WORD of wParam first. Because this value tells you if it is a Menu, Accelerator or child control which is sending the message.
swtich(msg)
{
case WM_COMMAND:
{
switch(HIWORD(wParam))
{
case 0: // Menu
{
// Check LOWORD(wParam) here
break;
}
}
return 0;
}
See the table on the MSDN documentation: WM_COMMAND message

creating a window with createwindow() when clicking a menu command

I want to create a window with CreateWindow() when clicking on a menu item that will be a child of the main window. I know I can use DialogBox() or CreateDialog() but I want to use CreateWindow(). I'm using this code
resource.rc file
#include "resource.h"
IDM_MENU MENU
{
POPUP "&Help"
{
MENUITEM "&About", IDM_HELP
}
}
About window procedure
LRESULT CALLBACK AboutProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch(msg)
{
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hwnd, msg, wParam, lParam);
}
return 0;
}
Main window procedure
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch(msg)
{
case WM_COMMAND:
switch(LOWORD(wParam))
{
case IDM_HELP:
{
WNDCLASSEX wc;
HWND hDlg;
MSG msg;
SecureZeroMemory(&wc, sizeof(WNDCLASSEX));
wc.cbSize = sizeof(WNDCLASSEX);
wc.hbrBackground = (HBRUSH)(COLOR_BTNFACE+1);
wc.hCursor = LoadCursor(0, IDC_ARROW);
wc.hIcon = (HICON)GetClassLong(hwnd, GCL_HICON);
wc.hIconSm = (HICON)GetClassLong(hwnd, GCL_HICONSM);
wc.hInstance = GetModuleHandle(0);
wc.lpfnWndProc = AboutProc;
wc.lpszClassName = TEXT("AboutClass");
if(!RegisterClassEx(&wc))
break;
hDlg = CreateWindowEx(0, wc.lpszClassName, TEXT("About"), WS_OVERLAPPEDWINDOW, 0, 0, 300, 200, hwnd, 0, wc.hInstance, 0);
ShowWindow(hDlg, SW_SHOWNORMAL);
while(GetMessage(&msg, 0, 0, 0) > 0)
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
UnregisterClass(wc.lpszClassName, wc.hInstance);
}
break;
}
break;
default:
return DefWindowProc(hwnd, msg, wParam, lParam);
}
return 0;
}
Is this a good idea? Can you register more than one class to the same instance? Also, is it a good idea to assign the main window icons to this child window or should I load them each time? Will those icons be deleted when I call UnregisterClass() in IDM_HELP? I have tried this program and everything works and the icons still show in the main window after I close this child window. But I still wonder if it's ok to assign the main window icons to this window since I call UnregisterClass() after the child window closes
There is nothing wrong with using CreateWindow/Ex() instead of CreateDialog()/DialogBox(). And there is nothing wrong with running your own modal message loop, as long as you implement it correctly. For instance, take heed of this warning:
Modality, part 3: The WM_QUIT message
The other important thing about modality is that a WM_QUIT message always breaks the modal loop. Remember this in your own modal loops! If ever you call the PeekMessage function or the GetMessage function and get a WM_QUIT message, you must not only exit your modal loop, but you must also re-generate the WM_QUIT message (via the PostQuitMessage message) so the next outer layer will see the WM_QUIT message and do its cleanup as well. If you fail to propagate the message, the next outer layer will not know that it needs to quit, and the program will seem to "get stuck" in its shutdown code, forcing the user to terminate the process the hard way.
The example you showed is not doing that, so you would need to add it:
ShowWindow(hDlg, SW_SHOWNORMAL);
do
{
BOOL bRet = GetMessage(&msg, 0, 0, 0);
if (bRet > 0)
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
else
{
if (bRet == 0)
PostQuitMessage(msg.wParam); // <-- add this!
break;
}
}
while (1);
UnregisterClass(wc.lpszClassName, wc.hInstance);
However, your modal window should NOT be using WM_QUIT just to break its modal loop, as doing so would exit you entire application! Use a different signal to make your modal loop break when the window is closed. For example:
ShowWindow(hDlg, SW_SHOWNORMAL);
while (IsWindow(hDlg) && (GetMessage(&msg, 0, 0, 0) > 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
LRESULT CALLBACK AboutProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch(msg)
{
case WM_CLOSE:
DestroyWindow(hwnd);
break;
default:
return DefWindowProc(hwnd, msg, wParam, lParam);
}
return 0;
}
Also, a modal window is supposed to disable its owner window, and then re-enable it when closed. Your example is not doing that either, so that needs to be added as well:
ShowWindow(hDlg, SW_SHOWNORMAL);
EnableWindow(hwnd, FALSE); // <-- add this
...
LRESULT CALLBACK AboutProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch(msg)
{
case WM_CLOSE:
EnableWindow(GetWindow(hwnd, GW_OWNER), TRUE); // <-- add this
DestroyWindow(hwnd);
break;
default:
return DefWindowProc(hwnd, msg, wParam, lParam);
}
return 0;
}
The Old New Thing blog has a whole series of posts on how to work with modal windows.
Modality, part 1: UI-modality vs code-modality
Modality, part 2: Code-modality vs UI-modality
Modality, part 3: The WM_QUIT message
Modality, part 4: The importance of setting the correct owner for modal UI
Modality, part 5: Setting the correct owner for modal UI
Modality, part 6: Interacting with a program that has gone modal
Modality, part 7: A timed MessageBox, the cheap version
Modality, part 8: A timed MessageBox, the better version
Modality, part 9: Setting the correct owner for modal UI, practical exam
The correct order for disabling and enabling windows
Make sure you disable the correct window for modal UI
Update: based on your comment that "No I don't want a modal window", you can ignore everything said above. All of that applies to modal windows only. Since you do not want a modal window, simply remove the secondary loop altogether and let the main message loop handle everything. Also, you do not need to call UnregisterClass(). It will unregister automatically when the process ends. Call RegisterClass() one time, either at program startup, or at least the first time you display the About window. You can use GetClassInfo/Ex() to know whether the class is already registered, or keep track of it yourself. Think of what happens if the user wants to display the About window more than one time during the process's lifetime. So let it re-use an existing class registration each time.
Try this:
LRESULT CALLBACK AboutProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch(msg)
{
case WM_CLOSE:
DestroyWindow(hwnd);
break;
default:
return DefWindowProc(hwnd, msg, wParam, lParam);
}
return 0;
}
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch(msg)
{
case WM_COMMAND:
switch(LOWORD(wParam))
{
case IDM_HELP:
{
WNDCLASSEX wc = {0};
wc.cbSize = sizeof(WNDCLASSEX);
wc.hInstance = GetModuleHandle(0);
wc.lpszClassName = TEXT("AboutClass");
if (!GetClassInfoEx(wc.hInstance, wc.lpszClassName, &wc))
{
wc.hbrBackground = (HBRUSH)(COLOR_BTNFACE+1);
wc.hCursor = LoadCursor(0, IDC_ARROW);
wc.hIcon = (HICON)GetClassLong(hwnd, GCL_HICON);
wc.hIconSm = (HICON)GetClassLong(hwnd, GCL_HICONSM);
wc.lpfnWndProc = AboutProc;
if (!RegisterClassEx(&wc))
break;
}
HWND hDlg = CreateWindowEx(0, wc.lpszClassName, TEXT("About"), WS_OVERLAPPEDWINDOW, 0, 0, 300, 200, hwnd, 0, wc.hInstance, 0);
if (hDlg)
ShowWindow(hDlg, SW_SHOWNORMAL);
}
break;
}
break;
default:
return DefWindowProc(hwnd, msg, wParam, lParam);
}
return 0;
}
Yes, you can use CreateWindow(). You can do anything in your event handler. Using DialogBox() just gives you a modal loop for free (so your main window cannot be interacted with until the dialog box is closed).
Yes, you can register multiple window classes. You are free to register all your window classes in advance; you do not need to call RegisterClass() and UnregisterClass() each time someone clicks your menu item.
I'm not sure if UnregisterClass() frees the various GDI resources allocated to your window class; anyone who knows the answer is free to comment.

How do I disable the 'Close Window' option in the taskbar?

How to write a C Code in order to disable the 'Close Window' option in the taskbar?
Compiler: GCC-mingw32
Which API Function should I use?
Thanks.
Make the window's message dispatcher process the message WM_SYSCOMMAND and filter it out if the message's wParam is SC_CLOSE.
Let WndProc be the windows message handler then the code to do might look like this:
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
...
switch (message)
{
case WM_SYSCOMMAND:
if (wParam == SC_CLOSE);
break;
return DefWindowProc(hWnd, message, wParam, lParam);
case ...
}
return 0;
}
This does not remove the "close"-menu item from the application's jump-list (its taskbar's local menu), but disables its functionality.

Use WH_MOUSE globally without DLL

I have a code to display a part of screen when mouse moves. But the WH_MOUSE doesn't work. I need to change GetModuleHandle(0), 0 to hInst, GetCurrentThreadId().
But then the application will work only when the mouse is over the application itself.
I want it global and I tried WH_MOUSE_LL, it is slower then WH_MOUSE.
Is that possible to use WH_MOUSE globally without DLL?
void SetHook()
{
gMouseHook = SetWindowsHookEx(WH_MOUSE, MouseProc, GetModuleHandle(0), 0);
}
//================================================================================
// Mouse Hook
static LRESULT CALLBACK MouseProc(int nCode, WPARAM wParam, LPARAM lParam)
{
if (nCode < 0) {
return CallNextHookEx(gMouseHook, nCode, wParam, lParam);
}
if (wParam == WM_MOUSEMOVE) {
MOUSEHOOKSTRUCT *mouseInfo = (MOUSEHOOKSTRUCT*)lParam;
int x = mouseInfo->pt.x;
int y = mouseInfo->pt.y;
PrintScreen(x, y);
}
return CallNextHookEx(gMouseHook, nCode, wParam, lParam);
}
Is that possible to use WH_MOUSE globally without DLL?
No, the hook procedure needs to be in a DLL so that it can be injected into other processes.
I tried WH_MOUSE_LL, it is slower then WH_MOUSE.
That probably means your hook procedure is slow.

Why doesn't SAPI's ISpNotifySource::SetNotifyWindowMessage() send the notification to my WndProc()?

I'm using WinForms, and I'm trying to get SetNotifyWindowMessage() to send a message to the WndProc, but it does not do so.
The function call:
HRESULT initSAPI(HWND hWnd)
{
...
if(FAILED( g_cpRecoCtxt->SetNotifyWindowMessage( hWnd, WM_RECOEVENT, 0, 0 )))
MessageBoxW(hWnd, L"Error sending window message", L"SAPI Initialization Error", 0);
...
}
The WndProc:
LRESULT WndProc (HWND hWnd, UINT message, WPARAM wparam, LPARAM lparam)
{
case WM_RECOEVENT:
ProcessRecoEvent(hWnd);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
Note: initSAPI() is called on a mouse click event.
Have you called ISpRecoContext::SetInterest as well? SetNotifyWindowMessage specifies what to do when an event occurs, but doesn't specify what events to listen for.

Resources