天天看點

[Win32] ShellHook的實作

本博文由CSDN部落客zuishikonghuan所作,版權歸zuishikonghuan所有,轉載請注明出處: http://blog.csdn.net/zuishikonghuan/article/details/49133099

啟動Windows,加載Win32子系統,登入一個使用者會話後,會啟動Shell程式,預設是explorer.exe,就是我們看到的桌面和工作列等,其實Shell是可以自定義的,我們完全可以自己編寫一個Shell取代系統的Shell。下面我們來看看編寫Windows Shell的核心技術之一——ShellHook。

ShellHook是什麼?當一個視窗建立、激活、關閉時,explorer總是能夠捕獲相應的消息,并更新工作列上,就是通過ShellHook實作的。

explorer總是能夠捕獲相應的消息,并更新工作列上,最初,我猜測explorer是通過timer定時調用枚舉視窗,或者使用全局消息鈎子實作,但是這兩種方法對系統資源的消耗是巨大的,其實,要想實作這一功能,使用ShellHook即友善又穩定,而且還沒有資源占用大的問題。

完整示例源碼如下(注釋很詳細):

#include <windows.h>
#pragma comment(lib,"user32.lib")
#pragma comment(lib,"gdi32.lib")
//#pragma comment(linker,"/manifestdependency:\"type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='x86' publicKeyToken='6595b64144ccf1df' language='*'\"")

LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam);

WNDCLASS wc;
const TCHAR* AppName = TEXT("MyWindowClass1");

HWND hwnd1;
HWND edit2 = 0;

//定義ShellHook的消息
static UINT WM_SHELLHOOKMESSAGE;

int APIENTRY _tWinMain(_In_ HINSTANCE hInstance,
	_In_opt_ HINSTANCE hPrevInstance,
	_In_ LPTSTR    lpCmdLine,
	_In_ int       nCmdShow)
{
	//這裡是在建構視窗類結構
	wc.style = CS_HREDRAW | CS_VREDRAW;
	wc.lpfnWndProc = WndProc;//視窗回調函數指針
	wc.cbClsExtra = 0;
	wc.cbWndExtra = 0;
	wc.hInstance = hInstance;//執行個體句柄
	wc.hIcon = LoadIcon(hInstance, TEXT("ICON_1"));
	wc.hCursor = LoadCursor(NULL, IDC_ARROW);//預設指針
	wc.hbrBackground = (HBRUSH)(COLOR_WINDOW);//預設背景顔色
	wc.lpszMenuName = NULL;
	wc.lpszClassName = AppName;//視窗類名

	//注冊視窗類
	if (!RegisterClass(&wc))
	{
		MessageBox(NULL, TEXT("注冊視窗類失敗!"), TEXT("錯誤"), MB_ICONERROR);
		return 0;
	}

	//建立視窗
	int style = WS_OVERLAPPEDWINDOW;
	hwnd1 = CreateWindowEx(WS_EX_TOPMOST, AppName, TEXT("ShellHook"), style, 50, 50, 500, 500, 0, NULL, hInstance, 0);
	if (hwnd1 == NULL)
	{
		MessageBox(NULL, TEXT("建立視窗失敗!"), TEXT("錯誤"), MB_ICONERROR);
		return 0;
	}

	//顯示、更新視窗
	ShowWindow(hwnd1, nCmdShow);
	UpdateWindow(hwnd1);

	//消息循環
	MSG msg;
	while (GetMessage(&msg, NULL, 0, 0))
	{
		TranslateMessage(&msg);
		DispatchMessage(&msg);
	}
	return msg.wParam;
}

void AddEditText(TCHAR* string, HWND hwnd)//向edit control添加字元串
{
	int len = GetWindowTextLength(hwnd);//擷取edit control字元個數
	SendMessage(hwnd, EM_SETSEL, len, len);//将選中設定為末尾
	SendMessage(hwnd, EM_REPLACESEL, 0, (LPARAM)string);//将末尾替換為指定的字元串
}

LRESULT CALLBACK WndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
	switch (uMsg){
	case WM_CREATE:
		//建立edit control
		edit2 = CreateWindowEx(WS_EX_CLIENTEDGE, TEXT("Edit"), TEXT(""), WS_CHILD | WS_VISIBLE | ES_MULTILINE | ES_WANTRETURN | WS_VSCROLL | ES_AUTOVSCROLL, 5, 5, 400, 450, hwnd, (HMENU)8, (HINSTANCE)GetWindowLong(hwnd, GWL_HINSTANCE), NULL);
		SendMessage(edit2, WM_SETFONT, (WPARAM)GetStockObject(17), 0);

		//進行ShellHook
		WM_SHELLHOOKMESSAGE = RegisterWindowMessage(TEXT("SHELLHOOK"));
		if (!RegisterShellHookWindow(hwnd))MessageBox(hwnd, TEXT("shellhook失敗"), TEXT(""), 0);

		break;
	case WM_DESTROY://視窗已經銷毀
		PostQuitMessage(0);//退出消息循環,結束應用程式
		return 0;
		break;
	default:
		break;
	}
	//這裡是處理ShellHook的回調
	HWND hwin;
	if (uMsg == WM_SHELLHOOKMESSAGE){
		TCHAR tmp[50] = {0};
		hwin = (HWND)lParam;//得到視窗句柄

		//判斷Shell消息類型
		if (wParam == HSHELL_WINDOWACTIVATED || wParam == HSHELL_RUDEAPPACTIVATED){
			AddEditText(TEXT("[視窗激活]"), edit2);
			goto print;
		}
		if (wParam == HSHELL_WINDOWDESTROYED){
			AddEditText(TEXT("[視窗關閉]"), edit2);
			goto print;
		}
		goto end;
	print:
		//這裡是判斷一下視窗風格和擴充風格,如果是toolwindow或者子視窗可以直接過濾掉,但這裡隻是輸出是特殊視窗,但不過濾
		DWORD style;
		style = GetWindowLongPtr(hwin, GWL_EXSTYLE);//擷取視窗擴充風格
		if (style & WS_EX_TOOLWINDOW){//使用位與運算判斷
			AddEditText(TEXT("ToolWindow "), edit2);
		}
		style = GetWindowLongPtr(hwin, GWL_STYLE);
		if (style & WS_CHILD){
			AddEditText(TEXT("Child "), edit2);
		}
		_itot((DWORD)hwin, tmp, 10);//将視窗句柄轉換為TCHAR字元串
		AddEditText(tmp, edit2);
		AddEditText(TEXT(" - "), edit2);
		DWORD len = GetWindowTextLength(hwin);//擷取視窗标題長度
		len = len * sizeof(TCHAR);//GetWindowTextLength得到的是字元數而不是位元組數 
		//配置設定記憶體
		TCHAR* til = (TCHAR*)VirtualAlloc(NULL, len + 2, MEM_COMMIT, PAGE_READWRITE);//TCHAR* til = new TCHAR[len + 1];
		RtlZeroMemory(til, len + 2);//或者:til[len]='\0';
		GetWindowText(hwin, til, len/sizeof(TCHAR) + 2);//擷取視窗标題
		AddEditText(til, edit2);
		AddEditText(TEXT("\r\n"), edit2);//添加換行符
		//釋放資源
		VirtualFree(til, len + 2, MEM_DECOMMIT);//delete[] til;
		return 0;
	end:
		;
	}
	return DefWindowProc(hwnd, uMsg, wParam, lParam);//其他消息交給系統處理
}
           

效果圖:

[Win32] ShellHook的實作

  

繼續閱讀