//------------------------------------------------ // WinMain.cpp //------------------------------------------------ LRESULT CALLBACK WinSunProc( HWND hwnd, // handle to window UNIT uMsg, // message identifier WPARAM wParam, // first message parameter LPARAM lParam // second message parameter ); int WINAPI WinMain( HINSTANCE hInstance, // handle to current HINSTANCE hPrevInstance, // handle to previous instance LPSTR lpCmdLine, // command line int nCmdShow // show state ) { // 设计一个窗口类 WNDCLASS wndcls; wndcls.cbClsExtra = 0;// 类的附加空间,由属于这个窗口类的所有窗口共享,用于存储类的附加信息,一般设为0. wndcls.cbWndExtra = 0;// Windows系统为每一个窗口管理内部数据结构,在注册一个窗口类时,应用程序能够指定一定数目的附加内存空间,成为窗口附加内存。在创建窗口类时,Windows系统就为窗口的结构分配和追加指定数目的窗口附加空间,应用程序可以为这部分内存存储窗口特有的数据,,Windows系统把这部分内存初始化为0.如果应用程序用WNDCLASS结构注册对话框(用资源文件的CLASS伪指令创建),必须用DLGWINDOWEXTRA(其实其值就是30啦)设置这个成员,一般我们设置为0。 wndcls.hbBackground = (HBRUSH)GetStockObject(BACK_BRUSH); wndcls.hCursor = LoadCursor(NULL, IDC_CROSS);// 第一个参数为NULL表示加载的是系统的标准图标。 wndcls.hIcon = LoadIcon(NULL, IDI_ERROR); wndcls.hInstance = hInstance;// 应用程序实例句柄由WinMain函数传进来。 wndcls.lpfnWndProc = WinSunProc; wndcls.lpszClassName = "Sunxin2006"; wndcls.lpszMenuName = NULL; wndcls.style = CS_HREDRAW | CS_VREDRAW;// CS_HREDRAW当窗口水平方向上的宽度发生变化时,将重绘整个窗口。当窗口重绘时,窗口中的文字和图形将被删除。如果没有指定这一项,那么在水平方向上调整窗口宽度时将不会发生重绘。CS_VREDRAW同上,CS_NOCLOSE将导致窗口没有关闭按钮。CS_DBLCLKS当用户双击鼠标时,向窗口过程发送鼠标双击消息。 RegisterClass(&wndcls); // 创建窗口,定义各个变量用来保存成功创建窗口后返回的句柄 HWND hwnd; // WM_CREATE消息被发出。 hwnd = CreateWindow("Sunxin2006", "http://www.sunxin.org",// 指定窗口的名字,如果窗口样式指定了标题栏,那么这里指定的窗口名字将显示在标题栏上。 WS_OVERLAPPEDWINDOW,// 创建指定的窗口样式,同一型号的窗口也有不同的外观样式WS_OVERLAPPEDWINDOW为以下的组合:WS_OVERLAPPED:产生一个层叠的窗口,一个层叠的窗口有一个标题栏和一个边框。WS_CAPTION:创建一个有标题栏的窗口。WS_SYSMENU:创建一个在标题栏上带有系统菜单的窗口,要和WS_CAPTION类一起使用。WS_THICKFRAME:创建一个具有可调边框的窗口,WS_MINIMIZEBOX:创建一个具有最小化按钮的窗口,必须同时设定WS_SYSMENU类型。WS_MAXIMIZEBOX:创建一个具有最大化按钮的窗口,必须同时设定WS_SYSMENU类型。 0, 0, 600, 400,// 参数x,y,nWidth,nHeight分别指定窗口左上角的x,y坐标,窗口的宽度和高度。如果x被设定为CW_USEDEFAULT,那么系统为窗口选择默认的左上角坐标并忽略y参数。如果参数nWidth被设为CW_USEDEFAULT,那么系统为窗口选择默认的宽度和高度,参数nHeight被忽略。 NULL,// 指定被创建的窗口的父窗口句柄,窗口之间可以有父子关系,子窗口必须具有WS_CHILD样式。对父窗口的操作同时也会影响到子窗口。影响的效果如下:父窗口销毁,子窗口在父窗口被销毁之前销毁;父窗口影藏,在父窗口被影藏之前影藏,子窗口只有在父窗口可见时可见;父窗口移动,跟随父窗口客户区一起移动;子窗口在父窗口显示之后显示。 NULL,// 指定窗口菜单的句柄。 hInstance,// 指定窗口所属的应用程序实例的句柄。 NULL// 作为WM_CREATE消息的附加参数lParam传入的数据指针。在创建多文档界面的客户窗口时,lpParam必须指向CLIENTCREATESTRUCT结构体。多数窗口把这个参数设为NULL。 );// 如果窗口创建成功,CreateWindow函数将返回系统为改窗口分配的句柄,否则返回NULL // 显示及刷新窗口 ShowWindow(hwnd,SW_SHOWNORMAL);// 第二个参数指定了窗口显示的状态,常用的有以下几种:// SW_HIDE:影藏窗口并激活其他窗口。 SW_SHOW:在窗口用来的位置以原来的尺寸激活和显示窗口。 SW_SHOWMAXIMIZED:激活窗口并将其最大化显示。 SW_SHOWMINIMIZED:激活窗口并将其最小化显示。 SW_SHOWNORMAL:激活并显示窗口。如果窗口时最小化或最大化的状态,系统将其恢复到原来的尺寸和大小。应用程序在第一次显示的时候应该指定这个参数。 UpdateWindow(hwnd);// 就好像我们买好房子需要装修一样。UpdateWindow函数通过发送一个WM_PAINT消息来刷新床哭哦,UpdateWindow将WM_PAINT消息直接发送给了窗口过程函数进行处理,而没有放在消息队列中 // 定义消息结构体,开始消息循环 MSG msg; while(GetMessage(&msg,NULL,0,0)) { TranslateMessage(&msg); DispatchMessage(&msg); } return msg.wParam; }// WinMain 注:BOOL GetMessage( LPMSG lpMsg,// address of structure with message 指向一个消息MSG结构体,GetMessage从线程的消息队列中取出的消息信息将保存在改结构体对象中。 HANDLE hWnd,// handle of window 指定接收属于哪一个窗口的消息。通常我们将其设为NULL,用于接收属于调用线程的所有窗口的窗口消息。 UNIT wMsgFilterMin,// first message 指定要获取消息的最小值,通常设为0. UNIT wMsgFilterMax// last message 指定要获取的消息的最大值。如果wMsgFilterMin和wMsgFilterMax都设为0,则接收所有的消息。 );// GetMessage函数接收到除WM_QUIT外的消息均返回非零值。对于WM_QUIT消息,改函数返回零。如果出现了错误,该函数返回-1,例如,当参数hWnd是无效的窗口句柄或lpMsg是无效的指针式。 TranslateMessage函数用于将虚拟键消息转换为字符消息。字符消息被投递到调用线程的消息队列中,当下一次调用GetMessage函数时被取出。当我们敲击键盘上的某个字符键是,系统将产生WM_KEYDOWN,WM_KEYUP消息。这两个消息的附加参数(wParam和lParam)包含的是虚拟件代码和扫描码等信息,而我们在程序中往往需要得到某个字符的ASCII,TranslateMessage这个函数可以将WM_KEYDOWN和WM_KEYUP消息的组合转换为一条WM_CHAR消息(该消息的wParam附加参数包含了字符的ASCII),并将转换后的新消息投递到调用线程的消息队列中。TranslateMessage并不会修改原有的消息,它只是产生新的消息并投递到消息队列中。 DispatchMessage函数分派一个消息到窗口过程,由窗口过程函数对消息进行处理。DispatchMessage只是将消息回传给操作系统,由操作系统调用窗口过程函数对消息进行处理。 // 编写窗口过程函数 LRESULT CALLBACK WinSunProc( HWND hwnd,// handle to window UINT uMsg,// message identifier WPARAM wParam,// first message parameter LPARAM lParam// secon message parameter ) { switch(uMsg) { case WM_CHAR: char szChar[20]; sprintf(szChar,"char code is %d",wParam); MessageBox(hwnd,szChar,"char",0);//第三个参数表示标题,第四个参数表示显示的样式,第一个参数表示被创建的窗口的拥有窗口,如果为NULL则表示没有拥有窗口。 break; case WM_LBUTTONDOWN: MessageBox(hwnd,"mouse clicked","message",0); HDC hdc;// 要在窗口中输出文字或者显示图形,需要用到设备描述表(Device Context)DC。DC是一个包含设备(物理输出设备,如显示器,以及设备驱动程序)信息的结构体,在Windows平台下,所有的图形操作都是利用DC来完成的,有DC去和设备驱动程序打交道,完成图形的绘制。程序员只需要获取DC(DC也是一种资源)的句柄,利用这个句柄去作图就可以了,使用DC,程序不用为图形的显示与打印输出分别处理了。无论显示还是打印,都是直接在DC上操作,然后由DC映射到这些物理设备上。 hdc = GetDC(hwnd);// 不能在响应WM_PAINT消息时调用,在获取一个DC句柄时,总是和一个指定的窗口相关联,因为我们在作图时需要画布,,窗口就相当于画布。 TextOut(hdc,0,50,"程序员之家",strlen("程序员之家")); ReleaseDC(hwnd,hdc);//在图形操作结束后,必须调用ReleaseDC函数来释放DC所占的资源,都则会引起内存泄露。 break; case WM_PAINT:// 当窗口客户区的一部分或者全部变为“无效”时,系统会发送WM_PAINT消息,通知应用程序重新绘制窗口。当窗口刚创建的时候,整个客户区都是无效的。因为这个时候程序还没有在窗口上绘制任何东西,当调用UpdateWindow函数时,会发送WM_PAINT消息给窗口过程,对窗口进行刷新。当窗口从无到有、改变尺寸、最小化后再回复、被其他窗口遮盖后再显示时,窗口的客户区都变为无效。,此时系统会给应用程序发送WM_PAINT消息,通知应用程序重新绘制。当窗口大小发生变化时是否发生重绘取决于WNDCLASS结构体中style成员是否设置了CS_HREDRAW和CS_VREDRAW标志。 hdc hDC; PAINTSTRUCT ps; hDC = BeginPaint(hwnd,&ps)// BeginPaint只能在响应WM_PAINT消息时调用。第二个参数PAINTSTRUCT结构体的指针,改结构体对象用于接收绘制的信息,在调用BeginPaint时,如果客户区的背景还没有被擦除,那么BeginPaint会发送WM_ERASEBKGND消息给窗口,系统会使用WNDCLASS结构体的hbrBackground成员指定的画刷来擦除背景。 TextOut(hDC,0,0,"http://www.sunxin.org",strlen("http://www.sunxin.org"));// 用TextOut函数输出文字,当发生重绘时,窗口中的文字和图形都会被擦除。如果我们想让某个图形始终在窗口中显示,就应该将图形的绘制操作放到响应WM_PAINT消息的代码中。那么系统为什么不直接保存窗口中的图形数据,而要由应用程序不断地进行重绘呢?这主要是因为图形环境中涉及的数据量太大,为了节省内存空间,提高效率,而采用了重绘的方式。 EndPaint(hwnd,&ps);// BeginPaint得到的函数必须用EndPaint函数去释放。 break; case WM_CLOSE:// 当用户单击窗口上的关闭按钮时,系统会发送一条WM_CLOSE消息。 if(IDYES == MessageBox(hwnd,"是否真的结束?","message",MB_YESNO)) { DestroyWindow(hwnd);// DestroyWindow函数在销毁窗口后会向窗口过程发送WM_DESTROY消息。注意,此时窗口虽然消失了,但应用程序并没有推出。所以如果你要控制程序是否退出,应该在WM_CLOSE消息的响应代码中完成。对WM_CLOSE的响应并不是必须的,如果应用程序没哟对该消息进行响应,系统会把这小消息传给DefWindowProc函数,而DefWindowProc函数则调用DestroyWindow函数来了响应这条WM_CLOSE消息。DestroyWindow函数在销毁窗口后,会给窗口发送WM_DESTROY消息,我们在改消息的响应代码中调用PostQuitMessage函数,PostQuitMessage函数向应用程序的消息队列中投递一条WM_QUIT消息并返回,GetMessage函数只有在接收到WM_QUIT消息时才返回0,此时消息循环才结束,程序退出。要想让程序正常退出,我们必须响应WM_DESTROY消息,并在消息响应代码中调用PostQuitMessage,想应用程序的消息队列中投递WM_QUIT消息。传递给PostQuitMessage函数的参数值将作为WM_QUIT消息的wParam参数,这个值通常做WinMain函数的返回值。 } break; case WM_DESTORY: PostQuitMessage(0); break; default: return DefWindowProc(hwnd,uMsg,wParam,lParam);// DefWindowProc函数调用默认的窗口过程,对应用程序没有处理的其他消息提供默认处理。对于大多数的消息,应用程序都可以直接调用DefWindowProc函数进行处理。在编写窗口过程时,应该将DefWindowProc函数的调用放到default语句中,并将该函数的返回值作为窗口过程函数的返回值。如果该函数没有对DefWindowProc的响应,那么窗口不能显示,但今次管理器中显示程序仍在运行,这是因为没有给消息找归宿。我感觉应该是WM_CREATE消息。 } return 0; }// WinSunProc