一.前言
- 大家好!这是我的第一篇博客,首先来个自我介绍吧!我是来自一所双非院校的大一新生,所学的专业是计算机科学与技术,纯纯小白一枚。
- 那我为什么要写博客呢?为什么?说实话,我一开始一直都没有写博客的想法,我都是看别人的博客,直到有一天,我的好同学李同学来向我推销他的博客,我那时就很惊讶,哇,你大一就会写博客了,这么厉害。他就跟我说:“你也可以的,就当做笔记吧。”(他的ID是:qq_64484137,也是一个大一新生博主,很厉害的,大家快去关注他一波!冲冲冲!)之后我也想了想,写博客也有很大的好处,你可以在写博客的过程中不断巩固自己的知识,提高自己的能力,是一种很好的学习方法,还有一点就是我这个人嘛稍微有一点点内敛,所以我也想通过写博客来改变自己,想成为一个比较阳光开朗的人(哈哈哈,说出来我都有点不好意思)。于是,我便有了写博客的想法。
- 就在不久,我们老师刚刚教完Windows应用程序基础,说实话,我听的也不是很懂,但我通过最近的学习和实践,基本上懂了(不敢说精通)。所以接下来,我来跟大家讲讲Windows应用程序的相关基础知识(绝对简单易懂),系好安全带,我们出发啦!
(本次使用的编译器为Microsoft Visual C++ 2010 Express,创建项目:新建项目->Win32项目->勾选空项目以及Windows应用程序->完成)
二.Windows编程基础知识
(1)窗口
窗口是Windows本身及其应用程序的基本界面单位,是应用程序生成的显示在屏幕上的一个矩形区域,是应用程序与用户间的直接接口;应用程序控制与窗口有关的一切内容,包括窗口的大小、风格、位置及窗口内显示的内容等。
Windows编程说白了,就是编一个窗口,使这个窗口具有显示和人机互动等功能。你现在看到的页面就是一个窗口,这个窗口显示了我写给你们的内容(显示功能),你们也可以对我的内容进行复制和粘贴等一系列操作(人机互动功能)。
(2)事件驱动
Windows采用事件驱动方式,即程序的流程不是由代码编写的顺序来控制的,而是由事件的发生来控制的,所有的事件是无序的,因此Windows应用程序是密切围绕信息的产生与处理而展开的,主要任务就是对接收事件发出的信息进行排序和处理。程序的执行过程就是选择事件和处理事件的过程,而当没有如何事件触发时,程序会因为查询事件队列失败而进入睡眠状态,从而释放CPU。
事件驱动怎么理解呢?事件,驱动。是不是有事件才会驱动,没事件就不会驱动?没错,就是这个道理,当有事件发生时,系统会把这些事件收集起来,然后发送给处理器,经过处理器的处理后,最后做出与事件相对应的响应。是不是很好理解呀。
(3)句柄
句柄(Handle)是整个Windows编程的基础。一个句柄是指使用的唯一的整数值,即以一个字节(程序中64位为8字节)长的数值,来标识应用程序中的不同对象和同类中的不同的实例,如一个窗口、按钮、滚动条、输出设备或文件等。应用程序能够通过句柄访问对应的对象的信息。
句柄是很重要的。但是大家是不是对句柄这东西感觉好难理解。句柄?什么东西啊!说实话,当我听老师讲句柄的时候,我也是一头雾水。后来我去网上查资料的时候发现了一个很好的解释,句柄它就是一个ID,一个标识符。怎么用呢?很简单。大家都知道C++怎么定义一个整型变量吧。
int x;//定义一个整形变量
接下来我们定义一个窗口句柄。
HWND hwnd;//定义一个窗口句柄
大家发现了没,它们定义的方式一模一样,如果你想定义一个整形数据那就用int,如果你想定义一个窗口句柄那就用HWND。这样对比的话,大家应该容易来理解吧。(句柄这个翻译有点害人啊)
句柄类型 | 说明 |
---|---|
HWND | 窗口句柄 |
HINSTANCE | 程序实例句柄 |
HCURSOR | 鼠标句柄 |
HFONT | 字体句柄 |
HIPEN | 画笔句柄 |
HBRUSH | 画刷句柄 |
BIDC | 图形设备环境句柄 |
HBITMAP | 位图句柄 |
HICON | 图标句柄 |
HMENU | 菜单句柄 |
HFILE | 文件句柄 |
三.Windows中的事件驱动程序设计
- 终于来到我们的重头戏了,前面的都是概念理论,会比较难理解,所以接下来我们将通过实践来看看这些概念理论是如何应用的。(一定要动手哦,这样才会提高的)
- Windows程序的执行过程是:程序入口、定义窗口类、注册窗口类、创建窗口、更新窗口、显示窗口、消息循环、窗口消息处理、程序执行实体结束。接下来我会逐一给大家详细讲讲。
(1)程序入口函数
在C/C++语言程序中,其中入口函数都是函数main()。但是在Windows程序中,在这个入口函数由WinMain()来代替,其原型为:
int WINAPI WinMain(HINSTANCE hInstance,//操作系统使用此值来标识 EXE (加载到) 可执行文件。 某些函数需要实例句柄Windows例如,加载图标或位图。
HINSTANCE hPrevInstance, //没有意义。 它用于 16 位Windows,但现在始终为零。
LPSTR lpCmdLine,//用于指定程序的命令行,是指向字符串的指针类型。
int nCmdShow//是一个标志,指示主应用程序窗口是最小化、最大化还是正常显示。
)
- WINAPI 是调用约定,调用约定定义函数如何从调用方接收参数。 例如,它定义参数在堆栈上的显示顺序。
- 函数返回 int 值,返回值不由操作系统使用,但可以使用返回值将状态代码传达给你编写的其他程序。
说实话,我们有时候学东西的时候要有分寸,什么叫做要有分寸?就是你要知道哪些东西你值得去深入学习,哪些东西不必要过于深究。就拿上面的入口函数里的参数来讲,你没必要去真正的搞懂它为什么会这么用,就算你真正的搞懂了用处也不大,而且还费时间。所以,只要知道它的一些意义就行了。
所以,对于入口函数,我们只要知道它是这么定义的就好了。
(2)定义窗口类
创建好程序入口函数后,接下来我们来看看如何定义一个窗口。定义窗口,就是定义窗口的基本属性,如窗口边框、窗口标题栏文字、窗口大小和位置、鼠标、背景色、处理窗口消息函数的名称等。
窗口类定义通过给窗口类数据结构WNDCLASS赋值来完成,该数据结构中包含窗口类的各种属性。我们先来看看WNDCLASS的结构。
typedef struct tagWNDCLASS
{
UINT style; //窗口的风格
WNDPROC lpfnWndProc; //指定窗口的消息处理函数的窗口过程函数
int cbClsExtra : //指定分配给窗口类结构之后的额外字节数
int cbwndExtra; //指定分配给窗口实例之后的额外字节数
INSTANCE hInstance : //指定窗口过程所对应的实例句柄
HICON hIcon; //指定窗口的图标
HCURSOR hCursor; //指定窗口的鼠标指针
HBRUSH hbrBackground; //指定窗口的背景画刷
LPCTSTR lpszMenuName; //窗口的菜单资源名称
LPCTSTR lpszClassName; //该窗口类的名称
}WNDCLASS, * PWNDCLASS;
以上就是WNDCLASS的结构体,在实际操作中,我不需要把这些代码打出来,因为编译器已经把它隐含定义了,我们只需要调用并且对它们赋值就行了。
WNDCLASS wcex; //定义一个窗口wcex
wcex.style = CS_HREDRAW | CS_VREDRAW;
wcex.lpfnWndProc = WndProc;
wcex.cbClsExtra = 0;
wcex.cbWndExtra = 0;
wcex.hInstance = hInstance;
wcex.hIcon = LoadIcon(wcex.hInstance, IDI_APPLICATION);
wcex.hCursor = LoadCursor(NULL, IDC_ARROW);
wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
wcex.lpszMenuName = NULL;
wcex.lpszClassName = szWindowClass;
有一些属性成员有不同的值可以赋,不同的值不同的效果,我直接把定义窗口的总链接给大家,我就不在这里赘述了。
链接:详细内容.
(3)注册窗口类
虽然我们定义了一个窗口,但是系统并不知道这个窗口的存在,所以接下来我们通过RegisterClass()函数来注册窗口类。
RegisterClass(&wcex);
注册窗口类这一步很简单。
(4)创建窗口类
在注册窗口后,并没有生成一个窗口实体,我们可以用函数CreateWindows()来创建窗口,其代码为:
HWND WINAPI CreateWindow( //返回值是窗口句柄
LPCTSTR lpClassName, //窗口类名,要与注册时指定的名称相同
LPCTSTR lpWindowName, //窗口标题
DWORD dwStyle, //窗口样式
int X, //窗口最初的x位置
int y, //窗口最初的y位置
int nWidth, //窗口最初的x大小
int nHeight, //窗口最初的y大小
HWND hWndParent, //父窗口句柄
HMENU hMenu, //窗口菜单句柄
HINSTANCE hInstance, //应用程序实例句柄
LPVOID lpParam //指向一个传递给窗口的参数值的指针,以便后续在程序中加以引用
);
其具体创建实例的代码为
hwnd=CreateWindow ( "HelloWin",
"我的窗口",
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT,
CW_USEDEFAULT,
480,
320,
NULL,
NULL,
hInstance,
NULL
);
创建窗口时,我们只需要按照参数的位置给其逐一赋值即可,如果想知道详细内容可以点击下方链接了解。
链接:详细内容
(5)显示窗口
在创建窗口后,可以使用函数ShowWindows()显示窗口,其代码为
BOOL ShowWindow(HWND hwnd,int nCmdShow);
其中,参数 hWnd 指定要显示的窗口的句柄,nCmdShow 表示窗口的显示方式,可以指定为从函数 WinMain()的nCmdshow 所传递而来的值。
由于函数 Show Window()的执行优先级不高,所以当系统正忙着执行其他任务时,不会立即显示窗口,此时,调用函数 Update Window()立即显示窗口,其代码为
UpdateWindow (HWND hwnd);
同时,函数UpdateWindow()还会给窗口过程发出 WM_ PAINT 消息使窗口客户区重绘。
(6)消息循环
Windows 操作系统是基于消息控制机制进行工作的,系统生成应用程序的消息队列,并将产生的消息放人其中,各应用程序不断地从消息队列中提取消息,并将其传递给窗口函数以进行相应的处理。函数GetMessage()就是用于从应用程序的消息队列中按照先进先出的原则将消息一个个地取出来,并放入一个MSG结构中。
我们先来看GetMessage()函数的结构:
BOOL GetMessage(
LPMSG lpMsg, //指向一个MSG结构的指针,用于保存信息
HWND hWnd, //指定获取具体窗口的消息
UINT wMsgFilterMin, //指定获取主消息值的最小值
UINT wMsgFilterMax //指定获取主消息值的最大值
);
LPMSG lpMsg是用来存储信息的,接收来的信息都存储在lpMsg指针里。函数GetMessage()可以过滤消息,比如定义HWND hWnd,系统就只能接收来自hWnd窗口的消息,如果该参数为NULL(意思为空,就是什么都没有),这时函数GetMessage()从该来自该应用程序线程的所有窗口的消息队列中获取消息。wMsgFilterMin和wMsgFilterMax的作用是过滤,函数GetMessage()只能接收wMsgFilterMin和wMsgFilterMax之间的消息。如果wMsgFilterMin和wMsgFilterMax都为0,那么表示接收所有消息。
我们来看看怎么写,应用程序通过while循环来获取消息,其代码为:
while (GetMessage(&msg,NULL,0,0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
函数TranslateMessage()的作用是将虚拟键消息转换为字符消息,以满足键盘输入的需要。
函数DispatchMessage()的作用是将将消息分派到窗口过程。
当且仅当函数GetMessage()在获取到消息WM_QUIT后,返回0值,于是程序退出消息循环。
有关函数GetMessage()的详细内容大家可以通过链接了解了解:链接.
(7)窗口函数
应用程序获取到消息后就调用窗口函数进行相应处理,窗口函数定义了应用程序对接收的不同消息的响应及处理过程,其代码为:
LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
HDC hdc;
PAINTSTRUCT ps;
RECT rect;
switch(message)
{
case WM_PAINT:
hdc=BeginPaint(hwnd,&ps);
GetClientRect(hwnd,&rect);
DrawText(hdc,TEXT("hello win"),-1,&rect,DT_SINGLELINE|DT_CENTER|DT_VCENTER);
EndPaint(hwnd,&ps);
return 0;
case WM_DESTROY:
PostQuitMessage(0);
return 0;
}
return DefWindowproc(hwnd,message,wParam,lParam);
}
-
CALLBACK WndProc()是在应用程序中定义的回调函数,用于处理发送到窗口的消息。接收的消息都会在这里进行处理,并作出对应的响应。
-
HWND hwnd 表示的是接收消息的窗口的句柄,这个句柄值与函数 Create Window()返回的值相等,如果用同一个
WNDCLASS 建立多个窗口,那么hwnd 标识特定的某个窗体。 -
UINT message 为标识窗体的数值。
-
WPARAM WParam 和 LPARAM IParam 都是 32 位消息的附加信息参数
-
程序本身通常自己不呼叫窗口消息处理程序,而由系统呼叫窗口消息处理函数,应用程序如果希望调用自身的窗口过程,那么可通过调用函SendMessage()来实现。
-
窗口函数使用 switch 和case 结构处理消息,开发者只需根据消息在 case 语句中编写相应的处理程序即可。
-
窗口消息处理程序在处理消息时,必须返回一个0值。
-
在 case 语句的消息处理程序段中,一般都有对消息 WM_DESTROY的处理,该消息是关闭窗口时发出的。一般情况下,应用程序调用函数PostQuitMessage()响应这条消息,其原型为:
void PostQuitMessage (int nExitcode);//nExitcode为应用程序退出代码
-
函数 PosrQuicMessage()的作用是向应用程序发出 WM_ QUIT 消息,并请求退出系统。
-
窗口消息处理程序不予处理的所有消息应该被传递给名为DefWindowProc()的 Windows函数,而且从函数DefWindowProc()传回的值必须由窗口消息处理程序传回。
以上内容大家有个基本的了解就行了。
由于我的水平有限,有些东西我不太懂,不好讲,也是怕误导(自己都不懂还讲,那就是害人了),所以我找了一些官方链接给你们,官方的链接是讲的很清楚的,大家可以去看看。
关于WNDPROC() 回调函数:这.
关于DrawText()函数:这里.
关于BeginPaint()函数:here.
关于GetClientRect()函数:点这.
关于EndPaint()函数:点.
关于PostQuitMessage()函数:它.
关于DefWindowproc()函数:这哦.
在这里我随便把总链接(关于Windows应用程序的基本内容)给大家:总链接.
四.实践
讲了这么多,我来给大家实践一波吧。
#include <windows.h>
#include <tchar.h>
static TCHAR szWindowClass[] = _T("DesktopApp");//主窗口类名。
static TCHAR szTitle[] = _T("好好学习Windows应用程序!");//出现在应用程序标题栏中的字符串。
int WINAPI WinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpCmdLine,
int nCmdShow
)
{
LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam);//要先声明一下
WNDCLASS wcex; //定义一个窗口wcex
wcex.style = CS_HREDRAW | CS_VREDRAW;
wcex.lpfnWndProc =WndProc;
wcex.cbClsExtra = 0;
wcex.cbWndExtra = 0;
wcex.hInstance = hInstance;
wcex.hIcon = LoadIcon(wcex.hInstance, IDI_APPLICATION);
wcex.hCursor = LoadCursor(NULL, IDC_ARROW);
wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
wcex.lpszMenuName = NULL;
wcex.lpszClassName = szWindowClass;
RegisterClass(&wcex);
HWND hwnd=CreateWindow(szWindowClass,
szTitle,
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT,
CW_USEDEFAULT,
480,
320,
NULL,
NULL,
hInstance,
NULL
);
ShowWindow(hwnd,nCmdShow);
UpdateWindow(hwnd);
MSG msg;
while (GetMessage(&msg,NULL,0,0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
HDC hdc;
PAINTSTRUCT ps;
RECT rect;
switch(message)
{
case WM_PAINT:
hdc=BeginPaint(hwnd,&ps);
GetClientRect(hwnd,&rect);
DrawText(hdc,TEXT("加油!相信你自己!"),-1,&rect,DT_SINGLELINE|DT_CENTER|DT_VCENTER);
EndPaint(hwnd,&ps);
return 0;
case WM_DESTROY:
PostQuitMessage(0);
return 0;
}
return DefWindowProc(hwnd, message, wParam, lParam);
}
运行结果如下:
注意:你可能会发现,个别代码格式没有见过,是版本的问题,主要是关于SAL,大家可以通过链接了解一下, SAL。
五.结语
哎。历时差不多一天半,终于写完了!写完的第一个感觉就是好累啊!!!如果你是认真看完的话,你会发现,越到后面,我写的情绪越来越低落了,没办法啊,上午上完体育课,一吃完饭就继续写都没有休息,好累,但是就算累我也要把这个写完,因为男人不能说不行!。写完之后我也挺开心的,因为我做了一件我从来没有做过也从来没有想过会做的事,成就感还是有的。写博客还是很有意义的,不仅可以帮助我巩固和拓展我的知识,挺高我的能力,也可以帮助到大家,为大家解答疑惑。我的第一篇博客就要结束了,回想写作的过程经历了许多,有激动的时候,也有低落的时候,大家情绪低落的时候可以多运动运动或者听听歌,结束了。结束了?不可能的,我会继续努力,这对我来说这是一个新的起点,我要学会改变自己,以新的自己去面对未来,过去的我可能会有点不好,但我相信如果我够努力,努力提升自己,我可以达到我心里的那个目标。还有,我会根据我们上课的进度来更新。所以,可不可以点个赞,关注一下我呀,也可以在评论区留下你的足迹,有了你们的支持,我才会走的更远!如果有什么问题,欢迎大家提问!最后,感谢大家!谢谢!我们后会有期!文章来源:https://www.toymoban.com/news/detail-441325.html
(声明一下:本人是大一新生,第一次写博客,难免会出现错误,如果大家发现错误,请及时告诉我,我会及时更正,谢谢大家!)文章来源地址https://www.toymoban.com/news/detail-441325.html
到了这里,关于Windows应用程序基础(详细,简单易懂)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!