C++
--
Questions
--
Followers
Top Experts
I just bought an IBM Thinkpad T-42, and found a couple of useless "Web Forward" and "Web Backwards" that I wrote a program to remap to Music Forwards and Music Backwards, something I use much more. Â The program sets a keyboard hook with SetWindowsHookEx. Â The standalone program set the hook, then popped up a message box, during which everything worked fine. Â Once you clicked on the message box, the procedure would unhook and then the program finished. Â Great for debugging and testing, now to make something useful...
So I went ahead and modified the program to be a Windows Service. Â The actual keyboard hook is in a DLL. Â Everything service-wise is working fine (i.e. Starting, Stopping, etc.), but the hook no longer works! Â I did not change the code of how the hook was set (into a DLL), so I'm confused why this part no longer works. Â The reason I know it doesn't work, is that I have some logging code, and I added a log to the top of the Keyboard Hook function that traps the keys to just say that the function was called... and it never gets called.
Any ideas?
Here's some code (parts taken out that I thought were irrelevant).
First, the DLL with the hook procedure:
[code]
BYTE vk_next = VK_MEDIA_NEXT_TRACK,
      vk_prev = VK_MEDIA_PREV_TRACK;
int WINAPI DllMain(HINSTANCE hInstance, DWORD fdwReason, PVOID pvReserved)
{
      Log("DllMain: called");
      return TRUE;
}
...
LRESULT CALLBACK LowLevelKeyboardProc(int nCode,WPARAM wParam,LPARAM lParam)
{
      Log("LowLevelKeyboardProc:
      BOOL bSuppress = FALSE;
...
      return (bSuppress ? TRUE : CallNextHookEx(NULL, nCode, wParam, lParam));
}
[/code]
And then the service:
[code]
#define DLL_LOCATION "ibmremaplib.dll"
#define SERVICE_NAME "IBM Keyboard Remapper"
HOOKPROC hkprcSysMsg;
static HINSTANCE hinstDLL;
static HHOOK hhookSysMsg;
SERVICE_STATUS Â Â Â Â Â ServiceStatus;
SERVICE_STATUS_HANDLE Â hStatus;
HHOOK Start();
BOOL Stop();
void GetErrorString(LPVOID lpMsgBuf);
void ServiceMain(int argc, char** argv);
void ControlHandler(DWORD request);
HHOOK Start()
{
      hhookSysMsg = SetWindowsHookEx(WH_KEYBOA
      return hhookSysMsg;
}
BOOL Stop()
{
      return UnhookWindowsHookEx(hhookS
}
int WINAPI WinMain(HINSTANCE hinst, HINSTANCE prevInstance, PSTR pszCmdLine, int iCmdShow)
{
      LPVOID lpMsgBuf = NULL;
      hinstDLL = LoadLibrary((LPCTSTR) DLL_LOCATION);
      if(!hinstDLL)
      {
           GetErrorString(&lpMsgBuf);
           //MessageBox(NULL, (LPTSTR)lpMsgBuf, TEXT("Error1"), 0);
           Log((PTSTR) lpMsgBuf);
           return S_FALSE;
      }
      hkprcSysMsg = (HOOKPROC)GetProcAddress(h
      if(!hkprcSysMsg)
      {
           GetErrorString(&lpMsgBuf);
           Log((PTSTR)lpMsgBuf);
           return S_FALSE;
      }
      Log("WinMain: called");
      SERVICE_TABLE_ENTRY ServiceTable[2];
      ServiceTable[0].lpServiceN
      ServiceTable[0].lpServiceP
      ServiceTable[1].lpServiceN
      ServiceTable[1].lpServiceP
      // Start the control dispatcher thread for our service
      StartServiceCtrlDispatcher
      LocalFree(lpMsgBuf);
      return S_OK;
}
void GetErrorString(void *lpMsgBuf)
{
      FormatMessage(FORMAT_MESSA
           NULL,
           GetLastError(),
           MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
           (LPTSTR) lpMsgBuf,
           0,
           NULL
      );
}
void ServiceMain(int argc, char** argv)
{
      ServiceStatus.dwServiceTyp
      ServiceStatus.dwCurrentSta
      ServiceStatus.dwControlsAc
      ServiceStatus.dwWin32ExitC
      ServiceStatus.dwServiceSpe
      ServiceStatus.dwCheckPoint
      ServiceStatus.dwWaitHint = 0;
      hStatus = RegisterServiceCtrlHandler
           SERVICE_NAME,
           (LPHANDLER_FUNCTION)Contro
      if(hStatus == (SERVICE_STATUS_HANDLE)0)
      {
           // Registering Control Handler failed
           Log(TEXT("Registering Control Handler failed."));
           return;
      }
      // We report the running status to SCM.
      ServiceStatus.dwCurrentSta
      SetServiceStatus (hStatus, &ServiceStatus);
      if(!Start())
      {
           Log(TEXT("ServiceMain: Could not hook!"));
      }
      else
      {
           TCHAR msg[50];
           Log(TEXT("ServiceMain: Started"));
           sprintf(msg, "%0.8X", hhookSysMsg);
           Log(msg);
      }
      while(ServiceStatus.dwCurr
      {
           Sleep(5000);
      }
}
void ControlHandler(DWORD request)
{
      switch(request)
      {
      case SERVICE_CONTROL_STOP:
      case SERVICE_CONTROL_SHUTDOWN:
           if(!Stop())
           {
                 Log(TEXT("ControlHandler: Could not unhook!"));
           }
           else Log(TEXT("ControlHandler: Stopped."));
           ServiceStatus.dwWin32ExitC
           ServiceStatus.dwCurrentSta
           SetServiceStatus (hStatus, &ServiceStatus);
           return;
      }
      // Report current status
      SetServiceStatus (hStatus, &ServiceStatus);
}
[/code]
Thanks for any help!
Kevin Grigorenko
Zero AI Policy
We believe in human intelligence. Our moderation policy strictly prohibits the use of LLM content in our Q&A threads.
Kevin
In fact, you should check the error code for every Windows API call. The error might not be in the Start() function.






EARN REWARDS FOR ASKING, ANSWERING, AND MORE.
Earn free swag for participating on the platform.
Thats the exact problem. Hooks execute in *user* contexts, not in services' context and they do not even share the same window station. I'd rather recommend to start these hooks like 'Autorun' apps, which ensures the proper context. BTW, what's your program supposed to do exactly?
Ahh, interesting. Â I wouldn't mind just running it in autorun, but how do I just let it sit there without a while(1) loop, which obviously doesn't work. Â Do I need to use a semaphore or something? Â I really just need to give my hook to windows and that's it. Â My program doesn't need to do anything else.
Here's the actual code, like I mentioned it maps a few keys on my thinkpad keyboard to different keys (in this case multimedia playlist forward and backward keys):
      BOOL bSuppress = FALSE;
      if(nCode == HC_ACTION) {
           switch (wParam) {
           case WM_KEYDOWN:
           case WM_SYSKEYDOWN:
           case WM_KEYUP:
           case WM_SYSKEYUP:
                 PKBDLLHOOKSTRUCT p = (PKBDLLHOOKSTRUCT) lParam;
                 bSuppress = NeedsRemap(p->vkCode);
                 if(bSuppress)
                 {
                      DWORD dwFlags = 0;
                      if(wParam == WM_KEYUP ||
                            wParam == WM_SYSKEYUP)
                      {
                            dwFlags |= KEYEVENTF_KEYUP;
                      }
                      // inject the appropriate logic:
                      // below is specific logic for the backward/forward
                      // buttons
                      if(p->scanCode != 0) /* quirk; see preamble */
                      {
                            if(p->vkCode == VK_IBM_MEDIA_PREV_TRACK)
                            {
                                 // backwards key
                                 keybd_event(vk_prev, 0, dwFlags, 0);
                            }
                            else if(p->vkCode == VK_IBM_MEDIA_NEXT_TRACK)
                            {
                                 // forwards key
                                 keybd_event(vk_next, 0, dwFlags, 0);
                            }
                      }
                 }
                 break;
           }
      }
Thanks for the help,
Kevin
Kevin

Get a FREE t-shirt when you ask your first question.
We believe in human intelligence. Our moderation policy strictly prohibits the use of LLM content in our Q&A threads.
>>which obviously doesn't work
That would ideed be a bad idea. All you need is a windowless app that installs the hook and the just waits for an event to terminate, e.g.
int PASCAL WinMain      (      HANDLE      hInstance,
                                 HANDLE      hPrevInst,
                                 LPSTR      lpszCmdLine,
                                 int            cmdShow
                            )
{
HANDLE Â Â Â Â Â Â Â Â Â Â Â hev;
      if      (      !(      hev      =      CreateEvent      (      NULL,     Â
                                                       FALSE,     Â
                                                       FALSE,     Â
                                                       MYAPP_EVENT_NAME
                                                  )
                 )
           )
           {
                 if      (      ERROR_ALREADY_EXISTS      ==      GetLastError      ())
                      {
                            if      (      !(      hev      =      OpenEvent      (      EVENT_ALL_ACCESS,     Â
                                                                             FALSE,     Â
                                                                             MYAPP_EVENT_NAME
                                                                        )
                                       )
                                 )
                                 {
                                       return      (      -1);
                                 }
                      }
                 else     Â
                      {
                            return      (      -2);
                      }
           }
      InstallHook();
      WaitForSingleObject      (      hev,      INFINITE);
      CloseHandle      (      hev);
}
And InstallHook() needs to be on its own thread, correct? Â Because I assume WaitForSingleObject is synchronous, and even if my hook call is in a separate DLL, the only thread of execution will be waiting, right?
Thanks,
Kevin
Thanks,
Kevin






EARN REWARDS FOR ASKING, ANSWERING, AND MORE.
Earn free swag for participating on the platform.
>>hypothesis I mentioned in the previous post).
Err, no - that function is supposed to reside in the hook DLL. Here's a sample taken out of one of my previous projects:
LONG __DYNLINK InstallHook  ( void)
{
  HANDLE  hev;
  if  (  g_hhk)  return  (  ERROR_ALREADY_EXISTS);
  g_hhk  =  SetWindowsHookEx   (  WH_GETMESSAGE,
                    ( HOOKPROC) HookProc,
                    g_hThisDll,
                    0
                  );
  if  (  !(  hev =  CreateEvent (  NULL, Â
                    FALSE, Â
                    FALSE, Â
                    MYAPP_EVENT_NAME
                  )
      )
    )
    {
      if  (  ERROR_ALREADY_EXISTS   ==  GetLastError   ())
        {
          if  (  !(  hev =  OpenEvent  (  EVENT_ALL_ACCESS, Â
                            FALSE, Â
                            MYAPP_EVENT_NAME
                          )
              )
            )
            {
              return  (  -1);
            }
        }
       else Â
        {
          return  (  -2);
        }
    }
  WaitForSingleObject (  hev,   INFINITE);
  Sleep  (  5000);
  return  (  0);
}
LONG __DYNLINK TerminateHook   ( void)
{
  HANDLE  hev;
  UnhookWindowsHookEx (  g_hhk);
  Sleep  (  5000);
  if  (  !(  hev =  OpenEvent  (  EVENT_ALL_ACCESS, Â
                    FALSE, Â
                    MYAPP_EVENT_NAME
                  )
      )
    )
    { Â
      return  (  GetLastError   ());
    }
  PulseEvent  (  hev);
  return  (  0);
}
First, the DLL:
>>Â The DLL header, "ibmremaplib.h":
#ifndef IBMREMAPLIB_H
#define IBMREMAPLIB_H
// A few #define's because these are only supported for
// Win2k and above
#ifndef VK_MEDIA_NEXT_TRACK
#define VK_MEDIA_NEXT_TRACK 0xB0
#endif
#ifndef VK_MEDIA_PREV_TRACK
#define VK_MEDIA_PREV_TRACK 0xB1
#endif
// The virtual key codes found on my T-42.
#define VK_IBM_MEDIA_PREV_TRACK 0xA6
#define VK_IBM_MEDIA_NEXT_TRACK 0xA7
#define MYAPP_EVENT_NAME "IBMKeyboardRemapper"
LONG InstallHook();
LONG TerminateHook();
#endif
>>Â The DLL source def file:
LIBRARY ibmremaplib
EXPORTS
LowLevelKeyboardProc
InstallHook
TerminateHook
>>Â The DLL source file, "ibmremaplib.cpp"
#define WIN32_LEAN_AND_MEAN Â Â Â Â Â Â Â Â Â Â Â // Exclude rarely-used stuff from Windows headers
#define _WIN32_WINNT 0x0400
#include <windows.h>
#include "ibmremaplib.h"
BYTE vk_next = VK_MEDIA_NEXT_TRACK,
      vk_prev = VK_MEDIA_PREV_TRACK;
static HHOOK g_hhk = NULL;
static HINSTANCE g_hThisDll;
int WINAPI DllMain(HINSTANCE hInstance, DWORD fdwReason, PVOID pvReserved)
{
      g_hThisDll = hInstance;
      return TRUE;
}
/*
 * Utility function to see if we will be dealing
 * with this key.
 */
BOOL NeedsRemap(DWORD key)
{
      if(key == VK_IBM_MEDIA_PREV_TRACK ||
           key == VK_IBM_MEDIA_NEXT_TRACK)
      {
           return TRUE;
      }
      return FALSE;
}
/*
 * The procedure we hook into Windows.
 */
LRESULT CALLBACK LowLevelKeyboardProc(
      int nCode,
      WPARAM wParam,
      LPARAM lParam
)
{
      // If this flag is set, the current key
      // will be suppressed. (Except in the case
      // that processing takes longer than the timeout
      // values defined in HKEY_CURRENT_USER\Control Panel\Desktop)
      BOOL bSuppress = FALSE;
      if(nCode == HC_ACTION) {
           switch (wParam) {
           case WM_KEYDOWN:
           case WM_SYSKEYDOWN:
           case WM_KEYUP:
           case WM_SYSKEYUP:
                 PKBDLLHOOKSTRUCT p = (PKBDLLHOOKSTRUCT) lParam;
                 bSuppress = NeedsRemap(p->vkCode);
                 if(bSuppress)
                 {
                      DWORD dwFlags = 0;
                      if(wParam == WM_KEYUP ||
                            wParam == WM_SYSKEYUP)
                      {
                            dwFlags |= KEYEVENTF_KEYUP;
                      }
                      // inject the appropriate logic:
                      // below is specific logic for the backward/forward
                      // buttons
                      if(p->scanCode != 0) /* quirk; see preamble */
                      {
                            if(p->vkCode == VK_IBM_MEDIA_PREV_TRACK)
                            {
                                 // backwards key
                                 keybd_event(vk_prev, 0, dwFlags, 0);
                            }
                            else if(p->vkCode == VK_IBM_MEDIA_NEXT_TRACK)
                            {
                                 // forwards key
                                 keybd_event(vk_next, 0, dwFlags, 0);
                            }
                      }
                 }
                 break;
           }
      }
      return (bSuppress ? TRUE : CallNextHookEx(NULL, nCode, wParam, lParam));
}
LONG InstallHook()
{
      HANDLE hev;
      if(g_hhk) return(ERROR_ALREADY_EXIST
      g_hhk = SetWindowsHookEx(
           WH_KEYBOARD_LL,
           (HOOKPROC) LowLevelKeyboardProc,
           g_hThisDll,
           0
      );
      if(!(hev = CreateEvent(NULL, FALSE,      FALSE, MYAPP_EVENT_NAME)))
      {
           if(GetLastError() == ERROR_ALREADY_EXISTS)
           {
                 if(!(hev = OpenEvent(EVENT_ALL_ACCESS
                 {
                      return(-1);
                 }
           }
           else
           {
                 return(-2);
           }
      }
      WaitForSingleObject(hev, INFINITE);
      Sleep(5000);
      return(0);
}
LONG TerminateHook()
{
      HANDLE hev;
      UnhookWindowsHookEx(g_hhk)
      Sleep(5000);
      if(!(hev = OpenEvent(EVENT_ALL_ACCESS
      {
           return(GetLastError());
      }
      PulseEvent(hev);
      return(0);
}
And finally, the driver program, "ibmremap.cpp":
#define WIN32_LEAN_AND_MEAN Â Â Â Â Â Â Â Â Â Â Â // Exclude rarely-used stuff from Windows headers
#define _WIN32_WINNT 0x0400
#include <windows.h>
#include <process.h>
#include "ibmremaplib.h"
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE prevInstance, PSTR pszCmdLine, int iCmdShow)
{
  HANDLE hev;
  if(!(hev = CreateEvent(NULL,
              FALSE,
              FALSE,
              MYAPP_EVENT_NAME
             )))
  {
    if(GetLastError() == ERROR_ALREADY_EXISTS)
    {
      if(!(hev = OpenEvent(EVENT_ALL_ACCESS
                 FALSE,
                 MYAPP_EVENT_NAME
                )))
      {
        return(-1);
      }
    }
    else
    {
      return(-2);
    }
  }
      InstallHook();
      WaitForSingleObject(hev, INFINITE);
      TerminateHook();
        CloseHandle(hev);
      return S_OK;
}
Thanks,
Kevin
Thanks so much! Â I'm halfway there.
Kevin Grigorenko

Get a FREE t-shirt when you ask your first question.
We believe in human intelligence. Our moderation policy strictly prohibits the use of LLM content in our Q&A threads.
Actually, you can remove a message in a WH_GETMESSAGE hook by using
MSG* pMsg = (MSG*) lParam;
MSG msg;
PeekMessage(&msg,pMsg->hWn
1. The PeekMessage remove doesn't work. Â Here is how my hook ends:
      if(bSuppress)
      {
           MSG tempMsg;
           PeekMessage(&tempMsg,msg->
           return 0;
      }
      else
           return CallNextHookEx(NULL, nCode, wParam, lParam);
The actual key injection is working, so bSuppress is true, but the original key still goes through.
2. A more perplexing problem. Â The code works for the first 5 minutes or so, and then without any intervention stops working (the program is still running in the background, and debug messages are printed to a file, but the hook seems to somehow have been unhooked). Â When I restart, it works again, but again for only 5 minutes or so. Â Why is my hook getting unhooked? Â I have even left the computer as is for 5 minutes and it did the samet thing, so it is not under my intervention.
Thanks so much,
Kevin






EARN REWARDS FOR ASKING, ANSWERING, AND MORE.
Earn free swag for participating on the platform.
Kevin

Get a FREE t-shirt when you ask your first question.
We believe in human intelligence. Our moderation policy strictly prohibits the use of LLM content in our Q&A threads.
C++
--
Questions
--
Followers
Top Experts
C++ is an intermediate-level general-purpose programming language, not to be confused with C or C#. It was developed as a set of extensions to the C programming language to improve type-safety and add support for automatic resource management, object-orientation, generic programming, and exception handling, among other features.