4

It's easy to get system menu on console application (GetSystemMenu) and add some own entries (AppendMenu). But then these menu items are useless for the app. Is there any way to get into message stream that would identify what menu item was clicked ?

I've tried to hook to console window but without any result, I mean the WH_SYSMSGFILTER, all is compiling ok but there are no messages shown the hook function is not run by the system.

Next thing was ReadConsoleInput and this works partially, that is it shows mouse events on the system menu, but there is no information in MENU_EVENT_RECORD structure about what menu item was clicked.

These are my attempts all in one snippet, here the console should be flooded with messages, but only those from ReadConsoleInput appear, but these doesn't contain any useful information. No matter if user clicks on first or second added menu item there are only two codes shown 278 (0x116) WM_INITMENU and 287 (0x11F) WM_MENUSELECT, but there is no way I know to get to the wParam of WM_MENUSELECT message.

#include <windows.h>
#include <stdio.h>

HHOOK sysMsgFilterHook;
LRESULT CALLBACK SysMsgFilterCallback(int nCode, WPARAM wParam, LPARAM lParam) {
  printf("%i\n", nCode);
  return CallNextHookEx(NULL, nCode, wParam, lParam);
}

static LRESULT CALLBACK consoleWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
  printf("%u\n", uMsg);
  WNDPROC origProc = (WNDPROC) GetProp(hWnd, "origProc");
  return CallWindowProc(origProc, hWnd, uMsg, wParam, lParam );
}


int main() {
  SetLastError(0);
  HWND console_hwnd = GetConsoleWindow();
  HMENU console_hMenu = GetSystemMenu(console_hwnd, FALSE);
  HINSTANCE console_hinstance = (HINSTANCE)GetWindowLong(console_hwnd, GWL_HINSTANCE);
  DWORD console_processid = GetWindowThreadProcessId(console_hwnd, NULL);
  HANDLE console_input_handle = GetStdHandle(STD_INPUT_HANDLE);

  AppendMenu(console_hMenu, MF_STRING | MF_CHECKED, NULL, "test menu item");
  AppendMenu(console_hMenu, MF_STRING | MF_CHECKED, NULL, "yet another menu item");

  WNDPROC origProc = (WNDPROC)SetWindowLongPtr(console_hwnd, GWL_WNDPROC, (LONG_PTR)&consoleWndProc);
  SetProp(console_hwnd, "origProc", (HANDLE)origProc);

  sysMsgFilterHook = SetWindowsHookEx(
    WH_SYSMSGFILTER,
    (HOOKPROC)SysMsgFilterCallback,
    console_hinstance,
    console_processid
  );

  DWORD numEvents = 0;
  INPUT_RECORD input;

  while(ReadConsoleInput(console_input_handle, &input, 1, &numEvents)) {
    //printf("input.EventType: %i\n", input.EventType);
    if (input.EventType == MENU_EVENT) {
      printf("input.Event.MenuEvent.dwCommandId %i\n", input.Event.MenuEvent.dwCommandId);
    }
  }
  //printf("GetLastError: %lu\n", GetLastError());
  UnhookWindowsHookEx(sysMsgFilterHook);
  system("pause");
  return 0;
}

I've succeeded with creating hook for mouse events, that is WH_MOUSE_LL. But all other hooks do not work.

What I intend to accomplish is to get some sort of WM_MENUCOMMAND message and then get rest with GetMenuItemInfo.

I've heard that the hooking procedure should be in another dll, but how to do that ? are there any working snippets ?

rsk82
  • 28,217
  • 50
  • 150
  • 240
  • You'll want to hook calls to the window procedure (`WH_CALLWNDPROC`). – Captain Obvlious Dec 17 '13 at 22:55
  • @CaptainObvlious: I've tried that , no effect. – rsk82 Dec 17 '13 at 22:59
  • 2
    This can't work, the console window is owned by a different process. You're well beyond what a console was made to do, time to progress to a Real Window. – Hans Passant Dec 17 '13 at 23:16
  • Your app runs in a separate process then the console, so you MUST implement your hooks and window procedure inside of a DLL so they can be injected into the address space of the console process. I ran your code as-is, and `ReadConsoleInput()` had no trouble reporting `MENU_EVENT` notifications for the custom menu items, without having to use any hooks at all, but `GetMenuItemInfo()` always fails with error 1456 (`ERROR_MENU_ITEM_NOT_FOUND`) for the reported menu item ID. – Remy Lebeau Dec 17 '13 at 23:19
  • I found this snipped: http://stackoverflow.com/questions/820804/how-to-hook-external-process-with-setwindowshookex-and-wh-keyboard - author says it works. I think it is supposed to compile in VS. Because now in gcc I get bunch of errors like: `'__in' was not declared in this scope` etc. – rsk82 Dec 18 '13 at 00:25
  • This is not supported, even if you find a way to get it to work. Make sure your customers understand this – Raymond Chen Dec 18 '13 at 02:23
  • maybe maybe... http://borland.newsgroups.archived.at/public.delphi.nativeapi.win32/200611/0611306948.html - *ReadConsoleInput was the wrong track. I finally found the way to detect the new items I added to the system menu using a hook installed with SetWinEventHook* – rsk82 Dec 18 '13 at 10:04

0 Answers0