Solved

system wide key/mouse hook does NOT work on NT4.0 if a background process

Posted on 2003-12-11
7
378 Views
Last Modified: 2013-12-03
Based on the prior expert's advice (thanks) I implemented system-wide (I wish) hook in C on NT 4.0.
It has two components: a hook DLL and the main test program. When I run the main test program in DOS console it does capture keyboard/mouse events.

However, if I start the test program in the background (EITHER via a home-grown network messaging mechanism where I issue system("start /B c:\path\main.exe") from an already running background program OR via a remote cmd console (using psexec)); it does not work.

Visual C++ source code is below (Main starts up, waits for an event and when detects one, it exits).

What am I doing wrong ?


//HOOK DLL
#include <windows.h>
#include <stdio.h>

HINSTANCE instance_handle;

#pragma data_seg (".hookdat")
HHOOK g_hHookKbd = NULL;
HHOOK g_hHookMse = NULL;
DWORD g_dwLastInputTick = 0;
#pragma data_seg()
#pragma comment (linker,"/section:.hookdat,rws")


// Get tick count of last keyboard or mouse event
__declspec( dllexport ) DWORD IdleCPUGetLastInputTime()
{
     return g_dwLastInputTick;
}

// Keyboard hook: record tick count
LRESULT CALLBACK MyKbdHook(int code, WPARAM wParam, LPARAM lParam)
{
    if (code==HC_ACTION)
     {
          g_dwLastInputTick = GetTickCount();
    }
    return CallNextHookEx(g_hHookKbd, code, wParam, lParam);
}
 
// Mouse hook: record tick count
LRESULT CALLBACK MyMouseHook(int code, WPARAM wParam, LPARAM lParam)
{
    if (code==HC_ACTION)
     {
       g_dwLastInputTick = GetTickCount();
    }
    return CallNextHookEx(g_hHookMse, code, wParam, lParam);
}
 
// Initialize DLL: install kbd/mouse hooks.
__declspec( dllexport ) bool IdleCPUInit()
{
   if (g_hHookKbd == NULL)
   {
          g_hHookKbd = SetWindowsHookEx(WH_KEYBOARD, (HOOKPROC) MyKbdHook, instance_handle, 0);
          g_hHookMse = SetWindowsHookEx(WH_MOUSE,    (HOOKPROC) MyMouseHook, instance_handle, 0);
          g_dwLastInputTick = GetTickCount(); // init count
    }
    return TRUE;
}
 
// Terminate DLL: remove hooks.
__declspec( dllexport ) void IdleCPUTerm()
{
    BOOL bRet1 = UnhookWindowsHookEx(g_hHookKbd);
    BOOL bRet2 = UnhookWindowsHookEx(g_hHookMse);
}

int WINAPI DllMain(HINSTANCE hinst, unsigned long reason, void* lpReserved)
{
     switch(reason) {
     case DLL_PROCESS_ATTACH:
          DisableThreadLibraryCalls(hinst);
          instance_handle = hinst;
          return TRUE;
     case DLL_PROCESS_DETACH:
          if ((g_hHookKbd != NULL) || (g_hHookMse != NULL))
          {
               IdleCPUTerm();
          }
          return TRUE;
     default:
          return TRUE;
     }
}


//MAIN TEST STUB
//COMMENT: I am using ordinal numbers for hook dll lookup because had some problems with passing the name
#include <stdio.h>
#include <windows.h>

typedef BOOL (*INITPROC) ();
typedef DWORD (*TIMEPROC) ();
typedef VOID (*TERMPROC) ();

int main(int argc,char *argv[]) {
    HINSTANCE hinstLib;
    INITPROC ProcInit = NULL;
    TIMEPROC ProcTime = 0;
    TERMPROC ProcTerm;
    BOOL fFreeResult;
    long last_input_time, time_running, time_started, time_diff;
    BOOL no_interrupt;

    hinstLib = LoadLibrary("hook");
    if (hinstLib != NULL) {
        ProcInit = (INITPROC) GetProcAddress(hinstLib, 2);
        if (ProcInit)  {
         fFreeResult=ProcInit ();
        }
    }
// initial time tick
    time_started = (long) GetTickCount();
    ProcTime = (TIMEPROC) GetProcAddress(hinstLib, 1);
    if (ProcTime) {
        no_interrupt=TRUE;
        while (no_interrupt) {
            last_input_time = (long) ProcTime();
            time_running = (long)((GetTickCount() - time_started)/1000);
            time_diff = (long)((GetTickCount() - last_input_time)/1000);
            if ((time_diff + 4) < time_running) {
                no_interrupt=FALSE;
            }
            if (no_interrupt==TRUE) {
                Sleep (5000);
            }
        }
    }
    ProcTerm = (TERMPROC) GetProcAddress(hinstLib, 3);
    if (NULL != ProcTerm) {
        (ProcTerm) ();
    }
    fFreeResult = FreeLibrary(hinstLib);
}


0
Comment
Question by:jerzyb
  • 3
  • 2
7 Comments
 
LVL 86

Expert Comment

by:jkr
ID: 9922809
>>However, if I start the test program in the background (EITHER via a home-grown network
>>messaging mechanism where I issue system("start /B c:\path\main.exe") from an already running
>>background program OR via a remote cmd console (using psexec)); it does not work.

That's not surprising, as there is a different desktop assiciated with the process than the one of the logged on user. You should put that in the autostart group for every user.
0
 

Author Comment

by:jerzyb
ID: 9922973
There are lots of users.
The job of the background process is to do heavy calculations (which have nothing to do with the logged in user) and suspend itself if a keyboard/mouse activity detected (to minimize any effects on the user).

The process is already LOW priority but sometimes its not enough.

The screensavers somehow do it so, there must be a different way.

Jerzy
0
 
LVL 86

Expert Comment

by:jkr
ID: 9922995
>>The screensavers somehow do it

Yes, but they are launched in the user's context...
0
Is Your Active Directory as Secure as You Think?

More than 75% of all records are compromised because of the loss or theft of a privileged credential. Experts have been exploring Active Directory infrastructure to identify key threats and establish best practices for keeping data safe. Attend this month’s webinar to learn more.

 

Author Comment

by:jerzyb
ID: 9923590
OK, since my program runs with admin priviledges, can I somehow start my key/mouse detection program in the context of the logged-in user?
That is, the program will monitor who is logged in and restart appropriately (but how?).
0
 
LVL 86

Accepted Solution

by:
jkr earned 500 total points
ID: 9923704
>>can I somehow start my key/mouse detection program in the context of the logged-in user?

Yes, you can, but it ain't that simple :o)

You have to obtain a token from a process the user is running (explorer.exe is a good choice <s>). You'll need the PID to call 'OpenProcess()', then 'OpenProcessToken()' with 'TOKEN_IMPERSONATE' access. See http://support.microsoft.com/default.aspx?scid=kb;EN-US;q175030 ("HOWTO: Enumerate Applications Using Win32 APIs (Q175030)") on how to get that PID - it comes with sample code. Then, once you have the token, you have to call 'ImpersonateLoggedOnUser()', et voilà, you are there - either launch the program or do that inside the program before initializing the hooks.
0
 
LVL 8

Expert Comment

by:adg080898
ID: 9953760
You are using #pragma data_seg()

You must use #pragma bss_seg because the shared variables are uninitialized. You are initializing them to zero, but zero doesn't count as initializing. Instead of #pragma data_seg use #pragma bss_seg, or even both (haven't tried both).

Sections:
text_seg: program code
data_seg: NONZERO initialized variables
bss_seg: zero initialized variables

The idea is that the linker does not neet to write the bss_seg content to the output file because it is understood to be all-zeros, reducing executable size.

Use DUMPBIN in the msvc tools directory to verify that the output file does contain a .hookdat section.
0

Featured Post

Is Your Active Directory as Secure as You Think?

More than 75% of all records are compromised because of the loss or theft of a privileged credential. Experts have been exploring Active Directory infrastructure to identify key threats and establish best practices for keeping data safe. Attend this month’s webinar to learn more.

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

Suggested Solutions

Title # Comments Views Activity
ODBC Connection; Switch between 2 SQL Databases VBA 11 72
Need to create an object factory 2 44
wordpress limitations 4 103
Post a good COM tutorial 1 49
This article shows how to make a Windows 7 gadget that extends its U/I with a flyout panel -- a window that pops out next to the gadget.  The example gadget shows several additional techniques:  How to automatically resize a gadget or flyout panel t…
Whether you've completed a degree in computer sciences or you're a self-taught programmer, writing your first lines of code in the real world is always a challenge. Here are some of the most common pitfalls for new programmers.
This is Part 3 in a 3-part series on Experts Exchange to discuss error handling in VBA code written for Excel. Part 1 of this series discussed basic error handling code using VBA. http://www.experts-exchange.com/videos/1478/Excel-Error-Handlin…
The is a quite short video tutorial. In this video, I'm going to show you how to create self-host WordPress blog with free hosting service.

911 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

21 Experts available now in Live!

Get 1:1 Help Now