Windows SDK programming series-hook

Source: Internet
Author: User

In this lesson, we will learn how to use Windows hook functions. The Windows hook function is very powerful. With it, you can detect other processes and change the behavior of other processes.

Theory:

Windows hook functions can be considered as one of the main features of windows. They allow you to capture events that occur in your own processes or other processes. With "hook-up", you can give Windows a callback function to process or filter events. This function is also called "hook function". When an event you are interested in occurs, windows will call this function. There are two types of hooks: local and remote.

  • Local hooks only hook events of your own processes.
  • Remote hooks can also hook events that occur in other processes. There are two remote hooks:
    • Based on the thread, it will capture events of a specific thread in other processes. In short, it can be used to observe the events that will happen to a specific thread in other processes.
    • System-wide event messages will be captured by all processes in the system.

Installing the hook function will affect the system performance. The system hooks for monitoring "system-scope events" are particularly obvious. Because the system calls your hook function when processing all related events, your system will obviously slow down. Therefore, use it with caution and uninstall it immediately after use. In addition, because you can intercept messages from other processes in advance, once your hook function has a problem, it will certainly affect other processes. Remember: being powerful also means taking responsibility for use.
Before correctly using the hook function, we will first explain how the hook function works. When you create a hook, Windows will first create a data structure in the memory, which contains information about the hook, then add the struct to an existing hook linked list. The new hook is added to the front of the old one. When an event occurs, if you install a local Hook, the hook function in your process will be called. If it is a remote Hook, the system must insert the hook function into the address space of other processes. To achieve this, the hook function must be in a dynamic link library, if you want to use a remote Hook, you must put the hook function in the dynamic link library. Of course, there are two exceptions: The work Log Hook and the work log playback hook. The hook functions of these two hooks must be in the thread where the hooks are installed. The reason is: These two hooks are used to monitor relatively low-level hardware events. Since they are recorded and played back, all events are of course sequential. Therefore, if the callback function is placed in the DLL and the input events are recorded in several threads, we cannot guarantee the correct order. The solution is to put the hook function in a single thread, such as the thread for installing the hook.
There are a total of 14 types of hooks. The following are the times when they are called:

  • Wh_callwndproc when sendmessage is called
  • Wh_callwndprocret when sendmessage is returned
  • Wh_getmessage when getmessage or peekmessage is called
  • Wh_keyboard when getmessage or peekmessage is called to query wm_keyup or wm_keydown messages from the Message Queue
  • Wh_mouse when getmessage or peekmessage is called to query mouse event messages from the Message Queue
  • Wh_hardware when getmessage or peekmessage is called to query non-mouse and keyboard messages from Message Queue types
  • Wh_msgfilter: when a message is to be processed in a dialog box, menu, or scroll bar. This hook is local. It is designed for the control objects that have their own message processing processes.
  • Wh_sysmsgfilter is the same as wh_msgfilter, but it is only within the system range.
  • Wh_journalrecord when Windows receives messages from the hardware queue
  • Wh_journalplayback when an event is requested from the system's hardware Input Queue
  • Wh_shell when a Windows Shell event occurs, for example, a task bar needs to re-draw its button.
  • Wh_cbt when a computer-based training (CBT) event occurs
  • Wh_foregroundidle is used by windows, and is rarely used by general applications.
  • Wh_debug is used to debug the hook function

Now we know some basic theories. Now we will explain how to install/uninstall a hook.
To install a hook, you can call the setjavaswhookex function. The function is prototype as follows:

Hhook setwindowshookex (intIdhook, HookprocLpfn, HinstanceHmod, DWORDDwthreadid);

  • IdhookIs one of the values listed above, such as: wh_mouse, wh_keyboard
  • LpfnIs the address of the hook function. If a remote hook is used, it must be placed in a DLL; otherwise, it should be placed in its own code.
  • HmodThe instance handle of the DLL where the hook function is located. If it is a local Hook, the value is null.
  • DwthreadidThe ID of the thread you want to monitor after installing the hook function. This parameter determines whether the hook is local or system-wide. If the value is null, the hook will be interpreted as within the system range, so that it can monitor all processes and their threads. If you specify a thread ID in your own process, this hook is a local hook. If the thread ID is the ID of a thread in another process, the hook is a global remote hook. There are two special cases: wh_journalrecord and wh_journalplayback always represent local system range hooks. The reason is that they are local because they do not need to be placed in a DLL. Wh_sysmsgfilter is always a system-wide remote hook. In fact, it is similar to the wh_msgfilter hook. If the threadid parameter is set to 0, they are exactly the same.

If the function is successfully called, the hook handle is returned. Otherwise, null is returned. You must save the handle because we need it to uninstall the hook later.

To uninstall a hook, call the unhookwidowhookex function. This function has only one parameter, that is, the handle of the hook to be detached. If the call is successful, a non-zero value is returned; otherwise, null is returned.
Now that you know how to install and uninstall a hook, let's take a look at the hook function ..
As long as the message event type of the hook you install occurs, Windows will call the hook function. For example, if the hook you installed is of the wh_mouse type, the hook function will be called when a mouse event occurs. Regardless of the hook type you installed, the hooks are prototype the same:

Lresult callback hookproc (intCode, WparamWparam, LparamLparam);

  •  

    • Ncode specifies whether to process the message
    • Wparam and lparam include additional messages for the message

Hookproc can be seen as a placeholder for a function name. As long as the function prototype is consistent, you can give the function any name. The meanings of the preceding parameters and returned values are different for different types of hooks. For example:

Wh_callwndproc

  • Ncode can only be hc_action, which indicates that a message is sent to a window.
  • If wparam is not 0, it indicates the message being sent.
  • Lparam pointer to the cwpstruct struct variable
  • Return Value: unused. 0 is returned.

Wh_mouse

  • Ncode is hc_action or hc_noremove
  • Wparam contains the mouse event message
  • Lparam pointer to the mousehookstruct struct variable
  • Return Value: If not processed, 0 is returned; otherwise, a non-0 value is returned.

Therefore, you must query your Win32 API guide to obtain detailed definitions of parameters of different types of hooks and the meaning of their return values. There is another problem: All the hooks are stored in a linked list, and the recently added hooks are placed in the head of the linked list. When an event occurs, Windows calls it from the head of the linked list to the end of the linked list. Therefore, your hook function has the responsibility to transmit messages to the hook function in the next chain. Of course, you may not do this, but you 'd better understand the reason for doing so. In most cases, it is best to pass the message event so that other Hooks have the opportunity to process the message. Call the next hook function to call callnexthookex. The function is prototype as follows:

Lresult callnexthookex (hhookHHK, IntNcode, WparamWparam, LparamLparam);

  • HHKIs the handle of your own hook function. You can use this handle to traverse the hook chain.
  • Ncode, wparam and lparam you only need to pass the passed parameters to callnexthookex.

Note: For remote hooks, hook functions must be placed in the DLL, which will be reflected in other process spaces. When Windows maps DLL to other process spaces, data segments are not mapped. In short, all processes only share the DLL code. As for the data segment, each process will have its own copy. This is a problem that is easily overlooked. You may assume that the values saved in the DLL can be shared among all the processes mapped to the DLL. Generally, because every process mapped to the DLL has its own data segment, your program runs well in most cases. But not the hook function. For hook functions, the DLL data segment must be the same for all processes. In this way, you must set the data segment to shared, which can be achieved by specifying the segment attribute in the Link switch.

# Pragma data_seg ("shared ")
Hinstance g_hinstance = NULL;
Hhook g_hhook = NULL;
Hwnd g_hwnd = NULL;
# Pragma data_seg ()
# Pragma comment (linker, "/section: shared, RWS ")

Shared represents a shared segment.

Example:

There are two modules in total: one is the GUI part, see cd d:/mybook/examples/firstwindow22/EXE
The other is the DLL used to install and uninstall hooks. For details, see cd d:/mybook/examples/firstwindow22/DLL.

DLL Code:
# Include "windows. H"
# Define wm_mousehook wm_user + 6

# Pragma data_seg ("shared ")
Hinstance g_hinstance = NULL;
Hhook g_hhook = NULL;
Hwnd g_hwnd = NULL;
# Pragma data_seg ()
# Pragma comment (linker, "/section: shared, RWS ")

Bool apientry dllmain (handle hmodule,
DWORD ul_reason_for_call,
Lpvoid lpreserved
)
{
G_hinstance = (hinstance) hmodule;
Return true;
}

Lresult callback mouseproc (INT ncode,
Wparam,
Lparam
)
{
Callnexthookex (g_hhook, ncode, wparam, lparam );
Lpmousehookstruct PS = (lpmousehookstruct) lparam;
Hwnd = windowfrompoint (PS-> pt );
Postmessage (g_hwnd, wm_mousehook, (wparam) hwnd, 0 );
Return 0;
}

Hhook installhook (hwnd)
{
G_hwnd = hwnd;
G_hhook = setwindowshookex (wh_mouse, mouseproc, g_hinstance, 0 );
Return g_hhook;
}

Void uninstallhook ()
{
Unhookwindowshookex (g_hhook );
}

EXE code:
# Include "windows. H"
# Include "tchar. H"
# Pragma comment (Lib, "mhook. lib ")
Hhook installhook (hwnd );
Void uninstallhook ();
Lresult callback mouseproc (INT ncode,
Wparam,
Lparam
);

# Define idd_maindlg 101
# Define idc_classname 1000
# Define idc_handle 1001
# Define idc_wndproc 1002
# Define idc_hook 1004.
# Define idc_exit 1005
# Define wm_mousehook wm_user + 6

Bool hookflag = false;
Tchar hooktext [] = _ T ("& hook ");
Tchar unhooktext [] = _ T ("& unhook ");
Tchar mytemplate [] = _ T ("% lx ");
Hinstance g_hinstance;
Hhook;

Int callback dlgfunc (
Hwnd hwnddlg,
Uint umsg,
Wparam,
Lparam
)
{
Byte buffer [128];
Byte temp [128];
Rect;
Switch (umsg)
{
Case wm_close:
If (hookflag)
Uninstallhook ();
Enddialog (hwnddlg, null );
Break;
Case wm_initdialog:
Getwindowrect (hwnddlg, & rect );
Setwindowpos (hwnddlg, hwnd_topmost, rect. Left, rect. Top, rect. Right, rect. Bottom, swp_showwindow );
Break;
Case wm_mousehook:
Getdlgitemtext (hwnddlg, idc_handle, (lpstr) temp, 128 );
Wsprintf (lpstr) buffer, mytemplate, wparam );
If (lstrcmpi (lpcstr) buffer, (lpcstr) temp )! = 0)
Setdlgitemtext (hwnddlg, idc_handle, (lpcstr) buffer );

Getdlgitemtext (hwnddlg, idc_classname, (lpstr) temp, 128 );
Getclassname (hwnd) wparam, (lpstr) buffer, 128 );
If (lstrcmpi (lpcstr) buffer, (lpcstr) temp )! = 0)
Setdlgitemtext (hwnddlg, idc_classname, (lpcstr) buffer );

Getdlgitemtext (hwnddlg, idc_wndproc, (lpstr) temp, 128 );
Wsprintf (lpstr) buffer, mytemplate, getclasslong (hwnd) wparam, gcl_wndproc ));
If (lstrcmpi (lpcstr) buffer, (lpcstr) temp )! = 0)
Setdlgitemtext (hwnddlg, idc_wndproc, (lpcstr) buffer );
Break;

Case wm_command:
If (lparam! = 0)
{
If (hiword (wparam) = bn_clicked)
{
If (loword (wparam) = idc_exit)
Sendmessage (hwnddlg, wm_close, 0, 0 );

Else if (loword (wparam) = idc_hook)
{
If (! Hookflag)
{
If (installhook (hwnddlg )! = NULL)
{
Hookflag = true;
Setdlgitemtext (hwnddlg, idc_hook, unhooktext );
Outputdebugstring ("hook/N ");
}
}
Else
{
Outputdebugstring ("unhook/N ");
Uninstallhook ();
Setdlgitemtext (hwnddlg, idc_hook, hooktext );
Hookflag = false;
Setdlgitemtext (hwnddlg, idc_classname, null );
Setdlgitemtext (hwnddlg, idc_handle, null );
Setdlgitemtext (hwnddlg, idc_wndproc, null );

}
}
}
}
Break;
Default:
Return false;

}
Return true;

}
Int apientry winmain (hinstance,
Hinstance hprevinstance,
Lpstr lpcmdline,
Int ncmdshow)
{
// Todo: Place code here.
G_hinstance = hinstance;
Dialogboxparam (g_hinstance, makeintresource (idd_maindlg), null, dlgfunc, null );
Return 0;
}

Analysis:

The main window of the application contains three editing controls, which display the Class Name of the window where the current mouse cursor is located, the window handle, and the address of the window process respectively. There are also two buttons: "Hook" and "eixt ". When you press the hook, the application hooks the event message entered by the mouse, and the text of this button changes to "unhook ". When you move the mouse over a window, messages about the window are displayed in the main window. When you press "unhook", the application will uninstall the hook. The main window uses a dialog box as its main window. It defines a message wm_mousehook, which is used to transmit messages between the main window and the DLL. When the message is received in the main window, wparam contains the handle of the window where the cursor is located. Of course, this is our arrangement. I did this for convenience. You can use your own methods to communicate between the main application and DLL.
If (installhook (hwnddlg )! = NULL)
{
Hookflag = true;
Setdlgitemtext (hwnddlg, idc_hook, unhooktext );
Outputdebugstring ("hook/N ");
}

The application has a global variable, hookflag, which is used to monitor the hook status. If the hook is installed, it is true; otherwise, it is false. When you press the hook button, the application checks whether the hook has been installed. If not, it will call the installhook function in the DLL to install it. Note that the handle of the Main Dialog Box is passed to the DLL, so that the hook dll can pass the wm_mousehook message to the correct window. When the application is loaded, the hook DLL is also loaded. Once the main program is loaded into the memory, the DLL is loaded immediately. The DLL entry point function is executed before the first statement of the main program is executed. So when the main program is executed, the DLL has been initialized. Let's load the port point and put the following code:

Bool apientry dllmain (handle hmodule,
DWORD ul_reason_for_call,
Lpvoid lpreserved
)
{
G_hinstance = (hinstance) hmodule;
Return true;
}

This section of code stores the DLL instance handle in a global variable. Hinstance is always valid because the entry point function is executed before all functions are called. We put the variable in. data so that every process has its own value. This is because when you move the cursor over a window, the hook DLL is mapped to the address space of the process. If other dll has been loaded at the default DLL load address, the hook dll will be mapped to another address. Hinstance is updated to another value. When you press unhook and then the hook, setwindowshookex will be called again. This time, it uses the new address as the instance handle. In the example, this is incorrect, and the DLL loading address has not changed. This hook will become a local one. You can only hook the mouse events that occur in your window, which is hard to satisfy.

Hhook installhook (hwnd)
{
G_hwnd = hwnd;
G_hhook = setwindowshookex (wh_mouse, mouseproc, g_hinstance, 0 );
Return g_hhook;
}

The installhook function is very simple. It saves the passed window handle in hwnd for future use. Call the setwindowshookex function to install a mouse hook. The return value of this function is stored in the global variable hhook and will be used in unhookwindowshookex in the future. After setwindowshookex is called, the mouse hook starts to work. No matter when a mouse event occurs, the mouseproc function will be called:


Lresult callback mouseproc (INT ncode,
Wparam,
Lparam
)
{
Callnexthookex (g_hhook, ncode, wparam, lparam );
Lpmousehookstruct PS = (lpmousehookstruct) lparam;
Hwnd = windowfrompoint (PS-> pt );
Postmessage (g_hwnd, wm_mousehook, (wparam) hwnd, 0 );
Return 0;
}

The hook function first calls the callnexthookex function to allow other hooks to process the mouse event. Then, call the windowfrompoint function to obtain the window handle at the given screen coordinate position. Note: We use the point member variable in the mousehookstruct struct variable pointed to by lparam as the current mouse position. Call the postmessage function to send the wm_mousehook message to the main program. One thing you must remember is: Do not use the sendmessage function in the hook function, which will cause a deadlock. The definition of mousehookstruct is as follows:

typedef struct {
POINT pt;
HWND hwnd;
UINT wHitTestCode;
ULONG_PTR dwExtraInfo;
} MOUSEHOOKSTRUCT, *PMOUSEHOOKSTRUCT;

 

  • PT is the position of the screen where the current mouse is located.
  • Hwnd is the handle of the window that will receive the mouse message. Usually it is the window where the mouse is located, but if the window calls setcapture, the mouse input will be directed to this window. Because we use the windowfrompoint function instead of the member variable.
  • Whittestcode specifies the hit-test value, which gives more mouse position values. It specifies the place where the mouse is located in the window. For a complete list of values, see the wm_nchittest message in the Win32 API guide.
  • Dwextrainfo this value contains related information. Generally, this value is set by the mouse_event function and can be obtained by calling getmessageextrainfo.

 

When the wm_mousehook message is received in the main window, it uses the window handle in the wparam parameter to query the window message.

Case wm_mousehook:
Getdlgitemtext (hwnddlg, idc_handle, (lpstr) temp, 128 );
Wsprintf (lpstr) buffer, mytemplate, wparam );
If (lstrcmpi (lpcstr) buffer, (lpcstr) temp )! = 0)
Setdlgitemtext (hwnddlg, idc_handle, (lpcstr) buffer );

Getdlgitemtext (hwnddlg, idc_classname, (lpstr) temp, 128 );
Getclassname (hwnd) wparam, (lpstr) buffer, 128 );
If (lstrcmpi (lpcstr) buffer, (lpcstr) temp )! = 0)
Setdlgitemtext (hwnddlg, idc_classname, (lpcstr) buffer );

Getdlgitemtext (hwnddlg, idc_wndproc, (lpstr) temp, 128 );
Wsprintf (lpstr) buffer, mytemplate, getclasslong (hwnd) wparam, gcl_wndproc ));
If (lstrcmpi (lpcstr) buffer, (lpcstr) temp )! = 0)
Setdlgitemtext (hwnddlg, idc_wndproc, (lpcstr) buffer );
Break;

To avoid text jitter during text re-painting, we compare the text that has been edited in the space midline with the text we will display. If they are the same, they can be ignored. Get the class name, call getclassname, get the window process, call getclasslong, input the gcl_wndproc flag, format them into text strings, and put them in the relevant editing space.

Uninstallhook ();
Setdlgitemtext (hwnddlg, idc_hook, hooktext );
Hookflag = false;
Setdlgitemtext (hwnddlg, idc_classname, null );
Setdlgitemtext (hwnddlg, idc_handle, null );
Setdlgitemtext (hwnddlg, idc_wndproc, null );

After you press unhook, the main program calls the uninstallhook function in the DLL. This function calls the unhookwindowshookex function. Then, it switches the text of the button back to "Hook", sets the hookflag value to false, and then clears the text in the editing control.

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.