![]() |
General Reading »
Hardware & System »
General
Intermediate
Using hook and web service to make other programs do things they were not designed to doBy Xiangyang Liu 刘向阳Projects demonstrating how to install and use a global hook and how to call a web service within the hook procedure |
C++, Windows, Visual Studio, Dev
|
|
Advanced Search Add to IE Search |
|
|
|
||||||||||||||||
The Windows Operating System allows you to use hooks to modify the behaviors of other applications. Here is how it works: You write a DLL that exports a procedure (a global hook) to handle certain Windows events such as mouse clicks, etc. Then you use the SetWindowsHookEx API to install the hook procedure. The operating system will detect that you installed the global hook and execute the procedure code within other applications when the targeted events occur. There are some good articles on Code Project that deal with the topics of writing and using Windows hooks. You can also find complete information about Windows hooks on MSDN.
In this article, I will introduce a generic hook utility I wrote that can be reused to do some interesting or dangerous things. The demo application is a special hook that provides encode/decode capability for XML and HTML texts. Here are the 4 projects included in the download zip file:
InstallGlobalHook to install them. There are many things to watch out for when working with Windows hooks. First of all, your global hook procedure can easily slow down the whole system if you are not careful. Secondly, it can break and/or crash other programs. Thirdly, it may become a security hole for you or your users. I assume anyone who reads this article already knows the consequences and is willing to take life in his/her own hands.
Here is how to install the demo application.

To start the demo, run application WebencoderClient.exe in the subfolder WebEncoderHook. This application will be minimized when started. The special hook procedure implemented in HookModule.dll will function only when this application is running.
Now you can start notepad.exe, type in some text or open an existing file. Then highlight some text and right click the mouse "while holding down the left shift key", you will see a new context menu with 6 command items: XML Encode, XML Decode, HTML Encode, HTML Decode, URL Encode, URL Decode.
Please note that you have to press down the left shift key in order for the new context menu to appear, otherwise, you will only see the normal context menu (with commands like copy, paste, etc.) when you right click the mouse. Secondly, if you don't highlight any text, you won't see the new context menu either.
The special hook procedure in HookModule.dll will be uninstalled when you exit the WebenconderClient application and the new context menu will no longer appear after that.
What do the commands in the new context menu do? For example, if you highlighted the following text and select the XML Encode command:
<text>Hello, world</text>
the highlighted text will be replaced by the following new text:
<text>Hello, world</text>
If you select the XML Decode command, then the changed text will be restored back to the original text.
What happens behind the scenes is that when you select one of the new commands, the hook procedure will retrieve the highlighted text from the current window and send it as input to the Webencoder service. The Webencoder service will convert the text and send it back to the hook procedure. The hook procedure will then replace the highlighted text with the output from the Webencoder service.
Why do I need to use a web service in this demo? It is certainly possible to write a hook procedure that does the same things without calling any web service, however the hook procedure is written as a low level C++ DLL, it is not easy to call other advanced libraries and components. Plus, having a web service makes it easier to enhance the application to do other more complicated stuff. Another advantage is that you can install the web service on a remote computer to serve multiple client programs/machines.
To use the web service remotely, all you have to do is modify the WebEncoderURL setting in the HookModule.ini file. The HookModule.ini file must be located in the same folder as the HookModule.dll file. Here is an example of the HookModule.ini file.

You may notice that notepad.exe and wordpad.exe are listed in the HookModule.ini file: the special hook procedure will only work with programs listed in this file. To make it work with another program, you will have to add its name into the file. To add more programs, you simply add new settings [Program2], [Program3], ..., and set Name= MyNewProgramName.exe in these new settings.
The global hook procedure in HookModule.dll will interact with all programs running on the same desktop as the WebencoderClient application. If a program is not listed in the HookModule.ini file, the hook procedure will detect that and won't do anything significant. Therefore it won't slow down the whole system.
However, this tool does not work with just any Windows program. For example, it won't work with Internet Explorer or Microsoft Word. This is because the special hook procedure subclasses Windows in the targeted programs and processes regular Windows messages for these Windows. The main Windows in Internet Explorer and Microsoft Word are not regular Windows (just speculation, I have no access to the source code of Internet Explorer and Microsoft Word). Another limitation is that the tool won't work with Unicode text (yet).
The HookUtility.dll basically calls the SetWindowsHookEx API to install a hook procedure. You may ask, why do I need a DLL for this? The answer is, ease of use. You can reuse it to install multiple hook procedures. Here is the signature of the InstallGlobalHook function.
extern "C" __declspec(dllexport) HHOOK InstallGlobalHook
(int nType, char* sModulePath, char* sMethodName);
You implement your own global hook procedure in a separate DLL and call this function to install it. When calling, you need to specify the hook type (which determines what events you want your hook to process), the path of your DLL (if your DLL is in the same folder as HookUtility.dll, then you don't need to give the full path, only the name is necessary), and the name of your hook procedure. As you can see, there is no need to get module handle and procedure address, unlike calling SetWindowsHookEx directly.
The return value of this function is the handle of the installed global hook. It can be used to uninstall the hook when calling the UnhookWindowsHookEx API.
For completeness, there is also a InstallLocalHook function in the HookUtility.dll, but it is not being fully tested yet. To get a complete description about hook types and information on how to implement a hook procedure, please refer to MSDN.
Typically, a hook procedure is written in low level C/C++ code. Unless you are an expert in Win32 programming, it is hard to write code that does significant things within a hook procedure. The approach I take is implement import functions in a web service and let the hook procedure call the web service to get things done.
However, calling a web service within a hook procedure is not exactly easy. I wrote a COM component about three years ago that simplifies the task a little bit, see my Code Project article Invoking web methods from a C++ console application for details. This COM component uses the SOAP Client object from Microsoft SOAP Toolkit 3.0 to call a web service. The catch is, parameters and output of the web service have to be represented as VARIANTs. In order to create and use this com component, I used the C++ class XYDispDriver in my code which is described in another code project article of mine. Here is the code in HookModule.dll that calls the Webencoder service.
...
XYDispDriver oDriver;
if(oDriver.CreateObject("XYSoapClient.1",CLSCTX_INPROC_SERVER))
{
USES_CONVERSION;
VARIANT* pOutput = NULL;
pOutput = oDriver.InvokeMethod("InitService",pWebEncoderURL,"","","");
if(pOutput!=NULL&&pOutput->lVal!=0)
{
WCHAR* pInput = GetSelectedText(hMyWnd);
WCHAR* pTypes[] = {L"xml",L"html",L"url"};
int nType = (wParam-771)/2;
WCHAR* pAction = NULL;
if((wParam-770)%2!=0) pAction = L"Encode";
else pAction = L"Decode";
VARIANT vInput1;
vInput1.vt = VT_BSTR;
vInput1.bstrVal = ::SysAllocString(pInput);
VARIANT vInput2;
vInput2.vt = VT_BSTR;
vInput2.bstrVal = ::SysAllocString(pTypes[nType]);
VARIANT vInput3;
vInput3.vt = VT_BSTR;
vInput3.bstrVal = ::SysAllocString(pAction);
VARIANT vInput4;
vInput4.vt = VT_EMPTY;
VARIANT vInput5;
vInput5.vt = VT_EMPTY;
pOutput = oDriver.InvokeMethod
("InvokeMethod","Process",vInput1,vInput2,vInput3,vInput4,vInput5);
...
The DLLs in the projects are dynamically loaded. This provides some flexibility but makes it harder to debug the code. I have to write a test program that statically links to all the DLLs to debug the code. If you build the code in debug mode, then you will also get a log file HookModuleLog.txt that contains import trace information. The log file will be located in the current working directories of the programs in which the hook procedure is running.
The Webencoder service in the article is written with Visual Studio .NET 2003. The other programs were written with Visual Studio 6.0.
I am not a Win32 expert and this is the first time I write code that implements a Windows hook. Please correct me if I left out anything or misspoke about some facts. Thanks.
| You must Sign In to use this message board. | ||||||||
|
||||||||
|
||||||||
|
||||||||
|
||||||||
General
News
Question
Answer
Joke
Rant
Admin
|
PermaLink |
Privacy |
Terms of Use
Last Updated: 20 Feb 2006 Editor: Deeksha Shenoy |
Copyright 2006 by Xiangyang Liu 刘向阳 Everything else Copyright © CodeProject, 1999-2009 Web22 | Advertise on the Code Project |