Getting rid of the Start button – Adding a Hook

September 16, 2013

In the previous post we saw how to find and remove the start button and move the task bar window to the left to occupy the free space left by the former start button.

However, we saw that by opening the system tray, the task bar moves back to its original position. We need to know when that happens, and then use the same trick to move it back to the “right” position.

To do that we would need to register somehow for the WM_MOVE message. This is one option, and we can verify this using Spy++’s Message window for task bar (again, another exercise for the reader).

The problem is that in Windows, a window message is posted or sent to the thread that created the window. Since the task bar was created by some thread in Explorer.Exe, clearly we can’t just expect that message to arrive at our doorstep. For that, we need a hook.

The term “hook” signifies some kind of interception mechanism, and indeed this is its meaning in this case. We want to know when the task bar’s creator thread receives such a message and act accordingly.

This kind of hook is supported by the Windows subsystem through the SetWindowsHookEx API function. It allows (among other things) to look at messages targeted at a particular thread (what we need) or even every thread on the current desktop (which may be useful for system-wide, or at least desktop-wide, hooking).

Before we call this function, we need to prepare a few things. The first and most important thing is to create a DLL. This is necessary, as our hook function is called in the context of the thread that created that window, which means it’s running under an Explorer.Exe process. The only way to provide our own function in this way is to place it in a DLL. The hooking API will inject our DLL into Explorer’s process automatically.

Another thing to bear in mind is that the DLL must match the “bitness” of Explorer.Exe, meaning it has to be 32 bit on 32 bit Windows, but it must be 64 bit on 64 bit Windows, because a 64 bit process cannot load a 32 bit DLL (or vice versa).

Add a new project to the existing solution of type “Win32 Application” and select a DLL project with a suitable name, like so (you can check the Export Symbols option if you want):

image

Next, we’ll link with the DLL in our hosting EXE, like so:

  1. #ifdef _WIN64
  2. #pragma comment(lib, "..\\x64\\Debug\\RemoverHook.lib")
  3. #else
  4. #pragma comment(lib, "..\\Debug\\RemoverHook.lib")
  5. #endif

We need to link with the correct LIB based on bitness. For x64 projects, the _WIN64 macro is defined and so leveraged here.

The next step is to create the actual hook function in the DLL; this must of a certain prototype, as mandated by the SetWindowsHookEx function:

  1. LRESULT CALLBACK HookFunction(int code, WPARAM wParam, LPARAM lParam) {

To make the function visible outside the DLL, we’ll add a DEF file (File / Add New Item) and place the function’s name (HookFunction in the example under the EXPORTS part):

  1. LIBRARY
  2. EXPORTS
  3.     HookFunction

Before we complete the function’s body, let’s set up the hook in the EXE. After we got a handle to the ReBar control hosting the Task Bar, we need to find out the thread that created it (inside Explorer):

  1. DWORD tid = ::GetWindowThreadProcessId(hReBar, nullptr);

GetWindowThreadProcessId returns the thread ID of the window’s creator and optionally the process ID as well (not interesting for us, hence the nullptr). Now we can set up the hook:

  1. HINSTANCE hDll = ::GetModuleHandle(L"RemoverHook.dll");
  2. HOOKPROC hookProc = (HOOKPROC)::GetProcAddress(hDll, "HookFunction");
  3. s_hHook = ::SetWindowsHookEx(WH_CALLWNDPROCRET, hookProc, hDll, tid);

We get the HINSTANCE of the DLL and the address of HookFunction and call SetWindowsHookEx with a WH_CALLWNDPROCRET hook type, which provides messages after they have been processed by the target thread; we could have used another hook where we see the message before it gets handled and we could even cancel it, but this is good enough.

The hook handle (s_hHook) is stored in a shared memory section inside the DLL, along with the ReBar control handle, so that both processes (our hosting process and the injected process, Explorer.Exe) have easy access to these:

  1. #pragma data_seg("Shared")
  2. __declspec(dllexport) HHOOK s_hHook = nullptr;
  3. __declspec(dllexport) HWND s_hRebarWnd = nullptr;
  4. #pragma data_seg()
  5. #pragma comment(linker, "/section:Shared,RWS")

This is a nice trick to get shared memory without resorting to memory mapped file objects. The last line has “RWS” as attributes to the data segment, where “S” is the important part, making it shared. Without it, these variables would be private to each process, just like any global variable in a DLL.

Next, we implement the hook function like so:

  1. LRESULT CALLBACK HookFunction(int code, WPARAM wParam, LPARAM lParam) {
  2.     if(code < 0)
  3.         return ::CallNextHookEx(s_hHook, code, wParam, lParam);
  4.     auto msg = (CWPRETSTRUCT*)lParam;
  5.     if(msg->message != WM_MOVE || msg->hwnd != s_hRebarWnd)
  6.         return ::CallNextHookEx(s_hHook, code, wParam, lParam);
  7.     if(s_hRebarWnd) {
  8.         RECT rc;
  9.         ::GetWindowRect(s_hRebarWnd, &rc);
  10.         ::SetWindowPos(s_hRebarWnd, nullptr, 0, 0, rc.right, rc.bottom – rc.top, SWP_NOZORDER | SWP_NOACTIVATE);
  11.     }
  12.     return ::CallNextHookEx(s_hHook, code, wParam, lParam);
  13. }

If the hook action code is zero or above, we can act. The documentation states that lParam is a pointer to a structure, CWPRETSTRUCT, containing all the information about the message. if it’s in fact WM_MOVE and the window is the ReBar control, then we simply move it to the left in the same way it was done from the hosting executable and that’s it.

One final thing to do – our EXE can’t exit – if it does, the hook is automatically “unhooked”, so a simple call to Sleep(INFINITE) would keep the process alive and unobtrusive.

Just as a final proof, we can open Process Explorer, and look at Explorer.Exe for our injected DLL:

image

If you’re feeling like it, you can add some GUI and allow removing and restoring the Start button, if you so desire. The complete source code can be downloaded here.

Add comment
facebook linkedin twitter email

Leave a Reply

Your email address will not be published.

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>

*

one comment

  1. stevOctober 15, 2016 ב 16:10

    Please re-upload the source code. Links is broken. Thanks!

    Reply