全國最多中醫師線上諮詢網站-台灣中醫網
發文 回覆 瀏覽次數:2307
推到 Plurk!
推到 Facebook!

Windows hooks

 
conundrum
尊榮會員


發表:893
回覆:1272
積分:643
註冊:2004-01-06

發送簡訊給我
#1 引用回覆 回覆 發表時間:2004-07-03 13:26:51 IP:61.221.xxx.xxx 未訂閱
 http://www.cppbuilderdevjournal.com/articles/issues/0103/Windows_hooks.htm
http://www.cppbuilderdevjournal.com/articles/articleindex.php    Windows hooks
by Kent Reisdorph
There are times when you need to know what? going on within Windows at the system level. You might need to know when a particular window (application) is closed, or when a particular keystroke combination is typed on the keyboard. The Windows API provides a means for spying on the system to obtain this kind of information: Windows hooks. 
This article will explain hooks and the use of the Win32 API functions SetWindowsHookEx(), UnhookWindowsHookEx(), and CallNextHookEx(). It will also point out some of the common pitfalls to avoid when implementing a hook.
 
What? a hook?
A Windows hook is a way of spying on Windows at the system level. Ultimately a Windows hook is a function you write that Windows calls whenever a hook event occurs. A hook allows you to examine Windows messages before they are processed by the system. You can simply monitor messages or you can modify certain messages if you choose. You provide a hook function, register the hook function with Windows, and from that point on Windows will call your hook function each time a particular type of system event occurs.
There are several types of Windows hooks. Most hooks can be installed to monitor an event in a specific thread or at the system level. A keyboard hook, for example, could capture the keystrokes for just one application or for all applications on the system. Some hooks can only be implemented at the system level. Table A lists the constants representing the hook types, the scope for that hook, and a description of each hook type.
 
Table A: Windows hook types.
 
Hook         Scope         Description
WH_CALLWNDPROC         Thread or system         Monitors Windows messages before they are sent to a particular window? window procedure.
WH_CALLWNDPROCRET         Thread or system         Monitors Windows messages after they have been processed by a particular window? window procedure.
WH_CBT         Thread or system          Used for computer-based training applications.
WH_DEBUG         Thread or system          Used when debugging other hook functions.
WH_FOREGROUNDIDLE         Thread or system          The hook procedure will be called when an application? foreground window goes idle.
WH_GETMESSAGE         Thread or system          Monitors messages posted to a message queue.
WH_JOURNALPLAYBACK         System only         Plays back a series of keystrokes and mouse movements previously recorded with a journal record hook (a macro). Normal mouse and keyboard input is disabled while the macro is playing.
WH_KEYBOARD          Thread or system           The hook procedure will be called when a WM_KEYDOWN or WM_KEYUP message is about to be processed.
WH_JOURNALRECORD         System only         The hook procedure will be called for all keyboard and mouse messages. Useful for recording macros.
WH_KEYBOARD_LL         Thread or system          A low-level keyboard hook (Windows NT only).
WH_MOUSE         Thread or system         The hook procedure will be called when a mouse message is about to be processed.
WH_MOUSE_LL         Thread or system         A low-level mouse hook (Windows NT only).
WH_MSGFILTER         Thread or system          Monitors messages as a result of an action in a menu, dialog box, or scroll bar created by a specific application.
WH_SHELL         Thread or system          Monitors messages affecting the Windows shell.
WH_SYSMSGFILTER         System only         Monitors messages as a result of an action in a menu, dialog box, or scroll bar created by any application.
 
 
The first step in implementing a Windows hook is to decide on the type of hook you need. A quick glance at Table A will reveal that making that determination might be difficult. You may have to spend some time experimenting before you settle on the best type of hook to use. Once you have determined the type of hook you need, you can get on with the work of writing the hook itself.
 
Windows hook functions
The Windows API has three primary hook functions. The SetWindowsHookEx() function is used to install a hook, the UnhookWindowsHookEx() function is used to remove a hook, and the CallNextHookEx() function is used to call the next hook in the hook chain (I will explain hook chaining a little later). The following sections describe these functions and explain their use.
 
SetWindowsHookEx()
As its name implies, SetWindowsHookEx() is used to install a hook. Here? the declaration:
HHOOK SetWindowsHookEx(
  int idHook,
  HOOKPROC lpfn, procedure
  HINSTANCE hMod,
  DWORD dwThreadId 
);        
The first parameter is used to specify the type of hook to install (see the constants in Table A). The second parameter is used to pass the address of a hook procedure that will be called when Windows activates the hook (I will explain the hook procedure later). The third parameter is the instance handle of the module where the hook procedure resides (the executable or a DLL). The final parameter is used to specify the thread ID of the thread to which the hook is being attached. For system-wide hooks this parameter must be 0.
If the hook was successfully installed, SetWindowsHookEx() returns a handle to the hook (an HHOOK). This handle is used later when uninstalling the hook and when calling the next hook in the hook chain. If the call to SetWindowsHookEx() fails the return value is 0. The following example installs a keyboard hook for the current process:
HHOOK KbHook = SetWindowsHookEx(
  WH_KEYBOARD, (HOOKPROC)MyKBHook,
  HInstance, GetCurrentThreadId()); 
In this example, the name of the hook procedure is MyKBHook(). Notice that the Windows API function GetCurrentThreadId() is used to specify the thread ID of the process for which the hook is attached. Notice also that the return value is saved in a variable called HKbHook. You should always check the return value to insure that the hook was successfully installed.
 
UnhookWindowsHookEx()
UnhookWindowsHookEx() removes a hook from the system. This function returns a non-zero value if the hook was successfully removed, or zero if an error occurred removing the hook. The most common reason for an error to occur while removing a hook is that the hook as already been removed and the hook handle is invalid. Removing a hook is trivial as this example shows:
Res = UnhookWindowsHookEx(HKbHook); 
As you can see, there? not much to UnhookWindowsHookEx(). Just be sure that you check the return value and take appropriate action if removing the hook fails.
 
CallNextHookEx()
Windows uses a chaining mechanism for hooks. This allows more than one process to implement a particular type of hook. Let? say, for example, that you install a keyboard hook. At that point you don急 know whether your keyboard hook is the only installed hook or not. One or more other applications might also have installed a keyboard hook. Windows calls your keyboard hook procedure and then expects you to keep the chain intact by calling the next hook in the chain. Calling the next hook in the chain is accomplished using the CallNextHookEx() function. You will see an example of CallNextHookEx() in the next section.
 
The hook procedure
The key element to a Windows hook is the hook procedure. The hook procedure is called each time an event occurs within Windows pertinent to the type of hook installed. The hook procedure for a keyboard hook, for example, will be called each time a key is pressed or released. Windows defines a hook procedure for each hook type. The declaration for a keyboard hook procedure, for example, looks like this:
LRESULT CALLBACK MyKBHook(int Code,
  WPARAM wParam, LPARAM lParam); 
Actually, all of the hook procedures have this same signature. The difference between the different hook procedures is in the values of the Code, wParam, and lParam parameters. These values of these parameters vary with the type of hook installed. It is not practical to attempt to cover each and every hook procedure in this article. See the Win32 API help under the topics SetWindowsHookEx() and ?ook Functions?for a description of the various hook procedures and their parameters. 
Any code in the hook procedure should be designed as efficiently as possible. A mouse hook, for example, is called every time a mouse message is generated. This could result in the mouse hook procedure being called thousands of times per second. Obviously you want any code in a hook procedure to be very efficient.
Ultimately the hook procedure needs to call CallNextHookEx() before exiting. Here? an example of a keyboard hook procedure that performs no action other than to call the next hook in the hook chain:
LRESULT CALLBACK MyKBHook(int Code,
  WPARAM wParam, LPARAM lParam)
{
  return CallNextHookEx(
    HKbHook, Code, wParam, lParam);
} 
As you can see, the values of the handle to the current hook (obtained in the initial call to SetWindowsHookEx) and the values of the Code, wParam, and lParam parameters are passed on to CallGetNextHookEx(). This insures that hook chain remains intact. Failure to call CallNextHookEx() will have unpredictable and almost certainly undesirable effects. Notice that the return value of CallNextHookEx() is returned as the hook procedure? function result.
 
Where does the hook procedure live?
An important aspect of a Windows hook is the question of where the hook procedure resides. If the hook is an application (thread) hook, the hook procedure can reside in either the application itself or in a DLL. If the hook is a system-wide hook then the hook procedure must reside in a DLL.
Implementing a system-wide hook in a DLL can be frustrating on the first attempt. It? important to realize that each application that accesses the hook will load the hook DLL. For example, let? say you have installed a system-wide keyboard hook. Remember that you need to call the CallNextHookEx() function from within your hook procedure, and that you must pass the handle of the current hook when you do so. But where do you store the hook? handle? You would normally use a global variable, but remember that each process that attaches to a 32-bit DLL gets its own copy of any global variables in the DLL. That means that the variable that holds the hook handle will contain a valid value only for the process that installed the hook. You must find a way to share the hook handle among all instances of the DLL. 
The best way to share data among instances of a DLL is with shared memory or a memory mapped file. Memory mapped files are beyond the scope of this article, so the example DLL presented later in this article uses VCL file streams to store hook handles. This is not necessarily a recommended approach, but is used in our example for the sake of simplicity. Needless to say, implementing a system-wide hook in a DLL takes careful consideration and planning.
 
Thread or system hook?
As indicated earlier, a hook can be installed for a specific thread or for the entire system (with the exception of system-only hooks). System-wide hooks can affect performance of the entire system so you should only use system-wide hooks when absolutely necessary. To install a hook for a specific thread, pass the thread ID in the final parameter to SetWindowsHookEx(). For example:
DWORD ID = GetCurrentThreadId();
HHOOK HKbHook = SetWindowsHookEx(
  WH_KEYBOARD, (HOOKPROC)MyKBHook,
  HInstance, ID);  
This code installs a keyboard hook for the current process only. To install a system-wide keyboard hook use code like this:
HHOOK HKbHook = SetWindowsHookEx(
  WH_KEYBOARD, (HOOKPROC)MyKBHook,
  HInstance, 0);  
This hook is a system-wide hook because 0 is passed for the dwThreadId parameter.
Regardless of whether you use a thread hook or a system-wide hook, you should install the hook only when needed, and uninstall it as soon as possible to avoid a drain on the system. 
 
Note: Many programmers have attempted the use of a system-wide keyboard hook to disable the Ctrl-Alt-Delete key combination. This key combination is protected by Windows and even a keyboard hook will not allow you to intercept Ctrl-Alt-Delete.
 
Conclusion
Listings A and B contain the header and source code for a DLL that implements two hooks. The first hook is a keyboard hook. This hook can be either a thread hook or a system-wide hook (as determined by the thread ID passed to the DLL when installing the hook). The keyboard hook saves keystrokes in a buffer and then displays the keystrokes in a message box when the buffer is full. The second hook in the example program is a shell hook. This hook monitors the system and notifies you when Windows Notepad starts and also when Notepad shuts down. The code for this article also includes a calling application that installs the hooks. I don急 show the source for the calling application here, but the source code for both the calling application and the DLL can be downloaded from the Bridges Publishing Web site. 
There is no question that the use of windows hooks is rare. When you need a hook of a particular type, however, nothing else will do the job as effectively. Be judicious in your use of hooks, but keep the power of this technique in your arsenal.
 
Listing A: MyHook.h
#ifndef _MYHOOK_H
#define _MYHOOK_H    #ifdef __DLL__
  #define DLL_EXP __declspec(dllexport)
#else
  #define DLL_EXP __declspec(dllimport)
#endif    extern "C" {
  bool DLL_EXP HookKb(DWORD ThreadID);
  bool DLL_EXP HookShell();
  bool DLL_EXP Unhook();
}    #endif
 
Listing B: MyHook.cpp
 
#include 
#include 
#pragma hdrstop    #pragma argsused
#include "myhook.h"    const int BuffSize = 50;
const String TempHookFile = "c:\\hook.dat";
HHOOK HKbHook;
HHOOK HShellHook;
char Buffer[BuffSize];
int Index;
TFileStream* FS;    int WINAPI DllEntryPoint(HINSTANCE hinst, 
  unsigned long reason, void* lpReserved)
{
  if (reason == DLL_PROCESS_ATTACH) {
    HKbHook = 0;
    HShellHook = 0;
    Index = 0;
  }
  return 1;
}    // NOTE: This example uses a file stream to
// save and restore the DLL's global data. It
// would be better to use a memory mapped file
// for this purpose but use of a memory mapped
// file is beyond the scope of this article.    // The keyboard hook procedure.
LRESULT CALLBACK MyKBHook(int Code,
  WPARAM wParam, LPARAM lParam)
{
  // if (HKbHook is 0 it means that a new
  // process has attached to the hook DLL.
  // We must load the HKbHook value from
  // the temp file so that we can pass it
  // in CallNextHookEx.
  if (HKbHook == 0) {
    FS = new TFileStream(
      TempHookFile, fmOpenRead);
    FS->Read(HKbHook, sizeof(HKbHook));
    FS->Read(HShellHook, sizeof(HShellHook));
    delete FS;
  }
  // if (Code < 0, call the next hook and exit.
  if (Code >= 0)
    // Key up messages only
    if ((lParam & 0x80000000) == 0x80000000)
    {
      // Save the keystroke in the buffer and
      // increment the buffer index variable.
      Buffer[Index] = char(wParam);
      Index  ;
      if (Index >= BuffSize) {
        // Buffer is full. Reset the buffer
        // index variable and show the buffer.
        Index = 0;
        MessageBox(0, Buffer, "Hook Message", 0);
      }
    }
  // Call the next hook in the chain.
  return CallNextHookEx(
    HKbHook, Code, wParam, lParam);
}    // The shell hook procedure.
int __stdcall MyShellHook(int Code,
  WPARAM wParam, LPARAM lParam)
{
  // if (HShellHook is 0 it means that a new
  // process has attached to the hook DLL.
  // We must load the HKbHook value from
  // the temp file so that we can pass it
  // in CallNextHookEx.
  if (HShellHook == 0) {
    FS = new TFileStream(
      TempHookFile, fmOpenRead);
    FS->Read(HKbHook, sizeof(HKbHook));
    FS->Read(HShellHook, sizeof(HShellHook));
    delete FS;
  }
  // Only interested in those case where a
  // window is created or destroyed.
  char buff[255];
  if ((Code == HSHELL_WINDOWCREATED) ||
     (Code == HSHELL_WINDOWDESTROYED)) {
    // Get the class name of the window.
    GetClassName((
      HWND)wParam, buff, sizeof(buff));
    // if (class name is Notepad then let
    // the show a message box.
    String S;
    if (!strcmp(buff, "Notepad")) {
      if (Code == HSHELL_WINDOWCREATED)
        S = "Notepad Starting!";
      else
        S = "Notepad Shutting Down!";
      MessageBox(0, S.c_str(), "Hook Message",0);
    }
  }
  // Call the next hook in the chain.
  return CallNextHookEx(
    HShellHook, Code, wParam, lParam);
}    // Exported function that is used to install
// the keyboard hook.
bool DLL_EXP HookKb(DWORD ThreadID)
{
  HKbHook = SetWindowsHookEx(WH_KEYBOARD, 
    (HOOKPROC)MyKBHook, HInstance, ThreadID);
  bool Result = Boolean(HKbHook);
  // Save the hook handles to a temporary file
  // so that other processes can have access
  // to those values.
  FS = new TFileStream(
    TempHookFile, fmCreate);
  FS->Write(HKbHook, sizeof(HKbHook));
  FS->Write(HShellHook, sizeof(HShellHook));
  delete FS;
  return Result;
}    // Exported function that is used to install
// the shell hook.
bool DLL_EXP HookShell()
{
  HShellHook = SetWindowsHookEx(WH_SHELL,
    (HOOKPROC)MyShellHook, HInstance, 0);
  bool Result = Boolean(HShellHook);
  // Save the hook handles to a temporary file
  // so that other processes can have access
  // to those values.
  FS = new TFileStream(
    TempHookFile, fmCreate);
  FS->Write(HKbHook, sizeof(HKbHook));
  FS->Write(HShellHook, sizeof(HShellHook));
  delete FS;
  return Result;
}    // Exported function that uninstalls all hooks.
bool DLL_EXP Unhook()
{
  bool Result = false;
  if (HKbHook != 0)
    Result = UnhookWindowsHookEx(HKbHook);
  if (HShellHook != 0)
    Result = UnhookWindowsHookEx(HShellHook);
  HKbHook = 0;
  HShellHook = 0;
  // Delete the temporary file.
  DeleteFile(TempHookFile);
  return Result;
}
________________________________________
Search articles online
Search text:       C  Builder Developer's Journal
Copyright ?2004, Baseline Grid Publications. All rights reserved. Reproduction in whole or in part in any form or medium without express written permission of Baseline Grid Publications is prohibited. All other product names and logos are trademarks or registered trademarks of their respective owners.
系統時間:2024-05-02 12:19:25
聯絡我們 | Delphi K.Top討論版
本站聲明
1. 本論壇為無營利行為之開放平台,所有文章都是由網友自行張貼,如牽涉到法律糾紛一切與本站無關。
2. 假如網友發表之內容涉及侵權,而損及您的利益,請立即通知版主刪除。
3. 請勿批評中華民國元首及政府或批評各政黨,是藍是綠本站無權干涉,但這裡不是政治性論壇!