看了浅墨大哥的博客,有很多感悟,筑梦逐梦中~
首先需要有个window主进程函数:
int WINAPI WinMain(HINSTANCE hinstance,HINSTANCE hPrevInstance,LPSTR lpCmdLine,int nComShow){
}
窗体过程函数:
LRESULT CALLBACK WindowProc(HWND hWnd,UINT msg,WPARAM wparam,LPARAM lparam);
其实:
#define CALLBACK __stdcall
#define WINAPI __stdcall
他们等价的,
书写window程序:
一、需要构建窗体类的结构体WNDCLASSEX:
原型如下:
typedef struct tagWNDCLASSEXW {
UINT cbSize;
/* Win 3.x */
UINT style;
WNDPROC lpfnWndProc;
int cbClsExtra;
int cbWndExtra;
HINSTANCE hInstance;
HICON hIcon;
HCURSOR hCursor;
HBRUSH hbrBackground;
LPCWSTR lpszMenuName;
LPCWSTR lpszClassName;
/* Win 4.0 */
HICON hIconSm;
} WNDCLASSEXW, *PWNDCLASSEXW, NEAR *NPWNDCLASSEXW, FAR *LPWNDCLASSEXW;
通常这样子书写:
WNDCLASSEX wndclass={0};
wndclass.cbClsExtra=0;
wndclass.cbSize=sizeof(WNDCLASSEX);//尺寸,固定
wndclass.style=CS_HREDRAW;//style如下---
wndclass.lpfnWndProc=WindowProc;//选择窗口过程
wndclass.hInstance=hinstance;//属于哪个实例
wndclass.hIcon=LoadIcon(NULL,IDI_HAND);//可以改变的ico
wndclass.hbrBackground=(HBRUSH)GetStockObject(WHITE_BRUSH);//背景画刷
wndclass.lpszMenuName=NULL;
wndclass.lpszClassName=L"absurd";//类名,注册
wndclass.hIconSm=NULL;
style:
#define CS_VREDRAW 0x0001//当垂直长度改变或移动窗口时,重画整个窗口
#define CS_HREDRAW 0x0002//当水平长度改变或移动窗口时,重画整个窗口</span>
#define CS_DBLCLKS 0x0008//允许向窗口发送双击鼠标键的消息</span>
#define CS_OWNDC 0x0020//给予每个窗口实例它本身的DC。注意,尽管这样是很方便,但它必须慎重使用,因为每个DC大约要占800个字节的内存。</span>
#define CS_CLASSDC 0x0040// 该窗口类的所有窗口实例都共享一个窗口类DC</span>
#define CS_PARENTDC 0x0080//将子窗口的裁剪区域设置到父窗口的DC中去,这样子窗口便可以在父窗口上绘制自身。注意,这是子窗口还是从系统缓存中获取DC,而不是使用父窗口的DC。使用该风格可以提高系统性能。</span>
#define CS_NOCLOSE 0x0200// 禁止系统菜单的关闭选项</span>
#define CS_SAVEBITS 0x0800//以位图形式保存被该窗口遮挡的屏幕部分,这样当给窗口移动以后,系统便可以用该保存的位图恢复屏幕移动的相应部分,从而系统不用向被该窗口遮挡的窗口发送 WM_PAINT 消息。该特性对于菜单类型的窗口比较合适,因为它通常是简短的显示一下之后便消失。设置该特性将增加显示该窗口的时间,因为它通常要先分配保存位图的内存。</span>
#define CS_BYTEALIGNCLIENT 0x1000//在字节边界上(在x方向上)定位窗口的用户区域的位置</span>
#define CS_BYTEALIGNWINDOW 0x2000//在字节边界上(在x方向上)定位窗口的位置</span>
#define CS_GLOBALCLASS 0x4000//当调用CreateWindow 或 CreateWindowEx 函数来创建窗口时允许它的hInstance参数和注册窗口类时传递给RegisterClass 的 hInstance参数不同。如果不指定该风格,则这两个 hInstance 必须相同。</span>
二、注册窗体结构体
RegisterClassEx(&wndclass);
三、创建窗体:
HWND hwnd=CreateWindow(L"absurd",//已注册的窗体结构体类名
L"absurdwin",//窗体名
WS_CAPTION|WS_OVERLAPPEDWINDOW,//dwStyle
CW_USEDEFAULT,//左上角位置出现坐标x
CW_USEDEFAULT,//y
WINDOW_WIDTH,//宽
WINDOW_HEIGHT,//高(这里宽高都是我自定义的)
NULL,//hWndParent指向被创建窗口的父窗口或所有者窗口的句柄。若要创建一个子窗口或一个被属窗口,需提供一个有效的窗口句柄。这个参数对弹出式窗口是可选的。Windows NT 5.0;创建一个消息窗口,可以提供HWND_MESSAGE或提供一个己存在的消息窗口的句柄。
NULL,//hMenu菜单句柄,或依据窗口风格指明一个子窗口标识。对于层叠或弹出式窗口,hMenu指定窗口使用的菜单:如果使用了菜单类,则hMenu可以为NULL。对于子窗口,hMenu指定了该子窗口标识(一个整型量),一个对话框使用这个整型值将事件通知父类。应用程序确定子窗口标识,这个值对于相同父窗口的所有子窗口必须是唯一的。
hinstance,//hlnstance与窗口相关联的实例句柄
NULL//lpParam指向一个值的指针,该值传递给窗口WM_CREATE消息。该值通过在IParam参数中的CREATESTRUCT结构传递。如果应用程序调用CreateWindow创建一个MDI客户窗口,则lpParam必须指向一个CLIENTCREATESTRUCT结构。
);
dwStyle如下:
/*
* Window Styles
*/
#define WS_OVERLAPPED 0x00000000L//产生一个层叠的窗口。一个层叠的窗口有一个标题条和一个边框。与WS_TILED风格相同。
#define WS_POPUP 0x80000000L//创建一个弹出式窗口。该风格不能与WS_CHILD风格同时使用.
#define WS_CHILD 0x40000000L//创建一个子窗口。这个风格不能与WS_POPUP风格合用。
#define WS_MINIMIZE 0x20000000L//
#define WS_VISIBLE 0x10000000L//创建一个初始状态为可见的窗口。
#define WS_DISABLED 0x08000000L//创建一个初始状态为禁止的子窗口。一个禁止状态的窗口不能接受来自用户的输入信息。
#define WS_CLIPSIBLINGS 0x04000000L//排除子窗口之间的相对区域,也就是,当一个特定的窗口接收到WM_PAINT消息时,WS_CLIPSIBLINGS 风格将所有层叠窗口排除在绘图之外,只重绘指定的子窗口。如果未指定WS_CLIPSIBLINGS风格,并且子窗口是层叠的,则在重绘子窗口的客户区时,就会重绘邻近的子窗口。
#define WS_CLIPCHILDREN 0x02000000L//<span style="text-indent: 28px;">当在父窗口内绘图时,排除子窗口区域。在创建父窗口时使用这个风格。</span>
#define WS_MAXIMIZE 0x01000000L//
#define WS_CAPTION 0x00C00000L //<span style="text-indent: 28px;">创建一个有标题框的窗口(包括WS_BORDER风格)。 /* WS_BORDER | WS_DLGFRAME */
#define WS_BORDER 0x00800000L//
#define WS_DLGFRAME 0x00400000L//<span style="text-indent: 28px;">创建一个带对话框边框风格的窗口。这种风格的窗口不能带标题条。
#define WS_VSCROLL 0x00200000L//<span style="text-indent: 28px;">创建一个有垂直滚动条的窗口。</span>
#define WS_HSCROLL 0x00100000L//<span style="text-indent: 28px;">创建一个有水平滚动条的窗口。</span>
#define WS_SYSMENU 0x00080000L//创建一个在标题条上带有的窗口,必须同时设定WS_CAPTION风格。</span>
#define WS_THICKFRAME 0x00040000L//<span style="text-indent: 28px;">创建一个可调边框的窗口,与WS_SIZEBOX风格相同。</span>
#define WS_GROUP 0x00020000L//<span style="text-indent: 28px;">指定一组控制的第一个控制。这个控制组由第一个控制和随后定义的控制组成,自第二个控制开始每个控制,具有WS_GROUP风格,每个组的第一个控制带有WS_TABSTOP风格,从而使用户可以在组间移动。用户随后可以使用光标在组内的控制间改变键盘焦点。</span>
#define WS_TABSTOP 0x00010000L//创建一个控制,这个控制在用户按下Tab键时可以获得键盘焦点。按下Tab键后使键盘焦点转移到下一具有WS_TABSTOP风格的控制。
#define WS_MINIMIZEBOX 0x00020000L/
#define WS_MAXIMIZEBOX 0x00010000L//创建一个具有最大化按钮的窗口。该风格不能与WS_EX_CONTEXTHELP风格同时出现,同时必须指定WS_SYSMENU风格。</span>
#define WS_TILED WS_OVERLAPPED
#define WS_ICONIC WS_MINIMIZE//创建一个初始状态为最小化状态的窗口。与WS_MINIMIZE风格相同。</span>
#define WS_SIZEBOX WS_THICKFRAME//创建一个可调边框的窗口,与WS_THICKFRAME风格相同。</span>
#define WS_TILEDWINDOW WS_OVERLAPPEDWINDOW
/*
* Common Window Styles
*/
#define WS_OVERLAPPEDWINDOW (WS_OVERLAPPED | \ //创建一个具有WS_OVERLAPPED,WS_CAPTION,WS_SYSMENU WS_THICKFRAME,WS_MINIMIZEBOX,WS_MAXIMIZEBOX风格的层叠窗口,与WS_TILEDWINDOW风格相同。
WS_CAPTION | \
WS_SYSMENU | \
WS_THICKFRAME | \
WS_MINIMIZEBOX | \
WS_MAXIMIZEBOX)
#define WS_POPUPWINDOW (WS_POPUP | \ //创建一个具有WS_BORDER,WS_POPUP,WS_SYSMENU风格的窗口,WS_CAPTION和WS_POPUPWINDOW必须同时设定才能使窗口某单可见。</span>
WS_BORDER | \
WS_SYSMENU)
#define WS_CHILDWINDOW (WS_CHILD)//和child相同
四、显示窗体并更新
ShowWindow(hwnd,SW_SHOW);
UpdateWindow(hwnd);
这里解释下后一个参数,就是显示的方式:
SW_FORCEMINIMIZE:在WindowNT5.0中最小化窗口,即使拥有窗口的线程被挂起也会最小化。在从其他线程最小化窗口时才使用这个参数。
SW_HIDE:隐藏窗口并激活其他窗口。
SW_MAXIMIZE:最大化指定的窗口。
SW_MINIMIZE:最小化指定的窗口并且激活在Z序中的下一个顶层窗口。
SW_RESTORE:激活并显示窗口。如果窗口最小化或最大化,则系统将窗口恢复到原来的尺寸和位置。在恢复最小化窗口时,应用程序应该指定这个标志。
SW_SHOW:在窗口原来的位置以原来的尺寸激活和显示窗口。
SW_SHOWDEFAULT:依据在STARTUPINFO结构中指定的SW_FLAG标志设定显示状态,STARTUPINFO 结构是由启动应用程序的程序传递给CreateProcess函数的。
SW_SHOWMAXIMIZED:激活窗口并将其最大化。
SW_SHOWMINIMIZED:激活窗口并将其最小化。
SW_SHOWMINNOACTIVE:窗口最小化,激活窗口仍然维持激活状态。
SW_SHOWNA:以窗口原来的状态显示窗口。激活窗口仍然维持激活状态。
SW_SHOWNOACTIVATE:以窗口最近一次的大小和状态显示窗口。激活窗口仍然维持激活状态。
SW_SHOWNORMAL:激活并显示一个窗口。如果窗口被最小化或最大化,系统将其恢复到原来的尺寸和大小。应用程序在第一次显示窗口的时候应该指定此标志。
五、消息投递
MSG msg={0};
while(msg.message!=WM_QUIT){//如果不是程序退出则消息循环
if(PeekMessage(&msg,0,0,0,PM_REMOVE)){//四个参数,1.指向消息结构体(从线程取出的消息存放进去),2.指定接受哪个窗体,所有则0,3.要获取消息的最小值,4.要获取消息的最大值,0为全部,5.PM_NOREMOVE|PM_REMOVE,分别表示取消息后是否移走(no不移走)
TranslateMessage(&msg);//将虚拟键消息转化为字符消息
DispatchMessage(&msg);//分发一个消息给窗口
}
}
六、填写窗体过程函数
LRESULT CALLBACK WindowProc(HWND hWnd,UINT msg,WPARAM wparam,LPARAM lparam){
//HDC hdc;
switch(msg){
case WM_KEYDOWN://可以使用相关消息
switch(wparam){
case VK_ESCAPE:
DestroyWindow(hWnd);
break;
case VK_UP:
MessageBox(NULL,L"你好",L"h",0);
Game_Clean(hWnd);
break;
}
break;
case WM_DESTROY:
PostQuitMessage(0);//表明有线程终止请求
break;
default:
return DefWindowProc(hWnd,msg,wparam,lparam);
}
return 0;
}
七、窗口类注销
UnregisterClass(L"absurd",wndclass.hInstance);
接下来讲GDI的使用:
HDC hdc=GetDC(hwnd);//获取设备环境句柄
.....
ReleaseDC(hwnd,hdc);//释放
下面讲几个可以使用的GDI绘图工具:
HPEN h_pen=CreatePen(PS_SOLID,//画笔样式
1,线条宽度
RGB(1,1,1)//颜色
);//创建画笔
HBRUSH h_brush=CreateSolidBrush(RGB(rand()%256,rand()%256,rand()%256));//创建实心画刷
HBRUSH h_brush2=CreateHatchBrush(HS_CROSS,//阴影样式
RGB(rand()%256,rand()%256,rand()%256)//前景色颜色值
);
关于画笔样式
#define PS_SOLID 0//实线
#define PS_DASH 1 /* ------- */
#define PS_DOT 2 /* ....... */
#define PS_DASHDOT 3 /* _._._._ */
#define PS_DASHDOTDOT 4 /* _.._.._ */
#define PS_NULL 5
#define PS_INSIDEFRAME 6
#define PS_USERSTYLE 7
#define PS_ALTERNATE 8
#define PS_STYLE_MASK 0x0000000F
#define PS_ENDCAP_ROUND 0x00000000
#define PS_ENDCAP_SQUARE 0x00000100
#define PS_ENDCAP_FLAT 0x00000200
#define PS_ENDCAP_MASK 0x00000F00
#define PS_JOIN_ROUND 0x00000000
#define PS_JOIN_BEVEL 0x00001000
#define PS_JOIN_MITER 0x00002000
#define PS_JOIN_MASK 0x0000F000
#define PS_COSMETIC 0x00000000
#define PS_GEOMETRIC 0x00010000
#define PS_TYPE_MASK 0x000F0000
关于阴影样式:
/* Hatch Styles */
#define HS_HORIZONTAL 0 /* ----- */
#define HS_VERTICAL 1 /* ||||| */
#define HS_FDIAGONAL 2 /* \\\\\ */
#define HS_BDIAGONAL 3 /* / */
#define HS_CROSS 4 /* +++++ */
#define HS_DIAGCROSS 5 /* xxxxx */
#define HS_API_MAX 12
选择好画笔后,使用需要用到
SelectObject(hdc,//设备上下文
h_brush//被选用对象
);
DeleteObject(
h_brush//删除被选用对象
);
绘制图形和线条:
先移动画笔
MoveToEx(hdc,x,y,
NULL//用来存放上一次的点,LPPOINT类型
);
画线
LineTo(hdc,
x,//终点
y//终点
};
画矩形:
Rectangle(hdc,bfx,//左上角
bfy,
x,//右下角
y);
文字输出:
先设置字体
HFONT hfont=CreateFont(45,//高度
0,//宽度
0,//显示角度
0,//字体角度
2,//磅数
0,//是否为斜体
0,//是否带下划线
0,//是否带删除
GB2312_CHARSET,//字符集
0,//输出精度
0,//裁剪精度
0,//输出质量
0,//间距的字符集
L"微软雅黑");
然后
wchar_t text1[]=L"1222324";
SetBkMode(hdc,TRANSPARENT);//设置文字透明
SetTextColor(hdc,RGB(rand()%256,rand()%256,rand()%256));//文字颜色
TextOut(hdc,x,y,text1,wcslen(text1));//输出
如果想贴图:先载入图片HBITMAP g_girl3=(HBITMAP)LoadImage(NULL,//NULL表示从硬盘,其他可以从内存句柄
L"GameMedia/girl3.bmp",IMAGE_BITMAP,//图片类型可以为.bmp .cur .ico
480,148,LR_LOADFROMFILE//表示从文件加载
);
//其次建立兼容DC,用来缓存
HDC g_mdc=CreateCompatibleDC(hdc);
//
DeleteDC(g_mdc);//删除设备环境
//No.3选用位图
SelectObject(g_mdc,g_hBitmap);
//No.4贴图
BitBlt(hdc,//目标设备
0,0,//目标方位xy
WINDOW_WIDTH,WINDOW_HEIGHT,//宽高
g_mdc,0,0,//源设备
SRCCOPY//直接拷贝
);
//另外一种常用于快速去背:
TransparentBlt(hdc,num2*60,WINDOW_HEIGHT-300,60,74,//目标
g_mdc,num2*60,0,60,74,//源
RGB(0,0,0)//需要被去背的颜色
);
#define SRCCOPY (DWORD)0x00CC0020 /* dest = source */
#define SRCPAINT (DWORD)0x00EE0086 /* dest = source OR dest */
#define SRCAND (DWORD)0x008800C6 /* dest = source AND dest */
#define SRCINVERT (DWORD)0x00660046 /* dest = source XOR dest */
#define SRCERASE (DWORD)0x00440328 /* dest = source AND (NOT dest ) */
#define NOTSRCCOPY (DWORD)0x00330008 /* dest = (NOT source) */
#define NOTSRCERASE (DWORD)0x001100A6 /* dest = (NOT src) AND (NOT dest) */
#define MERGECOPY (DWORD)0x00C000CA /* dest = (source AND pattern) */
#define MERGEPAINT (DWORD)0x00BB0226 /* dest = (NOT source) OR dest */
#define PATCOPY (DWORD)0x00F00021 /* dest = pattern */
#define PATPAINT (DWORD)0x00FB0A09 /* dest = DPSnoo */
#define PATINVERT (DWORD)0x005A0049 /* dest = pattern XOR dest */
#define DSTINVERT (DWORD)0x00550009 /* dest = (NOT dest) */
#define BLACKNESS (DWORD)0x00000042 /* dest = BLACK */
#define WHITENESS (DWORD)0x00FF0062 /* dest = WHITE */
如果使用罩法去背,需要准备如下图片:
使用又半部分和背景相与贴图,左半在相或贴图,即可