VC++深入详解 - 窗口的创建
- 格式:pdf
- 大小:377.26 KB
- 文档页数:9
C语⾔调⽤WIN32API学习之1创建窗⼝本学习笔记基于VC++6.0开发环境,通过c语⾔编程语⾔,调⽤win32 API进⾏windows系统应⽤程序开发。
1,打开VC++6.0,点击⽂件->新建->⼯程->Win32 Application ⼯程名填写example1,点击确定,选择⼀个空⼯程,点击完成。
2,点击"新建⽂件" 按钮,新建⼀个空⽩⽂件,点击⽂件->另存为输⼊⽂件名example1.c 选择⼯作空间对应的⽂件夹,保存。
3,点击FileView,右击Source File,点击添加⽂件到⽬录,选择example1.c,点击确定。
4,添加代码#include <windows.h>#include <Shlwapi.h>LRESULT CALLBACK WinSunProc(HWND hwnd,UINT uMsg,WPARAM wParam,LPARAM lParam); //声明消息回调函数int WINAPI WinMain(HINSTANCE hInstance,HINSTANCE hPreInstance,PTSTR szCmdLine,int iCmdShow) //Win32 Api 主程序函数⼊⼝{WNDCLASS wndcls; //定义窗体类HWND hwnd; //定义句柄⽤来保存成功创建窗⼝后返回的句柄MSG msg; //定义消息结构体变量wndcls.cbClsExtra=0; //结构体后附加的字节数,⼀般总为0wndcls.cbWndExtra=0; //窗体实例附加的字节数,⼀般总为0wndcls.hbrBackground=(HBRUSH)GetStockObject(BLACK_BRUSH); //背景颜⾊wndcls.hCursor=LoadCursor(NULL,IDC_ARROW); //光标句柄wndcls.hIcon=LoadIcon(NULL,IDI_APPLICATION); //图标句柄任务栏显⽰的图标wndcls.hInstance=hInstance; //模块句柄wndcls.lpfnWndProc=WinSunProc; //函数指针,指向处理窗⼝消息的函数⼊⼝wndcls.lpszClassName="windows"; //⾃定义类名,不要与其他类名重复wndcls.lpszMenuName=NULL; //菜单名的字符串wndcls.style=CS_HREDRAW | CS_VREDRAW; //指定窗⼝风格详见参数注释1if (!RegisterClass (&wndcls))return 0; //注册窗体类,如果失败直接返回0结束程序hwnd=CreateWindow("windows","创建⼀个新窗⼝",WS_OVERLAPPEDWINDOW,0,0,300,150,NULL,NULL,hInstance,NULL);//创建窗体//APIWS_OVERLAPPEDWINDOW为 Window Styles //详见参数注释2ShowWindow(hwnd,SW_SHOWNORMAL);//显⽰窗体的API 传⼊需要显⽰的窗体句柄和显⽰⽅式UpdateWindow(hwnd);//刷新窗体的APIwhile(GetMessage(&msg,NULL,0,0))//进⼊消息循环{TranslateMessage(&msg);//将虚拟键消息转换为字符消息。
举例说明#include//包含应用程序中数据类型和数据结构的定义long CALLBACK WndProc(HWND,UINT,WPARAM,LPARAM);//窗口说明//WinMain函数是所有Windows应用程序的入口,类似c语言中的main 函数其功能是完成//一系列的定义和初始化,并产生消息循环/* WinMain函数实现以下功能:注册窗口类,建立窗口及执行其他必要的初始化工作;进入消息循环,根据从应用程序消息队列接受的消息,调用相应的处理过程;当消息循环检测到WM_QUIT消息是终止程序运行WinMain函数有三个基本部分组成:函数说明、初始化和消息循环*/ //函数说明int WINAPI WinMain(HINSTANCE hInstance, // handle to current instanceHINSTANCE hPrevInstance, // handle to previous instanceLPSTR lpCmdLine, // command lineint nCmdShow // show state)//初始化//初始化包括窗口类的定义、注册、创建窗口实例和显示窗口四部分{HWND hwnd;MSG Msg;WNDCLASS wndclass;char lpszClassName[]="窗口";//窗口类名char lpszTitle[]="徐新坡"; //窗口标题名//窗口类定义//窗口类定义了窗口的形式与功能窗口类定义通过给窗口类数据结构WNDCLASS赋值完成//该数据结构中包含窗口类的各种属性wndclass.style =0; // 窗口类型为缺省类型wndclass.lpfnWndProc=WndProc; //定义窗口处理函数wndclass.cbClsExtra=0; //窗口类无扩展wndclass.cbWndExtra=0; //窗口实例无扩展wndclass.hInstance=hInstance; //当前实例句柄wndclass.hIcon=LoadIcon(NULL,IDI_APPLICATION);//窗口的最小化图标为缺省图标wndclass.hCursor=LoadCursor(NULL,IDC_ARROW); // 窗口采用箭头光标wndclass.hbrBackground=(HBRUSH)(GetStockObject(WHITE_BRUSH)); //窗口背景为白色wndclass.lpszMenuName=NULL; //窗口无菜单wndclass.lpszClassName=lpszClassName; //窗口类名为“窗口”//////////////// 以下是窗口类的注册 Windows系统本身提供部分预定义的窗口类,程序员也可以自定义窗口类// 窗口类必须先注册后使用if(!RegisterClass(&wndclass)) //如果注册失败发出警告{MessageBeep(0); return FALSE;}///////////////创建窗口创建一个窗口的实例由函数CreateWindow()实现hwnd=CreateWindow( lpszClassName, //窗口类名lpszTitle, //窗口标题名WS_OVERLAPPEDWINDOW, //窗口的风格CW_USEDEFAULT,CW_USEDEFAULT, //窗口左上角坐标值为缺省值CW_USEDEFAULT, CW_USEDEFAULT, //窗口的高和宽为缺省值NULL, //此窗口无父窗口NULL, //此窗口无子菜单hInstance, //创建此窗口的应用程序的当前句柄NULL //不使用该值);/////////////显示窗口ShowWindow(hwnd,nCmdShow);//绘制用户区UpdateWindow(hwnd);/////////////消息循环while(GetMessage(&Msg,NULL,0,0)){TranslateMessage(&Msg);DispatchMessage(&Msg);}}return Msg.wParam; //消息循环结束即程序结束时将信息返回系统/////////////窗口函数//窗口函数定义了应用程序对接收到的不同消息的响应,其中包含了应用程序对各种可能接受到的消息的//处理过程,时消息处理分支控制语句的集合long CALLBACK WndProc(HWND hwnd,UINT message,WPARAM wParam,LPARAM lParam){switch(message){case WM_DESTROY:PostQuitMessage(0);default: //缺省时采用系统消息缺省处理函数return DefWindowProc(hwnd,message,wParam,lParam);}return (0);}******************************************************************WinMain()WinMain是一个函数,该函数的功能是被系统调用,作为一个32位应用程序的入口点。
VC中自己创建对话框的方法这里讲在VC中不需要打开MFC工程,通过代码直接创建一个对话框,我看这个对话框比MFC的漂亮多了,而且要写会代码不需要研究MFC就直接开发新的工程.先打开VC++ 6.0 版本, 第一步:创建一个工程名为:Main Dialog的Win32 Application。
创建过程如下:打开File菜单中New,选自Win32 Application,工程名为:Main Dialog,点击OK,再选自A simple Win32 application。
点击Finish,再点击OK.(见图1)第二步:创建资源脚本。
再打开File菜单中New,选自File选项的Resource Script(就是资源脚本),文件名为Dialog,点击OK。
(见图2)第三步:插入对话框。
刚才建立的脚本Dialog显示为Dialog.rc,就在Dialog.rc上面点击鼠标右键Insert(插入)Resource中选自插入Dialog(对话框),点击New(新建),这样对话框插入完毕。
(见图3)第四步:添加resource.h。
我们把第四步插入的对话框ID改为IDD_MAIN ,在FileView中有个Header Files的文件,上面点击右键,选择Add Files to Folder...(添加文件到目录),弹出一个工程目录窗口,中间找到resource.h,然后OK了(添加)。
(见图4)第五步:复制代码。
双击Main Dialog.cpp,我们写代码的位置出现了(见图5),那里有几行代码,把它们全部删掉,下面已经准备好的代码全部复制到那里,编译,执行,您的是否成功了?#include "stdafx.h"#include "resource.h"#include <windowsx.h>BOOL WINAPI Main_Proc(HWND hWnd,UINT uMsg,WPARAM wParam,LPARAM lParam);BOOL Main_OnInitDialog(HWND hwnd,HWND hwndFocus,LPARAM lParam);void Main_OnCommand(HWND hwnd,int id,HWND hwndCtl, UINT codeNotify);void Main_OnClose(HWND hwnd);int APIENTRY WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,LPSTR lpCmdLine,int nCmdShow){DialogBox(hInstance,MAKEINTRESOURCE(IDD_MAIN),NUL L,Main_Proc);return 0;}BOOL WINAPI Main_Proc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam){switch(uMsg){HANDLE_MSG(hWnd, WM_INITDIALOG, Main_OnInitDialog);HANDLE_MSG(hWnd, WM_COMMAND, Main_OnCommand);HANDLE_MSG(hWnd,WM_CLOSE, Main_OnClose);}return FALSE;}BOOL Main_OnInitDialog(HWND hwnd, HWND hwndFocus, LPARAM lParam){return TRUE;}void Main_OnCommand(HWND hwnd, int id, HWND hwndCtl, UINT codeNotify){switch(id){case IDOK:{MessageBox(hwnd,TEXT("Thank you build me!"),TEXT("Happy"),MB_OK);}break;default:break;}}void Main_OnClose(HWND hwnd) {EndDialog(hwnd, 0);}执行以后的对话框图(见图6)是不是比MFC的漂亮!这对话框的缺点就是弹出时不是出现在显示器中间而老是出在显示器左上角,我不知道怎么调整,可定有办法把它显示在中间,有人知道解决的技巧,请教一下,我对他必有回报,我自己知道的一些会献给他!。
c语言创建窗口代码-回复如何使用C语言创建一个窗口C语言是一种通用的编程语言,可以用来开发各种类型的应用程序,包括图形用户界面(GUI)应用程序。
创建一个窗口是GUI应用程序中的基本操作之一,本文将介绍如何使用C语言创建一个窗口。
在使用C语言创建窗口之前,我们首先需要一个可供编程的集成开发环境(IDE),例如Code::Blocks或Visual Studio。
这些IDE提供了编写、编译和调试C语言程序的工具,并且支持创建窗口应用程序的相关库。
步骤1:导入窗口相关的库在C语言中,我们需要使用一些库来实现窗口的创建和管理。
常用的窗口库有WinAPI和GTK等。
在这篇文章中,我们将使用WinAPI来创建窗口。
首先,我们需要在代码中导入Windows头文件,以便使用WinAPI提供的函数和常量。
可以通过以下代码行实现:c#include <windows.h>步骤2:定义窗口过程在WinAPI中,窗口是由窗口过程函数控制的。
窗口过程函数是一个特殊的函数,它接收和处理与窗口相关的消息(例如鼠标点击、键盘输入等)。
我们可以通过以下代码定义一个简单的窗口过程函数:cLRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam){switch (uMsg){case WM_CLOSE:DestroyWindow(hwnd);break;case WM_DESTROY:PostQuitMessage(0);break;default:return DefWindowProc(hwnd, uMsg, wParam, lParam);}return 0;}在这个窗口过程函数中,我们根据收到的消息类型采取不同的行动。
当我们接收到WM_CLOSE消息时,我们调用DestroyWindow函数来销毁窗口。
当我们接收到WM_DESTROY消息时,我们调用PostQuitMessage 函数来终止应用程序。
如何使⽤visualstudio2019创建简单的MFC窗⼝(使⽤C++)本⽂介绍了如何使⽤visual studio2019创建简单的MFC窗⼝(使⽤C++)```cpp使⽤visual studio 2019 创建过程请参考Bili的上⼀篇⽂章⬇⬇→!使⽤visual studio 2019 创建简单的MFC窗⼝「使⽤底层的C语⾔」#include<windows.h> //底层实现窗⼝的头⽂件//6.处理窗⼝过程//CALLBACK 代表_stdcall 参数的传递顺序:从右到左依次⼊栈,并且函数返回前清空堆栈LRESULT CALLBACK WindowProc(HWND hand, //消息所属窗⼝句柄UINT uMsg, //具体消息名称 WM_XXXX消息名WPARAM wParam, //键盘附加消息LPARAM lParam) { //⿏标附加消息switch (uMsg){case WM_CLOSE://所有以XXXXWindow为结尾的⽅法,都不会进⼊到消息队列中,⽽是直接执⾏DestroyWindow(hand);//发送另⼀个消息WM_DESTROYbreak;case WM_DESTROY:PostQuitMessage(0);break;case WM_LBUTTONDOWN://⿏标左键按下{int xPos = LOWORD(lParam);int yPos = HIWORD(lParam);char buf[1024];wsprintf(buf, TEXT("x = %d,y = %d"),xPos,yPos);MessageBox(hand, buf, TEXT("按下⿏标左键"), MB_OK);break;}case WM_KEYDOWN: //键盘{MessageBox(hand, TEXT("键盘"), TEXT("按下键盘"), MB_OK);break;}case WM_PAINT: //画图{PAINTSTRUCT ps; //画图结构体HDC hdc = BeginPaint(hand, &ps);TextOut(hdc, 100, 100,TEXT("hello"), strlen("hello"));EndPaint(hand, &ps);break;}default:break;}//返回值⽤默认处理⽅式return DefWindowProc(hand, uMsg, wParam, lParam);}/*程序⼊⼝函数_In_ HINSTANCE hInstance,//应⽤程序实例句柄_In_opt_ HINSTANCE hPrevInstance,//上⼀个应⽤程序句柄,在WIN32环境下⼀般为NULL,不起作⽤了_In_ LPSTR lpCmdLine,//char * argv[]_In_ int nShowCmd//显⽰命令最⼤化、最⼩化、正常WINAPI 代表_stdcall 参数的传递顺序:从右到左依次⼊栈,并且函数返回前清空堆栈*/int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd ) {/*1.设计窗⼝2.注册窗⼝3.创建窗⼝4.显⽰和更新5.通过循环取消息6.处理消息(窗⼝过程)*///1.设计窗⼝WNDCLASS wc;wc.cbClsExtra = 0; //类的额外的内存wc.cbWndExtra = 0; //窗⼝额外的内存wc.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH); //设置背景wc.hCursor = LoadCursor(NULL, IDC_HAND); //设置光标,如果第⼀个参数为null,代表使⽤系统提供的光标wc.hIcon = LoadIcon(NULL, IDI_ERROR); //图标,如果第⼀个参数为null,代表使⽤系统提供的图标wc.hInstance = hInstance; //应⽤程序的实例句柄,传⼊winmain的形参即可wc.lpfnWndProc = WindowProc; //回调函数窗⼝过程wc.lpszClassName = TEXT("WIN"); //指定窗⼝名称wc.lpszMenuName = NULL; //菜单名称wc.style = 0; //显⽰风格,0代表默认//2.注册窗⼝RegisterClass(&wc);//3.创建窗⼝/*lpszClassName,//类名lpWindowName, //标题名dwStyle, //风格 WC_OVERLAPPEDWINDOWx, //坐标 CW_USERDEFAULTy, //坐标nWidth, //宽nHeight, //⾼hWndParent, //⽗窗⼝ nullhMenu, //菜单 nullhInstance, //实例句柄lpParam //附加值⿏标附加值*/HWND hwnd = CreateWindow(wc.lpszClassName, TEXT("WINDOWS"), WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, hInstance, NULL); //4.显⽰和更新ShowWindow(hwnd, SW_SHOWNORMAL);UpdateWindow(hwnd);/*HWND hwnd; //主窗⼝句柄UINT message; //具体消息名称WPARAM wParam; //附加消息键盘消息LPARAM lParam; //附加消息⿏标消息DWORD time; //消息产⽣时间POINT pt;*/ //附加消息⿏标消息 x,y//5.通过循环取消息MSG msg;/*_Out_ LPMSG lpMsg, //消息_In_opt_ HWND hWnd, //捕获窗⼝填null表⽰捕获所有窗⼝_In_ UINT wMsgFilterMin, //最⼤和最⼩的过滤消息⼀般填0_In_ UINT wMsgFilterMax);*/while (GetMessage(&msg, NULL, 0, 0)){/*if (GetMessage(&msg, NULL, 0, 0) == FALSE) {break;}*///翻译消息TranslateMessage(&msg);//不是false//分发消息DispatchMessage(&msg);}return 0;}划重点:项⽬->属性->常规->⾼级->将MFC的使⽤设置为在“共享dll中使⽤mfc”头⽂件mfc.h#include <afxwin.h>//mfc头⽂件class MyApp:public CWinApp{public:virtual BOOL InitInstance();};class MyFrame :public CFrameWnd {//窗⼝框架类public:MyFrame();//声明宏提⽰消息映射机制DECLARE_MESSAGE_MAP();afx_msg void OnLButtonDown(UINT,CPoint);afx_msg void OnChar(UINT,UINT,UINT);afx_msg void OnPaint();};源⽂件mfc.cpp#include “mfc.h”MyApp app;BOOL MyApp::InitInstance() {//创建窗⼝MyFrame* frame = new MyFrame;//显⽰和更新frame->ShowWindow(SW_SHOWNORMAL);frame->UpdateWindow();m_pMainWnd = frame;//保存指向应⽤程序的主窗⼝的指针return TRUE;//返回正常初始化}//分界宏BEGIN_MESSAGE_MAP(MyFrame, CFrameWnd)ON_WM_LBUTTONDOWN()//⿏标左键按下ON_WM_CHAR()ON_WM_PAINT()END_MESSAGE_MAP()MyFrame::MyFrame(){Create(NULL,TEXT(“windows”));}void MyFrame::OnLButtonDown(UINT, CPoint point){//TCHAR buf[1024];//wsprintf(buf, TEXT(“x = %d,y = %d”), point.x, point.y);//MessageBox(buf);CString str;str.Format(TEXT(“x = %d,y = %d”),point.x,point.y);MessageBox(str);}void MyFrame::OnChar(UINT key, UINT, UINT){CString str;str.Format(TEXT(“按下了%c键”),key);MessageBox(str);}void MyFrame::OnPaint(){CPaintDC dc(this);dc.TextOutW(100,100,TEXT(“为了部落”));dc.Ellipse(10,10,100,100);}到此这篇关于如何使⽤visual studio2019创建简单的MFC窗⼝(使⽤C++)的⽂章就介绍到这了,更多相关vs2019创建MFC窗⼝内容请搜索以前的⽂章或继续浏览下⾯的相关⽂章希望⼤家以后多多⽀持!。
VC编程常见问题解答五26、如何创建一个不规则形状的窗口可以VC++编程常见问题解答五26、如何创建一个不规则形状的窗口可以使用新的SDK函数SetWindowRgn。
该函数将绘画和鼠标消息限定在窗口的一个指定的区域,实际上使窗口成为指定的不规则形状。
使用AppWizard创建一个基于对的应用程序并使用资源编辑器从主对话资源中删除所在的缺省控件、标题以及边界。
给对话类增加一个CRgn数据成员,以后要使用该数据成员建立窗口区域。
Class CRoundDlg : public CDialog{…private :Crgn m_rgn : // window region…} ;修改OnInitDialog函数建立一个椭圆区域并调用SetWindowRgn将该区域分配给窗口:BOOL CRoundDlg : : OnInitDialog ( ){CDialog : : OnInitDialog ( ) ;//Get size of dialog .CRect rcDialog ;GetClientRect (rcDialog );// Create region and assign to window .m_rgn . CreateEllipticRgn (0 , 0 , rcDialog.Width ( ) , rcDialog .Height ( ) );SetWindowRgn (GetSafeHwnd ( ) , (HRGN) m_ rgn , TRUE );return TRUE ;}通过建立区域和调用SetWindowRgn,已经建立一个不规则形状的窗口,下面的例子程序是修改OnPaint函数使窗口形状看起来象一个球形体。
voik CRoundDlg : : OnPaint ( ){CPaintDC de (this) ; // device context for painting .//draw ellipse with out any borderdc. SelecStockObject (NULL_PEN);//get the RGB colour components of the sphere colorCOLORREF color= RGB( 0 , 0 , 255);BYTE byRed =GetRValue (color);BYTE byGreen = GetGValue (color);BYTE byBlue = GetBValue (color);// get the size of the view windowCrect rect ;GetClientRect (rect);// get minimun number of unitsint nUnits =min (rect.right , rect.bottom );//calculate he horiaontal and vertical step sizefloat fltStepHorz = (float) rect.right /nUnits ;float fltStepVert = (float) rect.bottom /nUnits ;int nEllipse = nUnits/3; // calculate how many to drawint nIndex ; // current ellipse that is being drawCBrush brush ; // bursh used for ellipse fill colorCBrush *pBrushOld; // previous brush that was selected into dc//draw ellipse , gradually moving towards upper-right corner for (nIndex = 0 ; nIndes < + nEllipse ; nIndes ++){//creat solid brushbrush . CreatSolidBrush (RGB ( ( (nIndex *byRed ) /nEllipse ).( ( nIndex * byGreen ) /nEllipse ), ( (nIndex * byBlue)/nEllipse ) ) );//select brush into dcpBrushOld= dc .SelectObject (&brhsh);//draw ellipsedc .Ellipse ( (int) fltStepHorz * 2, (int) fltStepVert * nIndex ,rect. right -( (int) fltStepHorz * nIndex )+ 1,rect . bottom -( (int) fltStepVert * (nIndex *2) ) +1) ;//delete the brushbrush.DelecteObject ( );}}最后,处理WM_NCHITTEST消息,使当击打窗口的任何位置时能移动窗口。
1.4.2 窗口的创建创建一个完整的窗口,需要经过下面几个操作步骤:*设计一个窗口类;*注册窗口类;*创建窗口;*显示及更新窗口。
下面的四个小分节将分别介绍创建窗口的过程。
完整的例程请参见光盘中的例子代码Chapter1目录下WinMain。
1.设计一个窗口类一个完整的窗口具有许多特征,包括光标(鼠标进入该窗口时的形状)、图标、背景色等。
窗口的创建过程类似于汽车的制造过程。
我们在生产一个型号的汽车之前,首先要对该型号的汽车进行设计,在图纸上画出汽车的结构图,设计各个零部件,同时还要给该型号的汽车取一个响亮的名字,例如“奥迪A6”。
在完成设计后,就可以按照“奥迪A6”这个型号生产汽车了。
类似地,在创建一个窗口前,也必须对该类型的窗口进行设计,指定窗口的特征。
当然,在我们设计一个窗口时,不像汽车的设计这么复杂,因为Windows已经为我们定义好了一个窗口所应具有的基本属性,我们只需要像考试时做填空题一样,将需要我们填充的部分填写完整,一种窗口就设计好了。
在Windows中,要达到作填空题的效果,只能通过结构体来完成,窗口的特征就是由WNDCLASS结构体来定义的。
WNDCLASS结构体的定义如下(请读者自行参看MSDN):typedef struct _WNDCLASS {UINT style;WNDPROC lpfnWndProc;int cbClsExtra;int cbWndExtra;HANDLE hInstance;HICON hIcon;HCURSOR hCursor;HBRUSH hbrBackground;LPCTSTR lpszMenuName;LPCTSTR lpszClassName;} WNDCLASS;下面对该结构体的成员变量做一个说明。
第一个成员变量style指定这一类型窗口的样式,常用的样式如下:n CS_HREDRAW当窗口水平方向上的宽度发生变化时,将重新绘制整个窗口。
1.4.2 窗口的创建创建一个完整的窗口,需要经过下面几个操作步骤:*设计一个窗口类;*注册窗口类;*创建窗口;*显示及更新窗口。
下面的四个小分节将分别介绍创建窗口的过程。
完整的例程请参见光盘中的例子代码Chapter1目录下WinMain。
1.设计一个窗口类一个完整的窗口具有许多特征,包括光标(鼠标进入该窗口时的形状)、图标、背景色等。
窗口的创建过程类似于汽车的制造过程。
我们在生产一个型号的汽车之前,首先要对该型号的汽车进行设计,在图纸上画出汽车的结构图,设计各个零部件,同时还要给该型号的汽车取一个响亮的名字,例如“奥迪A6”。
在完成设计后,就可以按照“奥迪A6”这个型号生产汽车了。
类似地,在创建一个窗口前,也必须对该类型的窗口进行设计,指定窗口的特征。
当然,在我们设计一个窗口时,不像汽车的设计这么复杂,因为Windows已经为我们定义好了一个窗口所应具有的基本属性,我们只需要像考试时做填空题一样,将需要我们填充的部分填写完整,一种窗口就设计好了。
在Windows中,要达到作填空题的效果,只能通过结构体来完成,窗口的特征就是由WNDCLASS结构体来定义的。
WNDCLASS结构体的定义如下(请读者自行参看MSDN):typedef struct _WNDCLASS {UINT style;WNDPROC lpfnWndProc;int cbClsExtra;int cbWndExtra;HANDLE hInstance;HICON hIcon;HCURSOR hCursor;HBRUSH hbrBackground;LPCTSTR lpszMenuName;LPCTSTR lpszClassName;} WNDCLASS;下面对该结构体的成员变量做一个说明。
第一个成员变量style指定这一类型窗口的样式,常用的样式如下:n CS_HREDRAW当窗口水平方向上的宽度发生变化时,将重新绘制整个窗口。
当窗口发生重绘时,窗口中的文字和图形将被擦除。
如果没有指定这一样式,那么在水平方向上调整窗口宽度时,将不会重绘窗口。
n CS_VREDRAW当窗口垂直方向上的高度发生变化时,将重新绘制整个窗口。
如果没有指定这一样式,那么在垂直方向上调整窗口高度时,将不会重绘窗口。
n CS_NOCLOSE禁用系统菜单的Close命令,这将导致窗口没有关闭按钮。
n CS_DBLCLKS当用户在窗口中双击鼠标时,向窗口过程发送鼠标双击消息。
style成员的其他取值请参阅MSDN。
知识点在Windows.h中,以CS_开头的类样式(Class Style)标识符被定义为16位的常量,这些常量都只有某1位为1。
在VC++开发环境中,利用goto definition功能,可以看到CS_VREDRAW=0x0001,CS_HREDRAW=0x0002,CS_DBLCLKS=0x0008,CS_NOCLOSE=0x0200,读者可以将这些16进制数转换为2进制数,就可以发现它们都只有1位为1,并且为1的位各不相同。
用这种方式定义的标识符称为“位标志”,我们可以使用位运算操作符来组合使用这些样式。
例如,要让窗口在水平和垂直尺寸发生变化时发生重绘,我们可以使用位或(|)操作符将CS_HREDRAW和CS_VREDRAW组合起来,如style=CS_HREDRAW |CS_VREDRAW。
假如有一个变量具有多个样式,而我们并不清楚该变量都有哪些样式,现在我们想要去掉该变量具有的某个样式,那么可以先对该样式标识符进行取反(~)操作,然后再和这个变量进行与(&)操作即可实现。
例如,要去掉先前的style变量所具有的CS_VREDRAW样式,可以编写代码:style=style & ~ CS_VREDRAW。
在Windows程序中,经常会用到这种位标志标识符,后面我们在创建窗口时用到的窗口样式,也是属于位标志标识符。
第二个成员变量lpfnWndProc是一个函数指针,指向窗口过程函数,窗口过程函数是一个回调函数。
回调函数不是由该函数的实现方直接调用,而是在特定的事件或条件发生时由另外一方调用的,用于对该事件或条件进行响应。
回调函数实现的机制是:(1)定义一个回调函数。
(2)提供函数实现的一方在初始化的时候,将回调函数的函数指针注册给调用者。
(3)当特定的事件或条件发生的时候,调用者使用函数指针调用回调函数对事件进行处理。
针对Windows的消息处理机制,窗口过程函数被调用的过程如下:(1)在设计窗口类的时候,将窗口过程函数的地址赋值给lpfnWndProc成员变量。
(2)调用RegsiterClass(&wndclass)注册窗口类,那么系统就有了我们所编写的窗口过程函数的地址。
(3)当应用程序接收到某一窗口的消息时,调用DispatchMessage(&msg)将消息回传给系统。
系统则利用先前注册窗口类时得到的函数指针,调用窗口过程函数对消息进行处理。
一个Windows程序可以包含多个窗口过程函数,一个窗口过程总是与某一个特定的窗口类相关联(通过WNDCLASS结构体中的lpfnWndProc成员变量指定),基于该窗口过程。
lpfnWndProc成员变量的类型是WNDPROC,我们在VC++开发环境中使用goto definition功能,可以看到WNDPROC的定义:typedef LRESULT (CALLBACK* WNDPROC)(HWND, UINT, WPARAM, LPARAM);在这里又出现了两个新的数据类型LRESULT和CALLBACK,再次使用goto definition,可以看到它们实际上是long和__stdcall。
从WNDPROC的定义可以知道,WNDPROC实际上是函数指针类型。
注意:WNDPROC被定义为指向窗口过程函数的指针类型,窗口过程函数的格式必须与WNDPROC相同。
知识点在函数调用过程中,会使用栈。
__stdcall与__cdecl是两种不同的函数调用约定,定义了函数参数入栈的顺序,由调用函数还是被调用函数将参数弹出栈,以及产生函数修饰名的方法。
关于这两个调用约定的详细信息,读者可参看MSDN。
对于参数个数可变的函数,例如printf,使用的是__cdecl调用约定,Win32的API函数都遵循__stdcall调用约定。
在VC++开发环境中,默认的编译选项是__cdecl,对于那些需要__stdcall调用约定的函数,在声明时必须显式地加上__stdcall。
在Windows程序中,回调函数必须遵循__stdcall调用约定,所以我们在声明回调函数时要使用CALLBACK。
使用CALLBACK而不是__stdcall的原因是为了告诉我们这是一个回调函数。
注意,在Windows 98和Windows 2000下,声明窗口过程函数时,即使不使用CALLBACK也不会出错,但在Windows NT4.0下,则会出错。
WNDCLASS结构体第三个成员变量cbClsExtra:Windows为系统中的每一个窗口类管理一个WNDCLASS结构。
在应用程序注册一个窗口类时,它可以让Windows系统为WNDCLASS结构分配和追加一定字节数的附加内存空间,这部分内存空间称为类附加内存,由属于这种窗口类的所有窗口所共享,类附加内存空间用于存储类的附加信息。
Windows系统把这部分内存初始化为0。
一般我们将这个参数设置为0。
第四个成员变量cbWndExtra:Windows系统为每一个窗口管理一个内部数据结构,在注册一个窗口类时,应用程序能够指定一定字节数的附加内存空间,称为窗口附加内存。
在创建这类窗口时,Windows系统就为窗口的结构分配和追加指定数目的窗口附加内存空间,应用程序可用这部分内存存储窗口特有的数据。
Windows系统把这部分内存初始化为0。
如果应用程序用WNDCLASS结构注册对话框(用资源文件中的CLASS伪指令创建),必须给DLGWINDOWEXTRA设置这个成员。
一般我们将这个参数设置为0。
第五个成员变量hInstance指定包含窗口过程的程序的实例句柄。
第六个成员变量hIcon指定窗口类的图标句柄。
这个成员变量必须是一个图标资源的句柄,如果这个成员为NULL,那么系统将提供一个默认的图标。
在为hIcon变量赋值时,可以调用LoadIcon函数来加载一个图标资源,返回系统分配给该图标的句柄。
该函数的原型声明如下所示:HICON LoadIcon( HINSTANCE hInstance, LPCTSTR lpIconName)LoadIcon函数不仅可以加载Windows系统提供的标准图标到内存中,还可以加载由用户自己制作的图标资源到内存中,并返回系统分配给该图标的句柄,请参看MSDN关于LoadIcon的解释。
但要注意的是,如果加载的是系统的标准图标,那么第一个参数必须为NULL。
LoadIcon的第二个参数是LPCTSTR类型,利用goto definition命令将会发现它实际被定义成CONST CHAR*,即指向字符常量的指针,而图标的ID是一个整数。
对于这种情况我们需要用MAKEINTRESOURCE宏把资源ID标识符转换为需要的LPCTSTR类型。
知识点在VC++中,对于自定义的菜单、图标、光标、对话框等资源,都保存在资源脚本(通常扩展名为.rc)文件中。
在VC++开发环境中,要访问资源文件,可以单击左边项目视图窗口底部的ResourceView选项卡,你将看到以树状列表形式显示的资源项目。
在任何一种资源上双击鼠标左键,将打开资源编辑器。
在资源编辑器中,你可以以“所见即所得”的方式对资源进行编辑。
资源文件本身是文本文件格式,如果你了解资源文件的编写格式,也可以直接使用文本编辑器对资源进行编辑。
在VC++中,资源是通过标识符(ID)来标识的,同一个ID可以标识多个不同的资源。
资源的ID实质上是一个整数,在“resource.h”中定义为一个宏。
我们在为资源指定ID的时候,应该养成一个良好的习惯,即在“ID”后附加特定资源英文名称的首字母,例如,菜单资源为IDM_XXX(M表示Menu),图标资源为IDI_ XXX(I表示Icon),按钮资源为IDB_ XXX(B表示Button)。
采用这种命名方式,我们在程序中使用资源ID时,可以一目了然。
WNDCLASS结构体第七个成员变量hCursor指定窗口类的光标句柄。
这个成员变量必须是一个光标资源的句柄,如果这个成员为NULL,那么无论何时鼠标进入到应用程序窗口中,应用程序都必须明确地设置光标的形状。
在为hCursor变量赋值时,可以调用LoadCursor函数来加载一个光标资源,返回系统分配给该光标的句柄。