vc SDK山寨spy++ FinderTool工具(靶心控件)纯GDI自绘

GDI编程本人不是很懂,以下代码,是之前还在MFC写代码的时候,度娘爬楼,挖坟千百次的成果,注释得已经比较详细了,反正我知道的都注释了,我不知道的我也没办法了。

现在只是把这个代码转到win32 SDK来写,实际上和MFC区别没多大,如果你需要MFC的,可以去下载我之前发布的资源来看:http://download.csdn.net/detail/gnorth/4982016

这个类,本意是继承来用的,继承了你可以不用看到这么多恶心的代码,你只关心你自己的代码即可,继承过去,重载了OnFinderWindow,在控件选取到一个窗口时,会把窗口句柄传到这个函数里,然后你可以根据该句柄取出它的各种信息,标题啊,类名之类的。

继承该类,创建派生类对象,调用Initialize成员,将一个STATIC图像控件的窗口句柄传进去,就可以了。

STATIC控件必须为图标类型,必须具有唯一的ID(不能为默认的IDC_STATIC),因为要响应消息。

然后如果还依赖什么头文件,就自己百度下吧,我的库里面有很多东西,我也分不清到底依赖些什么了。

代码反正是新出炉的,VC6下可用,其他版本的VS可能有需要改的地方,示例程序我过几天再写出来吧,SDK的窗口程序纯手打会很累的。


//H文件

namespace blx
{
	class fwt
	{
	public:
		fwt();
		virtual ~fwt();
		
	public:
		HWND m_hWnd;
		void Initialize(HWND hWnd);
		void Uninitialize();
		
	protected:
		virtual void OnFinderWindow(HWND hWnd);
		
	private:
		void OnChangeIcon(BOOL bState);
		HICON m_hDownIco, m_hUpIco, m_hOldIcon;
		HCURSOR m_hCursor;
		HPEN m_hFramePen;
		HBRUSH m_hFrameBrush;
		HANDLE m_hThread;
		friend void init_ico(fwt &obj);
		friend void init_cursor(fwt &obj);
		static LRESULT CALLBACK fwtWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
		DWORD WINAPI _ThreadProc();
		WNDPROC _fwtOldWndProc;
	};
}

void FillSolidRect(HDC hDC, LPCRECT lpRect, COLORREF clr)
{
	SetBkColor(hDC, clr);
	ExtTextOut(hDC, 0, 0, ETO_OPAQUE, lpRect, NULL, 0, NULL);
}
	
void FillSolidRect(HDC hDC, int left, int top, int right, int bottom, COLORREF clr)
{
	RECT rect = {left, top, right, bottom};
	SetBkColor(hDC, clr);
	ExtTextOut(hDC, 0, 0, ETO_OPAQUE, &rect, NULL, 0, NULL);
}

namespace blx
{
	fwt::fwt()
	{
		m_hFramePen = CreatePen(PS_SOLID, 5, 0);
		m_hFrameBrush = (HBRUSH)GetStockObject(NULL_BRUSH);
	}


	void fwt::Initialize(HWND hWnd)
	{
		m_hWnd = hWnd;
		init_ico(*this);//画图标
		init_cursor(*this);//画鼠标
		m_hOldIcon = (HICON)SendMessage(m_hWnd, STM_GETICON, NULL, NULL);//获取STATIC控件本来的图标
		SendMessage(m_hWnd, STM_SETICON, (WPARAM)m_hUpIco, NULL);//初始化设置STATIC控件为弹起时的图标
		SetWindowLong(m_hWnd, GWL_STYLE, GetWindowLong(m_hWnd, GWL_STYLE) | SS_NOTIFY);
		SetWindowLong(m_hWnd, GWL_USERDATA, (LONG)this);//因为要响应鼠标按下和弹起,所以把类成员放到用户数据上
		_fwtOldWndProc = (WNDPROC)SetWindowLong(m_hWnd, GWL_WNDPROC, (LONG)fwtWndProc);//拿到该控件的消息函数
	}

	void fwt::Uninitialize()
	{
		//各种还原与释放
		while(m_hThread != NULL)
			Sleep(1);
		SetWindowLong(m_hWnd, GWL_WNDPROC, (LONG)_fwtOldWndProc);
		SendMessage(m_hWnd, STM_SETICON, (WPARAM)m_hOldIcon, NULL);
		DestroyIcon(m_hDownIco);
		DestroyIcon(m_hUpIco);
		DestroyCursor(m_hCursor);
		DeleteObject(m_hFramePen);
		m_hWnd = NULL;
		
	}


	fwt::~fwt()
	{
		if(m_hWnd)
			Uninitialize();
	}

	void fwt::OnChangeIcon(BOOL bState)
	{
		if(bState)
		{
			//按下鼠标左键时
			SetCapture(m_hWnd);//捕获鼠标
			SetCursor(m_hCursor);//设置鼠标造型为我们画的那个靶心
			SendMessage(m_hWnd, STM_SETICON, (WPARAM)m_hDownIco, NULL);//设置图标为没靶心的图标
			typedef DWORD (WINAPI fwt::*_TEPROC)();
			_TEPROC __proc = &fwt::_ThreadProc;
			LPTHREAD_START_ROUTINE _proc;
			__asm{
				mov eax, __proc
					mov _proc, eax
			}
			m_hThread = CreateThread(NULL, 0, _proc, this, 0, NULL);
			//因为这个线程入口是一个类成员,所以要先拿到它的地址,再来启动
			//实际上,线程入口可以用静态成员或者友元函数,把参数传过去,一样能够访问到类中的私有成员。
		}
		else
		{
			//弹起时释放鼠标
			ReleaseCapture();
			SendMessage(m_hWnd, STM_SETICON, (WPARAM)m_hUpIco, NULL);//设置为有靶心的造型
			WaitForSingleObject(m_hThread, INFINITE);//等待线程结束
			CloseHandle(m_hThread);
			m_hThread = NULL;
		}
	}


	void fwt::OnFinderWindow(HWND hWnd)
	{
		//这个虚函数,是给你继承这个类去重载来用的,你继承过去搞,就不用随时看到这些恶心的代码,写起程序来会舒服点不会吐
		
	}


	LRESULT CALLBACK fwt::fwtWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
	{
		fwt *thisPtr = (fwt*)GetWindowLong(hWnd, GWL_USERDATA);
		if(uMsg == WM_LBUTTONDOWN || uMsg == WM_LBUTTONUP)
			thisPtr->OnChangeIcon(uMsg == WM_LBUTTONDOWN);
		return CallWindowProc(thisPtr->_fwtOldWndProc, hWnd, uMsg, wParam, lParam);
	}


	DWORD WINAPI fwt::_ThreadProc()
	{
		//因为要在桌面上画橡皮套,所以感觉线程能更好点,所以就用了线程来处理
		
		POINT point;
		HWND hWnd = NULL, hLastWnd = NULL;
		HDC hDC = GetDC(NULL);//获取桌面DC
		SetROP2(hDC, R2_NOTXORPEN);//异或笔
		HPEN hOldPen = (HPEN)SelectObject(hDC, m_hFramePen);//选入准备好的画笔
		HBRUSH hOldBrush = (HBRUSH)SelectObject(hDC, m_hFrameBrush);//选入透明背景画刷
		RECT rect = {0,0,0,0};
		while(GetKeyState(VK_LBUTTON) & 0x80)//当鼠标左键被按下时循环
		{
			GetCursorPos(&point);
			hWnd = WindowFromPoint(point);//获取鼠标指向的窗口
			
			if(hLastWnd != hWnd && hWnd != m_hWnd)//最后获取的窗口与鼠标指向的窗口不想等,并且鼠标指向的窗口不是FinderTool自己时
			{
				Rectangle(hDC, rect.left, rect.top, rect.right, rect.bottom);//将上一次绘制的橡皮套抹掉,因为我们是用异或笔画的,所以再画一次自然就将之前的擦除了
				Sleep(8);//适当的延时
				hLastWnd = hWnd;
				GetWindowRect(hWnd, &rect);//获取当前窗口的位置
				Rectangle(hDC, rect.left, rect.top, rect.right, rect.bottom);//绘制橡皮套
				OnFinderWindow(hWnd);//通知响应函数
			}
			Sleep(8);
		}
		Rectangle(hDC, rect.left, rect.top, rect.right, rect.bottom);//把最后绘制的橡皮套擦掉
		
		SelectObject(hDC, hOldBrush);//恢复画刷
		SelectObject(hDC, hOldPen);//恢复画笔
		ReleaseDC(NULL, hDC);//释放DC
		
		//因为是直接在桌面上画橡皮套,可能有时候某些地方擦不干净之类的问题,因为桌面这个东西可能会被其他程序也来画两笔,或者某些程序刷新一下桌面。
		//这时候把我们画过的地方刷没了,最后我们为了自己恢复自己曾经画过的痕迹,再画一次,那就会有残留了。
		//这个问题留给你自己去解决了,我反正暂时就这么用了。
		
		hWnd = GetParent(m_hWnd);
		GetWindowRect(hWnd, &rect);
		InvalidateRect(hWnd, &rect, FALSE);
		return 0;
	}


	void init_ico(fwt &obj)
	{
		HDC hDC = GetDC(obj.m_hWnd);
		HDC hMemDC = CreateCompatibleDC(hDC);
		HBITMAP hMemBitmap = CreateCompatibleBitmap(hDC,31,30);
		HBITMAP hOldBitmap = (HBITMAP)SelectObject(hMemDC, hMemBitmap);
		FillSolidRect(hMemDC, 0, 0, 31, 30, 0);//填充黑色边框
		FillSolidRect(hMemDC, 1, 5, 30, 29, 0xFFFFFF);//填充白色区域
		
		//其实个人感觉,不要下面这几行代码,然后中间的靶心换成黑色的会更酷一点。
		//本例是示例模仿,所以尽可能把外框搞的更像点,至于中间的靶心,真要模仿成一样,用代码画起来太累了,还是搞点简单的吧。
		
		FillSolidRect(hMemDC, 5, 1, 26, 4, 0xFF0000);//填充蓝色区域
		FillSolidRect(hMemDC, 1, 1, 4, 4, 0xC0C0C0);//填充左上角灰色区域
		FillSolidRect(hMemDC, 27, 1, 30, 4, 0xC0C0C0);//填充右上角灰色区域
		MoveToEx(hMemDC, 2, 2, NULL);//画左上角灰色区域中间的黑点
		LineTo(hMemDC, 3, 3);
		MoveToEx(hMemDC, 28, 2, NULL);//画右上角灰色区域中间的黑点
		LineTo(hMemDC, 29, 3);
		
		//////////////////////////////////////////////////////////////////////
		//到这里,按下时的图标就画完了,用hMemBitmap来创建图一个HICON
		ICONINFO icoi;
		icoi.fIcon = TRUE;
		icoi.xHotspot = 0;
		icoi.yHotspot = 0;
		icoi.hbmMask = hMemBitmap;
		icoi.hbmColor = hMemBitmap;
		obj.m_hDownIco = CreateIconIndirect(&icoi);
		
		//接下来画没有按下鼠标时的造型
		//之前绘制的按下时的造型,还在内存中,所以在这里接着画就接着那个造型画几笔,就完成了
		
		//创建红色画笔并选入内存DC
		HPEN hPen = CreatePen(PS_SOLID, 1, 0x0000FF);
		HPEN hOldPen = (HPEN)SelectObject(hMemDC, hPen);
		
		MoveToEx(hMemDC, 15, 8, NULL);//上
		LineTo(hMemDC, 15, 14);
		
		MoveToEx(hMemDC, 7, 16, NULL);//左
		LineTo(hMemDC, 13, 16);
		
		MoveToEx(hMemDC, 15, 19, NULL);//下
		LineTo(hMemDC, 15, 25);
		
		MoveToEx(hMemDC, 18, 16, NULL);//右
		LineTo(hMemDC, 24, 16);
		
		MoveToEx(hMemDC, 15, 16, NULL);//中间红点
		LineTo(hMemDC, 16, 17);
		
		icoi.fIcon = TRUE;
		icoi.xHotspot = 0;
		icoi.yHotspot = 0;
		icoi.hbmMask = hMemBitmap;
		icoi.hbmColor = hMemBitmap;
		obj.m_hUpIco = CreateIconIndirect(&icoi);//创建
		
		
		//各种恢复与释放
		SelectObject(hMemDC, hOldPen);
		SelectObject(hMemDC, hOldBitmap);
		DeleteObject(hPen);
		DeleteObject(hMemBitmap);
		DeleteDC(hMemDC);
		ReleaseDC(obj.m_hWnd, hDC);
	}

	void init_cursor(fwt &obj)
	{
		//画鼠标也就是只有红色的那几笔,非常简单
		
		HDC hDC = GetDC(obj.m_hWnd);
		HDC hColorDC = CreateCompatibleDC(hDC);
		//创建颜色图像并选入内存DC
		HBITMAP hColorBitmap = CreateCompatibleBitmap(hDC, 17, 17);
		HBITMAP hOldColorBitmap = (HBITMAP)SelectObject(hColorDC, hColorBitmap);
		
		//创建红色画笔并选入内存DC
		HPEN hColorPen = CreatePen(PS_SOLID, 1, 0x0000FF);
		HPEN hOldColorPen = (HPEN)SelectObject(hColorDC, hColorPen);
		
		FillSolidRect(hColorDC, 0,0,17,17,0);
		
		MoveToEx(hColorDC, 8, 0, NULL);//上
		LineTo(hColorDC, 8, 6);
		
		MoveToEx(hColorDC, 0, 8, NULL);//左
		LineTo(hColorDC, 6, 8);
		
		MoveToEx(hColorDC, 8, 11, NULL);//下
		LineTo(hColorDC, 8, 17);
		
		MoveToEx(hColorDC, 11, 8, NULL);//右
		LineTo(hColorDC, 17, 8);
		
		MoveToEx(hColorDC, 8, 8, NULL);//中
		LineTo(hColorDC, 9, 9);
		
		//这里需要注意鼠标需要透明,需要热点
		//鼠标透明需要传入一个二值化的图像,白色的就是透明的,不然就是正常显示颜色的
		//所以根据上面绘制好的图像来创建一个二值化图像
		
		HDC hMackDC = CreateCompatibleDC(hDC);
		HBITMAP hMaskBitmap = CreateCompatibleBitmap(hDC, 17, 17);
		HBITMAP hOldMaskBitmap = (HBITMAP)SelectObject(hMackDC, hMaskBitmap);
		
		for(int x = 0; x < 17; x++)
		{
			for(int y = 0; y < 17; y++)
			{
				if(!GetPixel(hColorDC, x, y))
					SetPixel(hMackDC, x, y, 0xFFFFFF);
			}
		}
		
		//这里很重要,在创建Icon之前,必须把对象选出,如果创建前没选出,那创建出来就不会是透明的玩意。
		//具体为什么会这样,我反正不懂,也不需要我非要去懂,但我还是希望你懂了之后来告诉我为啥(我先拦下砖头)
		
		SelectObject(hColorDC, hOldColorBitmap);
		SelectObject(hColorDC, hOldColorPen);
		SelectObject(hMackDC, hOldMaskBitmap);
		DeleteObject(hColorPen);
		
		ICONINFO icoi;
		icoi.fIcon = FALSE;
		icoi.xHotspot = 8;//热点x坐标
		icoi.yHotspot = 8;
		icoi.hbmMask = hMaskBitmap;
		icoi.hbmColor = hColorBitmap;
		obj.m_hCursor = CreateIconIndirect(&icoi);
		
		DeleteObject(hColorBitmap);
		DeleteObject(hMaskBitmap);
		DeleteDC(hColorDC);
		DeleteDC(hMackDC);
		ReleaseDC(obj.m_hWnd, hDC);
	}
}



你可能感兴趣的:(SDK,面向对象,C++,GDI,窗口,SPY++)