windows编程——窗口与消息2
2014-01-24 15:12
330 查看
消息处理函数
消息处理函数又叫窗口过程,在这个函数中,不同的消息将被switch语句分配到不同的处理程序中去。Windows的消息处理函数的原型是这样定义的:LRESULT CALLBACK WindowProc(
HWND hwnd, //接收消息窗口的句柄
UINT uMsg, //主消息值
WPARAM wParam, //副消息值1
LPARAM lParam //副消息值2
);
消息处理函数必须按照上面的这个样式来定义,当然函数名称可以随便取。
第一次代码中的WinProc( )函数就是一个典型的消息处理函数。在这个函数中明确地处理了几个消息,分别是WM_KEYDOWN(击键)、WM_LBUTTONDOWN(鼠标左键按下)、WM_RBUTTONDOWN(鼠标右键按下)、WM_CLOSE(关闭窗口)、WM_DESTROY(销毁窗口)。值得注意的是,应用程序发送到窗口的消息远远不止以上这几条,像WM_SIZE、WM_MINIMIZE、WM_CREATE、WM_MOVE等频繁使用的消息就有几十条。在附录中可以查到Windows常见消息列表。
为了减轻编程的负担,Windows提供了DefWindowProc( )函数来处理这些最常用的消息,调用了这个函数后,这些消息将按照系统默认的方式得到处理。因此,在消息处理函数中,只须处理那些有必要进行特别响应的消息,其余的消息都可交给DefWindowProc(
)函数来处理。
常用Windows函数
显示对话框
MessageBox函数可以用来显示对话框,它的原型是:int MessageBox(HWND hwndParent, LPCSTRlpszText, LPCSTR lpszTitle, UINT fuStyle);
其中的四个参数依次为:窗口句柄,文字内容,标题,风格。常用风格有:MB_OK、MB_OKCANCEL、MB_RETRYCANCEL、MB_YESNO、MB_YESNOCANCEL,代表对话框有哪些按钮。常用返回值有IDCANCEL、IDNO、IDOK、IDRETRY、IDYES,代表哪个按钮被按下。
定时器
定时器可以使程序每隔一段时间执行一个函数。用法如下:SetTimer(HWND hwnd, UINT ID, UINT Elapse, TIMERPROC TimerFunc);
四个参数依次为窗口句柄、定时器标识(同一程序内各个定时器的标识应不相同,一般从1、2、3...一直排下去)、每隔多少毫秒(千分之一秒)执行一次程序,要执行的过程。
这个要执行的过程应这样定义:
void CALLBACK MyTimer(HWND hwnd,UINT uMsg,UINT idEvent,DWORD dwTime);
这几个规定的参数用法在实例中会了解到,一般很少用到。
注意:定时器的优先级不高,当处理器很忙时我们需要定时执行的程序常常不能按时执行;无论你把定时器的Elapse(时间间隔)设得多小,它实际上最小只能是55ms。
得到时间
我们的程序经常需要得到当前的准确时间来完成测试速度等工作。这时我们可以使用GetTickCount(),因为该函数可以返回Windows已经运行了多少毫秒。等待操作
很多时候,我们在程序的一些位置需要一段延时。虽然可以用for( )等循环语句实现,但不精确,而且都白白地占用了CPU时间。那么我们该调用什么语句呢?Sleep()。括号内填入你想等待的微秒数即可。
播放声音
我们可以使用MCI来简易地实现在程序中播放MP3等声音。使用它需要预先声明,我们需要在文件头包含头文件#include<mmsystem.h>,链接函数库#pragmacomment(lib, "winmm.lib")
下面先让我们看看播放MP3的过程。首先我们要打开设备:
mciSendString("open + MP3路径 alias 音乐在程序里的代名词", NULL, 0, NULL);
接着就可以播放MP3了:
mciSendString("play + 打开时定下的音乐在程序里的代名词", NULL, 0, NULL);
停止播放:
mciSendString("stop + 打开时定下的音乐在程序里的代名词", NULL, 0, NULL);
最后要关闭设备:
mciSendString("close + 打开时定下的音乐在程序里的代名词", NULL, 0, NULL);
多线程编程
基础知识
多线程编程技术在编程中是有不少应用的。举个例子,如果我们要在我们编写的游戏中使用一个超大的场景,那么我们要一边显示图形一边预读数据才能使游戏进行得平滑和流畅。那么如何实行在显示图形得同时预读数据呢?这就可以使用多线程技术。什么是线程?我们知道,一个程序的运行又被称为一个进程(Process),而一个进程可以拥有多个线程(Thread),它们共享进程的各种资源,帮助进程同时做几件事。每个线程都拥有一个优先级别,它决定了此线程执行的优先程度。注意在线程间切换是需要一些时间的,所以同时使用多个优先度很高的线程不是一个好主意。
使用多线程的基本方法并不复杂,有点像使用定时器的方法。我们来看一个实例吧:
首先,给出线程函数的定义:
void f( ) { i++; }
然后在主程序里创建一个线程:
DWORD ThreadID;
HANDLE hThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)f, NULL, 0, &ThreadID);
CreateThread( )的原形如下:
HANDLE CreateThread(
LPSECURITY_ATTRIBUTES lpThreadAttributes, //设定安全属性,一般NULL
SIZE_T dwStackSize,//堆栈大小,一般给它0,即使用默认值
LPTHREAD_START_ROUTINE lpStartAddress, //指向线程函数的指针
LPVOID lpParameter, //线程函数使用的参数(最多一个)的指针
DWORD dwCreationFlags, //如为CREATE_SUSPENDED则暂时挂起线程
LPDWORD lpThreadId //返回线程的标识
);
由于我们把dwCreationFlags设为0,现在f线程就会开始执行;如果把它设为CREATE_SUSPENDED或在此后执行了SuspendThread(
hThread );(挂起进程),执行ResumeThread ( hThread );(继续挂起的进程) 可以让它继续执行。
我们可以执行SetThreadPriority(hThread, nPriority )设定线程的优先级,nPriority的可能取值有:
nPriority取值 | 含义 |
THREAD_PRIORITY_IDLE | 线程为最低优先级 |
THREAD_PRIORITY_LOWEST | 线程为很低优先级 |
THREAD_PRIORITY_BELOW_NORMAL | 线程为较低优先级 |
THREAD_PRIORITY_NORMAL | 线程为一般优先级 |
THREAD_PRIORITY_ABOVE_NORMAL | 线程为较高优先级 |
THREAD_PRIORITY_HIGHEST | 线程为很高优先级 |
THREAD_PRIORITY_TIME_CRITICAL | 线程为最高优先级 |
得到进程:HANDLE hProcess = GetCurrentProcess( );
设定进程优先级:SetPriorityClass(hProcess, dwPriorityClass );
dwPriorityClass的各种取值意义如下表:
dwPriorityClass取值 | 含义 |
IDLE_PRIORITY_CLASS | 进程为最低优先级 |
BELOW_NORMAL_PRIORITY_CLASS | 进程为较低优先级(系统须为Win2000或XP) |
NORMAL_PRIORITY_CLASS | 进程为一般优先级 |
ABOVE_NORMAL_PRIORITY_CLASS | 进程为较高优先级(系统须为Win2000或XP) |
HIGH_PRIORITY_CLASS | 进程为很高优先级 |
REALTIME_PRIORITY_CLASS | 进程为最高优先级(慎用) |
hThread, 返回值 );,该函数会造成诸多不良后果,不宜使用。
协调线程
多线程技术的真正难点在于如何协调多个线程。关键问题是如何控制各个线程对进程数据的访问。多个线程操作相同的数据时,一般是需要按顺序访问的,否则会多个线程重复控制一个变量,数据错乱。为解决这个问题,就需要引入互斥变量,让每个线程都按顺序地访问变量。CRITICAL_SECTION CriticalSection1; //我们假想此全局变量代表了i的使用权
InitializeCriticalSection(&CriticalSection1);//使i成为临界资源,也就是说一次只能有一个线程访问的资源
设置完以后,假如有第二个线程访问临界资源,就会先挂起,等待第一个线程用完临界资源、"离开"
EnterCriticalSection(&CriticalSection1);//此语句会自动等待
i--; //可以放心地使用i;
使用完数据后记得“离开”:
LeaveCriticalSection(&CriticalSection1);
最后要解除临界资源:
DeleteCriticalSection(&CriticalSection1);
还有一个就是等待另一个线程结束,这可以通过调用下面的语句实现:
WaitForSingleObject(hThread, dwMilliseconds );
其中dwMilliseconds为最多等待多少毫秒,若为INFINITE,则永远等下去。
如果等待多个线程,可以这样做:
WaitForMultipleObjects( 线程数,线程数组的指针, bWaitAll, dwMilliseconds );
bWaitAll如果为true,此语句等待至全部线程完成;若为false,此语句等待至线程数组中任一线程完成。
这一章第一次看有一点印象就好,以后在实践中才能完全掌握,以后用到可以回来再看
————2014年1月23日17:44:07
相关文章推荐
- windows基础编程----第三篇(窗口的消息处理机制)
- Windows编程 从消息窗口到基本窗口 游戏循环窗口框架的简单实现
- windows 编程随笔——窗口和信息补充之消息循环
- windows编程——窗口与消息1
- 初学Windows编程笔记1——窗口和消息
- 初学Windows编程笔记1——窗口和消息
- windows编程学习笔记(1)创建窗口与消息循环
- WINDOWS窗口创建及消息处理代码
- Directx3D9学习之二:Windows编程之最简单窗口程序
- Windows编程基础 第三章 基本窗口创建过程 MDI窗口创建
- Windows编程基础 第五章 鼠标消息 定时器 菜单
- windows编程(2)深入探讨MFC消息循环和消息泵
- Windows编程入门-WM_PAINT消息
- 【Windows编程】Step.2 消息循环机制
- Windows编程窗口
- 眼见为实(2):介绍Windows的窗口、消息、子类化和超类化
- Windows消息编程
- Windows编程 Windows窗口的从诞生到死亡
- Windows消息编程