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);
}
}