开始着手写自己的代码。
目的:
1.巩固这段时间工作学到的东西。
2.培养自己良好的编码习惯。
坚持下去!
最近有个同事经常谈论设计模式,而我一直固执的认为谈设计模式就像纸上谈兵,没有多大意义,关键还得用得上。晚上心血来潮搜了些看看,发现程序员或多或少都用到了设计模式,不管你知不知道你在使用,区别只在于你用得够不够巧妙。我是个唯用主义者,觉得一切能实用到具体环境中的东西才是值得研究的,这个可能跟万恶的中国教育有关吧。程序员应该都是完美主义者,可是在越来越快的需求变更和越来越紧的项目进度下,有多少人能够把自己的代码写上一遍,改上十遍呢?网游开发的朋友会望着魔兽世界自叹不已,普通的程序员估计也会看着诸多漂亮源码感叹不已,殊不知魔兽世界的开发用了十年,在中国这可能么?悲哀之余聊以自慰的是读书的时候可以为自己的完美主义精雕细琢写出来的代码,扯远了扯远了。
关于设计模式,我想说的是你不能指望知道了他自己能够马上写出漂亮的代码,但是你可以借鉴里面的诸多方案,来帮助自己所面对的具体环境找到最适合的框架设计,这个很重要,尤其是当你的代码可能移交给别人时,你需要让他们能够在你的框架下很轻松的继续扩展,而不是东加一点,西加一点,到最后惨不忍睹,维护起来,即使把所有参与开发的人员叫在一起也寸步难行。
个人觉得写的不错的设计模式笔记:
http://caterpillar.onlyfun.net/Gossip/DesignPattern/DesignPattern.htm 可惜是繁体的,估计是出自台湾同胞之手吧
最近公司事情太多,一直没有时间再看看书,周末抽空看看。
Note:
1.子窗口向父窗口发送消息时,子窗口向父窗口发送WM_COMMAND消息。
其中LOWORD(wParam)为子窗口ID,HIWORD(wParam)为通知码,lParam为子窗口句柄
2.id = GetWindowLong(hwndChild,GWL_ID);//获取ID
id = GetDlgCtrlID(hwndChild);//同上
hwnd = GetDlgItem(hwndParent,ID);//获取句柄
3.SendMessage(hwndButton,BM_SETSTATE,1,0);//模拟鼠标按下按钮消息
SendMessage(hwndButton,BM_SETSTATE,0,0);//使鼠标恢复正常状态
4.WM_KILLFOCUS 消息的wParam是接受输入焦点的窗口句柄
WM_SETFOCUS消息的wParam是即将失去输入焦点的窗口句柄
通过处理WM_KILLFOCUS消息可以组织子窗口获得输入焦点
case WM_KILLFOCUS:
if(hwnd == GetParent((HWND)wParam))
SetFocus(hwnd);
return 0;
5.
Note:
1.SetTimer、KillTimer、WM_TIMER —— 三个关键字。
2.WM_TIMER并非异步消息,因此不能保证计时一定准确。
3.///////////////////////////////////////////////////////////////////////////////////////////////////
//将计时器消息发送给窗口
SetTimer(hwnd,uintTimerID,uims,NULL);
KillTimer(hwnd,uintTimerID);
WM_TIMER消息的wParam等于计时器的标志ID:uingTimerID
///////////////////////////////////////////////////////////////////////////////////////////////////
//通过回调函数处理计时器消息1
SetTimer(hwnd,uintTimerID,uims,TimerProc);
Void CALLBACK TimerProc(HWND hwnd,UINT message,UINT iTimerID,DWORD dwTime)
{
//Process your timerproc
}
///////////////////////////////////////////////////////////////////////////////////////////////////
//通过回调函数处理计时器消息2
iTimerID = SetTimer(NULL,0,uims,TimerProc);
Void CALLBACK TimerProc(HWND hwnd,UINT message,UINT iTimerID,DWORD dwTime)
{
//Process your timerproc
}
Note:
1.iMouse = GetSystemMetrics(SM_MOUSEPRESENT);//检查鼠标是否安装
cButtons = GetSystemMetrics(SM_CMOUSEBUTTONS);///检查鼠标键个数
wdnclass.hCursor = LoadCursor(NULL, IDC_ARROW);//指定窗口的默认光标
2.鼠标事件中lParam值包含了鼠标位置。
x = LOWORD(lParam);
y = HIWORD(lParam);
wParam值指示鼠标键及Shift及Ctrl键状态。
wParam&MK_SHIFT !=0 //说明按鼠标键时Shift键也按下了。
3.希望窗口接受到双击事件,需要设定窗口风格。
wndclass.style = CS_DBLCLKS;
4.非客户区鼠标消息。
NC not client,有此标记的大多为非客户区消息,如NCPAINT等。
wParam、lParam与客户区鼠标消息有区别。
wParam指明移动或者单击鼠标键的非客户区位置,WINUSER.h中以HT开头的标志符。
lParam高、低位分别表示y、x坐标,注意是屏幕的而不像客户区内消息的是客户区坐标,使用时需要装换。
ScreenToClient(hwnd,&pt);
ClientToScreen(hwnd,&pt);
5.WM_NCHITTEST
非客户区命中测试,此消息优先于所有其他的客户区和非客户区鼠标消息。其中lParam含有鼠标x、y值,wParam没有实际意义。对此消息的操作可以屏蔽所有鼠标消息,很强大。
6.Windows使用WM_NCHITTEST消息产生所有其他鼠标消息。
7.鼠标光标的操作
鼠标光标有个显示计数,通过ShowCursor(TRUE)增加,通过ShowCursor(FALSE)减少。GetCursorPos(&pt)获取光标位置。SetCursorPos(x,y)设置位置。
8.GetWindowLong(hwnd,GWL_HINSTANCE);//获取窗口的hinstance
9.GetCapture ReleaseCapture。获取、释放鼠标,即使鼠标不在当前窗口内。
ps:鼠标操作是当前Windows最为常用的操作之一,使用较为频繁,但是不难,较易理解。
Note:
1.键盘消息的处理分为2步:首先在系统消息队列中保存消息,然后将它们放入应用程序的消息队列,目的是为了同步。考虑键盘输入导致窗口焦点切换,若直接放到应用程序消息队列中可能切换后的窗口不再能够接受到键盘输入。
2.对产生可显示字符的击键组合,Window产生击键消息以及字符消息;对于不产生字符的键则只产生击键消息。
3.WM_KEYDOWN可能会重复发送,WM_KEYUP每次击键只会发生一次。
4.对于所有击键消息,wParam是击键代码,表示按下或释放的键;而lParam则包含属于击键的其他数据。
5.功能键的判断。
iState = GetKeyState(VK_SHIFT);
iState = GetKeyState(VK_CAPITAL);//根据iSate的不同位来查看
6.合理的键盘操作应该根据WM_CHAR、WM_DEADCHAR、WM_SYSCHAR、WM_STSDEADCHAR来进行,WM_KEYDOWN类消息有可能因为键盘布局的差异性出现错误。TranslateMessage消息从WM_KEYDOWN消息产生了字符消息。WM_CHAR消息夹在击键消息之间。DEADKEY基本上可以无视了。
如,巧了A键,产生的消息顺序为:WM_KEYDOWN | WM_CHAR | WM_KEYUP
7.规则:如果需要读取输入到窗口的键盘字符,处理WM_CHAR消息;如果需要读取光标键、功能键等,处理WM_KEYDOWN消息。
8.插入符函数。
CreateCaret、SetCaretPos、ShowCaret、HideCaret、DestroyCaret、GetCaretPos、GetCaretBlinkTime、SetCaretBlinkTime
一个消息队列只能支持一个插入符,所以不应该在窗口Create时创建Caret避免多个窗口的情况。只有获取焦点的窗口才应该有Caret很容易理解~~
HideCaret具有累积性,也就是说Hide了多少次就要Show多少次才能把Caret搞出来...
Note:
1.图形输出设备分为光棚设备和矢量设备。大部分pc输出设备都是光棚设备,即以点模式表示图像。矢量设备使用线来绘制图像,如绘图仪。使用矢量的程序是在硬件之上的一层抽象。
2.GDI函数的分类。
获取(创建)、释放(清除)设备描述表的函数,如GetDc,ReleaseDC;获取设备描述表信息的函数,如GetTextMetrics;绘图函数,如TextOut;设着和获取设备描述表参数的函数,如SetTextColor控制TextOut输出字颜色;使用GDI对象的函数,如CreatePen等。
3.图元。直线和曲线,由Pen画;填充区域;位图;文本;
4.设备描述表的获取方式:
1)hdc = BeginPaint(hwnd,&ps);
EndPaint(hwnd,&ps);
2)hdc = GetDC(hwnd); // Note:GetDC(NULL)获取整个屏幕的DC
ReleaseDC(hwnd,hdc);
3)hwd = GetWindowDC(hwnd);
ReleaseDC(hwnd,hdc);
4)hdc = CreateDC(lpszDriverName, lpszDeviceName, lpszOutput, lpInitData); // Note:CreateDC(_T("DISPLAY"),NULL,NULL,NULL)获取整个屏幕DC
DeleteDC(hdc);
5)hdcMem = CreateCompatibleDC(hdc); //使用位图时,可用内存设备描述表
DeleteDC(hdcMem);
6)hdcMeta = CreateMetaFile(pszFileName); //获取元文件设备描述表
hmf = CloseMetaFile(hdcMeta);
5.一个设备描述表通常就是一个物理显示设备。
iValue = GetDeviceCaps(hdc, iIndex); //根据iIndex获取dc的相关属性。
6.保存设备描述表。
wndclass.style = CS_OWNDC; //可以为窗口建立自己的DC,在修改DC之后DC一直有效直到EndPaint或者ReleaseDC。
idSaved = SaveDC(hdc);
//修改DC属性
RestoreDC(hdc,idSaved); //恢复保存的DC
上述语句等价于 SaveDC(hdc);
RestoreDC(hdc,-1);
7.画线。
影响画线的dc属性:当前画笔位置;画笔;背景方式;背景色;绘图模式。
画直线:
MoveToEx(hdc,xBeg,yBeg,NULL);
LineTo(hdc,xEnd,yEnd);
GetCurrentPositionEx(hdc,&pt);//获取当前位置
8.画笔。
HPEN hPen,hPenOld;
hPen = GetStockObject(WHITE_PEN); //获取现有画笔的句柄
hPenOld = SelectObject(hdc,hPen);//将画笔选进设备描述表,hPenOld记录老的画笔
//CreateOwnPen
HPEN hPen = CreatePen(iPenStyle,iPenWidth,crColor); // 一种方法
LOGPEN logpen;
HPEN hPen = CreatePenIndirect(&logpen);//另一种方法
DelectObject(SelectObject(hdc,hPen));//及时删除画笔,节省资源
//获取画笔信息
GetObject(hPen,sizeof(LOGPEN),(LPVOID)&logpen);//获取hPen属性
hPen = GetCurrenObject(hdc,OBJ_PEN);//获取当前dc中HPEN
//点画线和虚线的空隙填充,受dc的背景模式和背景色影响
SetBkColor(hdc,crColor);//设置背景颜色
SetBkMode(hdc,TRASPARENT);//TRANSPARENT组织填充背景,OPAQUE模式将用背景色填充空隙
//绘图方式
//画笔像素与目标位置处原来像素之间按位运算,叫做“光栅运算”ROP,画线只有二维,称为ROP2,有意思的东西。
SetROP2(hdc,iDrawMode);
iDrawMode=GetROP2(hdc);//默认为R2_COPYPEN,画笔色彩替代背景
9.填充区域。
Rectangle、Ellipse、RoundRect、Chord、Pie、Polygon、PolyPolygon
边界框画法跟画线一样、封闭区域使用HBRUSH填充。
HBRUSH hBrush = GetStockObject(WHITE_BURSH);
SelectObject(hdc,hBrush);
SelectObject(hdc,GetStockObject(NULL_PEN));//画无边界框
SelectObject(hdc,GetStockObject(NULL_BRUSH));//不填充区域
//多边形的填充方式有ALTERNATE和WINDING两种,区别很神奇。
10.画刷。
HBRUSH hBrush = CreateSolidBrush(crColor);//创建画刷
hBrush = CreateHatchBrush(iHatchStyle,crColor);//创建斜影画刷
hBrush = CreatePatternBrush/*CreateDIBPatternBrushPt*/; //创建基于位图的画刷
LOGBRUSH logBrush;
hBrush = CreateBrushIndirect(&logBrush);
//其余操作类似HPEN
11.GDI映射方式。
SetMapMode(hdc,iMapMode);
iMapMode = GetMapMode(hdc);
//至于其他相关映射方式,个人认为使用的可能性很小。
12.矩形、区域、剪裁。
FillRect(hdc,&rect,hBrush);//无需将hBrush选进hdc
FrameRect(hdc,&rect,hBrush); //用brush画矩形框,但不填充
InvertRect(hdc,&rect); //将矩形框中所有像素翻转
SetRect(&rect,xLeft,yTop,xRight,yBottom);
OffsetRect(&rect,x,y);//将矩形移动几个单元
InflateRect(&rect,x,y);//增减矩形的尺寸
SetRectEmpty(&rect);//矩形为0
CopyRect(&rcDest,&rcSrc);//拷贝
IntersectRect(&rcDest,&rcSrc1,&rcSrc2);//取两矩形交集
UnionRect(&rcDest,&rcSrc1,&rcSrc2);//取两矩形并集
bEmpty = IsRectEmpty(&rect);//确定是否为空
bInRect = PtInRect(&rect,Point);//点是否在矩形内
13.PeekMessage(&msg,NULL,0,0,PM_REMOVE);//大家伙,值得注意
The PeekMessage function normally does not remove WM_PAINT messages from the queue. WM_PAINT messages remain in the queue until they are processed. However, if a WM_PAINT message has a NULL update region, PeekMessage does remove it from the queue. —— 摘自MSDN 貌似跟书上说的有出入,需要注意
14.区域
HRGN hRgn = CreateRectRgn(xLeft,yTop,xRight,yBottom);
hRgn = CreateRectRgnIndirect(&rect);
hRgn = CreateEllipticRgn(xLeft,yTop,xRight,yBottom);
hRgn = CreateEllipticRgnIndirect(&rect);
iRgnType = CombineRgn(hDestRgn,hSrcRgn1,hSrcRgn2,iCombine);
//区域填充参数类似Rect
小结:
最近公司项目用到了大量画图操作,主要设计界面自画,重复的将bitmap贴到特定rect,设置透明色。作为windows程序设计,不可避免的都会涉及到自画控件的开发,而自画控件很大一部分工作就是改变控件的UI,画图基础可以让自己更好的理解代码,
Note:
1.WinMain进入循环之前,通常会有UpdateWindow来向窗口过程投放WM_PAINT消息。
2.触发WM_PAINT消息的事件:之前隐藏的窗口部分重新可见;改变窗口的大小;ScrollWindow或ScrollDC滚动客户区;InvalidRect或者InvalidRgn显示产生WM_PAINT消息;而Validate则会将WM_PAINT取消掉。
3.程序应该组织成可以保留绘制客户区需要的所有信息。
4.客户区无效区域、有效区域的理解,处理WM_PAINT时,窗口过程可以通过GetUpdateRect获取无效区域的坐标。
5.设备描述表总是与显示器上的特定窗口相关。
6.WM_ERASEBKGND处理无效区域的擦除,并利用WNDCLASS.hbrbackground中的刷子属性填充无效区域。
7.PAINTSTRUCT结构。
8.InvalidateRect(hwnd,NULL,TRUE)使整个客户区无效,并擦除背景,如果最后一个参数位FALSE则不擦除背景。
9.获取设备描述表的方法:
1) hdc = BeginPaint(hwnd,&ps);
EndPaint(hwnd,&ps); 此方法仅在无效区域上画图
2) hdc = GetDC(hwnd);
ReleaseDC(hwnd,hdc); 此方法可在客户端任何区域上画图,且不会使无效变有效。
GetWindowDC,返回整个窗口的设备描述表,可修改窗口标题等。
今天下午跟新,服务器出了问题,没提交上去,郁闷!后面的部分很重要,涉及到TextOut、ScrollBar等有用信息,以后提交时切记先保存再提交。
Note:
1.Windows程序员开始时总是复制一段代码然后做相应修改
2.常量前缀注记:
CS —— Class Stye CW —— Create Window DT —— Draw Text
IDI —— ID of Icon IDC —— ID of Cursor MB —— MessageBox
SND —— Sound WM —— Window Message WS —— Window Style
WParam —— WcharParam (16bit) LParam —— LongParam(32bit) 默认为16位系统,而在32位系统中,两者均为32bit,W失去意义
LRESULT —— LongResult
#define CALLBACK __stdcall
HINSTANCE —— Handle of Instance HWND —— Handle of Window HDC —— Handle of Device Content 32bit
3.RegisterClass之后根据WndClass中的classname来CreateWindow,而classname属性在WndClass最后,多少有些降低了它的重要性,让人容易遗忘,至少刚开始时我是这样的。
4.32bit系统无需判断hprevinstance,而16bit系统需要以节省内存。
5.是否可理解所有窗口都是桌面窗口的子窗口或者间接子窗口?
6.UpdateWindow(hwnd)向hwnd发送WM_PAINT消息。
7.WM_Quit导致GetMessage返回0,消息循环结束。而PostQuitMessage通常用于响应WM_Destroy来向消息队列发送WM_Quit消息。为何不直接发送WM_Quit消息?如果这样的话在程序关闭之前你无法做任何其他操作。WM_Destroy响应函数可用于程序清理工作。
8.TraslateMsg进行键盘转换,DispatchMsg将消息交给窗口,而窗口将其交给适合的消息处理函数。
9.Windows程序所做的一切都是处理响应发送给窗口过程的消息。
10.Windows会在创建之后给窗口发送WM_CREATE消息,而不是之前一直潜意识中误解的WM_CREATE消息的响应函数来负责Create Windows。
11.消息循环和窗口过程并非并发进行。
12.DefWndProc也是你窗口的一部分。
ps:如有不对,请指正。
1. Unicode vs ASCII
typedef unsigned short wchar_t:char-一个字节位,wchar_t-两个字节,仅此而已
2. wchar下的字符函数
wchar_t szWchar[3]= L“ab”;
strlen(szWchar); 输出1 ? wchar_t类型的'a'内存中为 61 00, 结果可以理解
相应的调用wstrlen(szWchar) 输出2。字符串长度不变,只是字节数改变。类似的其他函数在wchar.h头文件中可参阅。
3.TCHAR的引入
通过宏定义统一管理char、wchar_t两种类型。
#define _t(x) L##x
4.Sprintf(char* szBuff, const char* szFormat,...)
格式化字符串到缓冲区,很有用的函数