Advanced MFC Programming

Advanced MFC Programming Advanced MFC Programming

math.hcmuns.edu.vn
from math.hcmuns.edu.vn More from this publisher
11.04.2014 Views

Chapter 13. Adding Special Features to Application window handle to the DLL, we can call a function exported from the DLL when the hook is installed, and ask the DLL to keep this handle for later use. However, data stored in the DLL is slightly different from data stored in a common process. For a normal process, it has its own memory space, the static variables stored in this space will not change throughout their lifetime. However, for a DLL, since it can be shared by several processes, its variables are mapped to the application’s memory space separately. By doing this, for a variable contained in the DLL, different applications may have different values. This will eliminate data conflicting among different processes. To prevent the value of a variable stored in DLL from being changed, we can define a custom data segment to store the variable. Defining Data Segment In order to specify a special data segment for storing such type of variables, we can use macro #pragma data_seg to specify the data segment, and use -SEGMENT switch to link the project. DLL Implementation Sample 13.10\Hook demonstrates keyboard hook implementation. The hook procedure stays in a separate DLL file: “HookLib.Dll”. Creating a DLL is slightly different from creating MFC based applications, there is no skeleton generated at the beginning. After using the application wizard to generate a Win32 based DLL project, we are not provided with a single line of source code. Since our DLL is relatively small, we can use just one “.c” and “.h” file to implement it. We can create these two files by opening new text files, then executing command Project | Add To Project | Files... to add them into the project. In the sample, the DLL is implemented by “HookLib.h” and “HookLib.c” files. File “HookLib.h” declares all the functions that will be included in the DLL: #if !defined(__HOOKLIB_H__) #define __HOOKLIB_H__ #include "Windows.h" #include "BaseTyps.h" #if !defined(__DLLIMPORT__) #define DLLENTRY EXTERN_C __declspec(dllexport) #define STDENTRY EXTERN_C __declspec(dllexport) HRESULT STDAPICALLTYPE #define STDENTRY_(type) EXTERN_C __declspec(dllexport) type STDAPICALLTYPE BOOL CALLBACK LibMain(HANDLE, DWORD, LPVOID); int CALLBACK WEP(int); LRESULT CALLBACK KeyboardProc(int, WPARAM, LPARAM); #else #define DLLENTRY EXTERN_C __declspec(dllimport) #define STDENTRY EXTERN_C __declspec(dllimport) HRESULT STDAPICALLTYPE #define STDENTRY_(type) EXTERN_C __declspec(dllimport) type STDAPICALLTYPE #endif STDENTRY_(BOOL) SetKeyboardHook(HWND, HINSTANCE); STDENTRY_(BOOL) UnsetKeyboardHook(); #endif Function LibMain(…) and WEP(…) are the entry point and the exit point of the DLL respectively. The reason for using so many #if macros here is that it enables us to use the same header file for both the DLL and the application that links the DLL. As we build the DLL, we want to export functions so that they can be called outside the DLL; in the application, we need to import these functions from the DLL. Macro __declspec(dllimport) declares an import function and __declspec(dllexport) declares an export 416

Chapter 13. Adding Special Features to Application function. As we can see, if macro __DLLIMPORT__ is defined, function LibMain(…), WEP(…) and KeyboardProc(…) will be declared (they will be used only in the DLL). In this case, two other functions SetKeyboardHook(…) and UnsetKeyboardHook() will be declared as import function. If the macro is not defined, the two functions will be declared as export functions. The reason for using macro EXTERN_C is that the DLL is built with C convention and our application is built with C++ convention. To make them compatible, we must explicitly specify how to build the functions in the DLL. In the sample, two functions are exported from the DLL: SetKeyboardHook(…) will be used by the application to install hook; UnsetKeyboardHook() will be used to remove the hook. In file “HookLib.c”, first two static variables are declared: #pragma data_seg("SHARDATA") static HWND g_hWnd=NULL; static HHOOK g_hHook=NULL; #pragma data_seg() We use #pragma data_seg("SHARDATA") to specify that g_hWnd and g_hHook will be stored in "SHARDATA" segment instead of being mapped to calling processes. Function SetKeyboardHook(…) installs system wide keyboard hook by calling function SetWindowsHookEx(…). When using this function, we must provide the instance handle of the DLL library and the handle of the application’s mainframe window: STDENTRY_(BOOL) SetKeyboardHook(HWND hWnd, HINSTANCE hInstance) { g_hHook=SetWindowsHookEx ( WH_KEYBOARD, KeyboardProc, hInstance, 0 ); if(g_hHook == FALSE) { return FALSE; } g_hWnd=hWnd; } return TRUE; The handle of application’s mainframe window is stalled in variable g_hWnd for later use. The handle of the hook is stored in variable g_hHook and will be used in function UnsetKeyboardHook() to remove the keyboard hook: STDENTRY_(BOOL) UnsetKeyboardHook() { return UnhookWindowsHookEx(g_hHook); } Function KeyboardProc(…) is the hook procedure. When there is a keystroke, this function will be called, and the keystroke information will be processed within this function: LRESULT CALLBACK KeyboardProc(int code, WPARAM wParam, LPARAM lParam) { if(code < 0 || code != HC_ACTION) { return CallNextHookEx(g_hHookKeyboard, code, wParam, lParam); } if(code == HC_ACTION) { int nKeyState; nKeyState=GetKeyState(VK_CONTROL); if(lParam & 0x80000000 || lParam & 0x40000000) { 417

Chapter 13. Adding Special Features to Application<br />

function. As we can see, if macro __DLLIMPORT__ is defined, function LibMain(…), WEP(…) and<br />

KeyboardProc(…) will be declared (they will be used only in the DLL). In this case, two other functions<br />

SetKeyboardHook(…) and UnsetKeyboardHook() will be declared as import function. If the macro is not<br />

defined, the two functions will be declared as export functions.<br />

The reason for using macro EXTERN_C is that the DLL is built with C convention and our application is<br />

built with C++ convention. To make them compatible, we must explicitly specify how to build the<br />

functions in the DLL. In the sample, two functions are exported from the DLL: SetKeyboardHook(…) will<br />

be used by the application to install hook; UnsetKeyboardHook() will be used to remove the hook.<br />

In file “HookLib.c”, first two static variables are declared:<br />

#pragma data_seg("SHARDATA")<br />

static HWND g_hWnd=NULL;<br />

static HHOOK g_hHook=NULL;<br />

#pragma data_seg()<br />

We use #pragma data_seg("SHARDATA") to specify that g_hWnd and g_hHook will be stored in<br />

"SHARDATA" segment instead of being mapped to calling processes.<br />

Function SetKeyboardHook(…) installs system wide keyboard hook by calling function<br />

SetWindowsHookEx(…). When using this function, we must provide the instance handle of the DLL library<br />

and the handle of the application’s mainframe window:<br />

STDENTRY_(BOOL) SetKeyboardHook(HWND hWnd, HINSTANCE hInstance)<br />

{<br />

g_hHook=SetWindowsHookEx<br />

(<br />

WH_KEYBOARD,<br />

KeyboardProc,<br />

hInstance,<br />

0<br />

);<br />

if(g_hHook == FALSE)<br />

{<br />

return FALSE;<br />

}<br />

g_hWnd=hWnd;<br />

}<br />

return TRUE;<br />

The handle of application’s mainframe window is stalled in variable g_hWnd for later use. The handle<br />

of the hook is stored in variable g_hHook and will be used in function UnsetKeyboardHook() to remove the<br />

keyboard hook:<br />

STDENTRY_(BOOL) UnsetKeyboardHook()<br />

{<br />

return UnhookWindowsHookEx(g_hHook);<br />

}<br />

Function KeyboardProc(…) is the hook procedure. When there is a keystroke, this function will be<br />

called, and the keystroke information will be processed within this function:<br />

LRESULT CALLBACK KeyboardProc(int code, WPARAM wParam, LPARAM lParam)<br />

{<br />

if(code < 0 || code != HC_ACTION)<br />

{<br />

return CallNextHookEx(g_hHookKeyboard, code, wParam, lParam);<br />

}<br />

if(code == HC_ACTION)<br />

{<br />

int nKeyState;<br />

nKeyState=GetKeyState(VK_CONTROL);<br />

if(lParam & 0x80000000 || lParam & 0x40000000)<br />

{<br />

417

Hooray! Your file is uploaded and ready to be published.

Saved successfully!

Ooh no, something went wrong!