1. CUI程序与GUI程序
CUI程序:控制台界面
GUI程序:图形用户界面程序
2. windows编程头文件
通常我们包含windows.h头文件,它是一个 综合型头文件。
其次还有一些头文件,例如:tchar.h,通用字符串的定义 Commctrl.h控件API的定义
3. WinMain函数
C语言中有一个main函数,在windows编程中有一个WinMain函数,对于WinMain函数来说,他的形式永远那么固定
int APIENTRY _tWinMan( _In_ HINSTANCE hInstance,_In_opt_ HINSTANCE hPrevInstance,_In_ LPTSTR lpCmdLine,_In_ int nCmdShow)
其中APIENTRY指明了函数调用方式是stdcall
WinMain是一个宏定义,自适应win32程序的版本是多字节还是UNICODE版本 (多字节编码ASC,宽字节编码unicode)
hInstance是程序的实例句柄,它是程序加载地址
其他参数略
4. 字符编码
ASCLL字符占1个字节
Unicode占2个字节,使用Unicode编码可以使工程同时支持多种语言,在系统底层开发中,字符串都是以Unicode编码形式进行操作
Tip :当我们定义UNICODE宏时候,CreateWindowEx是宽字节版函数,没有定义UNICODE宏,CreateWindowEX是多字节版函数,这里CreateWindow是一个宏,我们切记,在windows编程中,根本就没有CreateWindowEx这个函数
常用字符串处理函数:
ACS版 | UNICODE | T版 | |
获取长度 | strlen | wcsnlen | _tcslen |
字符串拷贝 | strcpy_s | wcscpy_s | _tcscpy_s |
字符串转数字 | atoi | _wtoi | _tstoi |
sscanf_s | swscanf_s | _stscanf_s | |
数字转字符 | sprintf_s | swprintf_s | _stprintf_s |
DWORD 实际是 unsigned long
BOOL 实际是 int
WCHAR 实际是 w_char
WCHAR 实际是 w_char
UINT 实际是 unsigned int
所有windows数据类型都是通过在SDK头文件中定义的,它们都是来源于标准C的数据类型
6. windows句柄和对象
句柄:用以代表一个对象,然后使用函数操作这个对象的时候,就把句柄传进去,在作用上类似于C++this指针。
常见的句柄类型有:HWND 窗口句柄 HMODULE模块句柄 HDC环境绘图句柄 HMENU菜单句柄 HANDLE内核对象句柄
每一种句柄类型都是一种数据类型
7. WINDOWS错误处理
我们根据API函数的返回值来判断函数是否成功,但是我们要进一步知道函数错误在哪里,所有在WINDOWS下每一个线程都有一块区域能够存储错误码,可以使用SetLastError(),这个API设置一个数值,每一个API函数退出之前都会调用这个函数,并且给此API设置一个错误码,我们在API调用结束之后,可以调用GetLaseError()得到错误码,再根据错误码判断是什么错误
Tip:我们想知道某一个函数的调用结果,在函数调用完毕的地方,立即使用GetLastError函数,否则错误码可能被其他API覆盖
查看错误码的方式:
- 在VS中自带工具 “错误查找”
- 使用FormatMessage函数,有错误码得到错误字符串
- 在VS监视栏中输入 err,hr可以时时查看API的监控结果 (这个最为方便)
8. windows编程原理
windows编程程序功能的实现原则是事件驱动函数,我们也可以成为消息驱动函数 事件也为消息
而消息的产生一般代表有一个事件需要窗口程序去处理
消息产生的条件:
用户主动产生消息 :用户点击了窗体,按下了键盘
windows系统本身产生的消息:系统时间的更改,系统即将被关闭
应用程序本身产生的消息:应用程序主动给自己发送一个消息,例如调用了MoveWindow窗口函数
其他应用程序发送过来的消息
消息的流动流程:windows内部有一个消息队列,大部分消息会先进系统消息队列,每一个程序也有消息队列,操作系统能够识别出来消息队列的消息属于哪个程序,从而将消息队列中消息发送给程序的消息队列
消息泵,又称消息循环
GetMessage(LPMSG lpMsg, // 消息结构体地址 (MSG)
HWND hWnd, // 窗口句柄
UINT wMsgFilterMin, // 第一个消息
UINT wMsgFilterMax // 最后一个消息
)
消息泵大概流程:GetMessage从主线程消息队列中获取一个消息将他赋值到MSG中,然后进入while循环
TranslateAccelerator判断是不是一个按键消息,如果是按键消息,将按键消息转换成一个WM_CHAR发送给回调函数
DispatchMessage函数把此信息发送给消息指定窗口中设定的回调函数
回调函数模型
LRESULT CALLBACK WindowProc(
HWND hwnd; //窗口句柄
UINT umessage; //消息ID
WPARAM wParam; //消息参数1
LPARAM lParam; //消息参数2
)
这里需注意,返回值类型为 LRESULT 因为回调函数返回值必然是调用DefWindowProc函数来处理,所有回调函数返回值不能为0
LRESULT本质就是 long 长整形 。
DefWindowProc(); 此函数返回值即是上面回调函数的返回值,本函数作用,它将窗口不处理的消息默认传递给系统做默认处理,此函数在 回调函数中必不可少
9. 创建窗口
1.先设计一个窗口类
2.在注册窗口类
3.创建窗口
4.显示刷新窗口
5.建立消息泵,循环接受处理消息(前提要将回调函数写好)
10. 消息机制
消息是windows操作系统发给应用程序的一个通告,他告诉应用程序某个特定的事件发生了
系统发送给应用程序某个消息,最终处理消息的是应用程序的窗口函数,如果程序不负责处理,此消息就被系统默认处理
(所以在窗口回调函数返回值不是0,而是我们写的 return DefWindowProc(hWnd,uMsg,wParam,lParam) )
消息本身是作为一个记录传递给应用程序,这个记录中包含了消息的类型,以及其他信息 (这里也很好理解,因为消息的所有信息是被MSG结构体保存,里面的不同字段代表着不同的消息类型,以及消息的附加信息)
这里再次说明下MSG结构体里面几个重要的参数,也是我们常见的参数,例如
- HWND hwnd 窗口句柄,表示发给哪个窗口,通常在设计窗口类的时候已经指定了是发给哪个窗口
- UINT message 消息ID,表示发送的哪个消息,也就是我们通常说的消息号,根据号来告诉程序,发送了什么消息类型 比如WM_COMMAND消息号0X111
- wParam 通常是一个与消息相关的常量值,它的存在是取决于你是什么消息类型,也就是上一条。也可能是窗口或者控件的句柄
- lParam 通常是一个指向内存中数据的指针,由于WParam,lParam都是32位,他们之间都可以转换
这里我们来简单说说wParam和lParam
我们可以确定的是wParam和lParam是根据message的不同,他们才发生改变,他们具体代表什么,并没有固定的说法
但是,一般来说wParam表示控件的ID,或者高16位和低16位组合起来分别表示鼠标的位置,如果发消息的时候需要附带某种结构的指针或者某种类型的句柄,习惯上用lParam.
我们还可以用 LOWORD 和 HIWORD 来取消息的低16位和高16位 他们是两个宏
Tip:我们常见的WM_COMMAND消息
消息来源 | wParam(高16位) | wParam(低16位) | IParam |
菜单 | 0 | 菜单ID | 0 |
快捷键 | 1 | 快捷键ID | 0 |
控件 | 控件通知码 | 控件ID | 控件句柄 |
这时候 WORD wHigh = HIWORD(wParam)
WORD wLow = LOWRD (wParam)
这里 wHigh 取到的是控件的窗口句柄
wLow 取到的是控件ID
消息标识符的值
消息标识符的取值范围:在0x0000 到 WM_USER(0x400)
如果我们要自定义消息,在后面MFC中使用类向导去自定义一个消息,我们需要声明消息宏的时候,范围要大于WM_USER。通常我们可以 WM_USER+1去定义自定义消息宏
消息的种类
大致分为三种,通用窗口消息,控件消息,自定义消息
通用窗口消息,通常以WM开头
控件消息,控件整体上能够使用通用窗口消息,对于不同控件还有自己能够处理的消息
自定义消息,就是我们上面所说的我们可以根据用途去规定,wParam和lParam含义
队列消息和非队列消息
从消息发送的途径来看,消息可以分为两种:队列消息和非队列消息
队列消息,最常见的就是鼠标和键盘触发的消息 当鼠标键盘事件被触发,相应的鼠标和键盘驱动程序就会把这些事件转换成相应的消息,然后输送到系统消息队列,由Windows系统去进行处理,Windows系统就会把在系统消息队列里面取出一个消息,保存到MSG消息结构体,在将MSG结构体发送给我们的窗口,下面的事情就由我们线程消息队列去解决,Window开始自己忙自己的事
非队列消息,会绕过系统队列和消息队列,直接将消息发送给窗口
消息的发送
我们上面说了消息队列的概念,下面我们就详细说说,消息是如果发送给我们的窗口的
把一个消息发送到窗口有三种方式:发送,寄送,和广播
发送消息:常见的函数有SendMessage(非队列消息),这个函数的主要作用向一个或者多个窗口发送一条消息,一直等消息被处理了才会返回
寄送消息:常见的函数是PostMessage(队列消息) 该函数把一条消息放置创建hWnd窗口的线程的消息队列中,该函数不等消息被处理就马上将控制返回
以上者两者具有代表性的函数,我们可以分析下发送消息和寄送消息,这两种方式不同:
被发送的消息不会被立即处理,函数不会立即返回
被寄送的消息不会被立即处理,它会被放进一个先进先出的队列中,一直等到应用程序空线的时候才会被处理,不过函数放置完消息后立即返回
消息的接受
消息接受的函数主要有三个:GetMessage, PeekMessage, WaitMessage
GetMessage(LPMSG lpMsg, HWMD hWnd, UINT wMsgFilterMin, UINT wMsgFilterMax)
该函数用来获取与hWnd参数所指定的窗口相关的wMsgFilterMin和wMsgFilterMax参数所给出消息值范围内的消息,如果hWnd为NULL,则GetMessage获取属于该调用函数应用程序的任一窗口的消息,如果wMsgFilterMin和wMsgFilterMax都为0,则GetMessage可以返回所有得到的消息,函数获取消息之后,就删除消息队列中的除了WM_PAINT消息之外的其他消息,至于WM_PAINT则只有被处理后才删除
对于上面的描述解释下:GetMessage这个函数第一个参数是消息MSG结构体的地址,里面可以得到消息的各种信息,通常我们传参&msg ,至于第二个参数hWnd,我们通常写0,因为如果写0,这个GetMessage函数就可以获得该应用程序的任意窗口消息,这也是我们想要做到的,我们通常情况下不会希望,哪一个窗口不被发消息,不被获取消息。第三个参数和第四个参数,从参数原型就可以看出一个是min一个是max,它代表我们获取消息的范围值(通常是0-400),但是如果我们自定义消息他的默认范围就会变化,所以我们这里通常填写 0 0,它代表GetMessage获取所有的消息。
当函数获取消息后,系统消息队列的传递任务就完成了,系统消息队列就可以把这些消息删除了。但是:WM_PAINT消息要等其被处理后才能被删除,它是重绘消息(特殊)
PeekMessage(LPMSG lpMsg, HWMD hWnd, UINT wMsgFilterMin, UINT wMsgFilterMax, wRemoveMsg)
PeekMessage与GetMessage区别在于:该函数不会等有消息进去队列才返回
WaitMessage()
当一个应用程序无事可做时候, 该函数就将控制权交给另外的应用程序,同时将该程序挂起,知道有一个新的消息放入应用程序队列中才返回
11. Window窗口
窗口的风格有三种类型:重叠窗口, 弹出窗口, 子窗口
WS_OVERLAPPED WS_POPUP WS_CHILD
重叠窗口:是顶级窗口,有一个标题栏,边框,客户区,它的目的是作为一个应用程序的主窗口,它也可以有一个窗口菜单,最大化和最小化的按钮,滚动条
弹出窗口:是顶级窗口,并且连接到桌面窗口的子窗口列表,弹出窗口多用于对话框或者MESSAGBOX,它具有WS_POPUP风格
子窗口: 通常会有WS_CHILD风格,并且只能够被分配到父窗口的客户区域,子窗口必须要有父窗口,父窗口可以是层叠窗口,也可以是弹出窗口,也可以是其他子窗口
也就是说,子窗口不能移出父窗口的客户区(解释上面红色语句)
我们可以使用CreateWindowEx()为 窗口扩展风格
窗口层次结构
我们可以从桌面窗口出发,遍历得到所有窗口具体代码:
int _tmain(int argc, _TCHAR* argv[])
{
//得到桌面窗口句柄
HWND hwnd = GetDesktopWindow();
//得到屏幕的第一个子窗口
hwnd = GetWindow(hwnd,GW_CHILD);
char szname[266] = { 0 };
//循环遍历得到所有的子窗口
while (hwnd!=NULL)
{
memset(szname, 0, 266); //每次进入循环,清空缓冲区
GetWindowTextA(hwnd, szname, 266); //获得窗口的窗口名
printf("%s
", szname);
hwnd = GetNextWindow(hwnd, GW_HWNDNEXT); //赋值给下一个窗口
}
return 0;
}
对于以上函数,解释下代码
GetDesktopWindow() 获取桌面窗口句柄
GetWindow() 返回于指定窗口有特定关系的窗口句柄,这里,传入GW_CHILD 获得相对于屏幕的第一个子窗口句柄 (这里必须是有特定关系)
memset 初始化缓冲区
GetWindowTextA() 获取指定窗口句柄的标题
GetNextWindow 它是一个宏,他的本质还是GetWindow(),这里我们传入的参数变为GW_HWNDNEXT,这里我们指定找到他的下一个兄弟窗口,不能传 GW_CHILD,因为对应了上面的红字,其他窗口直接是没有特定关系
12. 控件基础
控件是一本分windows系统内置的窗口类,他们只能是某一个窗口的子窗口,所以创建他们的风格必须都是WS_CHILD风格
我们创建一个控件的时候,我们并没有去注册一个窗口类,因为系统以及帮我们注册好了,消息相应函数也不是我们写的,都由windows提供,即他们都有自己的窗口回调函数,(ps:当然我们也可以自己去改变这些空间本身的回调函数,setwindowlong() )
控件具体分为两种
一种是标准控件
窗口类名 | 控件 | 英文 |
"button" | 按钮 | Button |
复选框 | CheckBox | |
单选框 | RadioButton | |
"static" | 静态文本 | Static Text |
图片 | Picture Control | |
"combobox" | 复合框 | ComboBox |
"edit" | 编辑 | Edit |
"listbox" | 列表框 | ListBox |
"scrollbar" | 滚动条 | ScrollBar |
另一种是通用控件,通用控件较为复杂
窗口类名 | 控件 |
WC_LISTVIEW | 列表框控件 |
WC_TREEVIEW | 树控件 |
WC_TABCONTROL | Tab控件 |
HOTKEY_CLASS | 热键控件 |
控件的创建
我们也是使用CreateWindow函数,只是窗口类不需要我们去注册,但是我们要指定他们的风格,WS_CHILD与WS_VISIBLE风格
控件相关的消息
分为两种
一种是用于控制控件行为的控件控制消息
一种是用于通知父窗口用户行为的控件通知消息
简单的说明下这两种消息的含义:
看起来比较绕口,我们只需要明白他们的含义就好,
先来说说第一种控制控件行为的控件控制消息,一些控件除了会通知窗口消息之外,还有自己的专属消息,我们只需要向这些控件发送这些消息来控制他们的行为就行(比如按钮被点击按下,按钮被按下只有系统知道,我们看到了图标改变那是因为图标被重绘了),我们并不需要知道他们怎么被处理(按下按钮触发什么事件,我们并不用操心他们会触发什么事)
第二种消息,为控件通知消息,控件通知消息是子控件来通知父窗口一些事件,常见的有子控件被点击,子控件需要重绘,对应了上面所说的去理解其中的含义。
这里,控件通知消息分为两大类:
WM_COMMAND: 标准控件的通知消息,标准控件的通知消息比较简单
WM_NOTIFY: 附加通用控件通常会用此消息给父窗口发通知 (后面分析为什么要分为两大类)
13. Windows中资源 略
14. 对话框
引入对话框资源这一操作,可以使我们方便去控制窗口的各个控件位置,属性
两个对话框创建函数的API
一个是DiaologBox,一种是CreateDialog
DiaologBox是生成一个模态窗口,他不需要自己写消息循环
CreateDialog生成一个非模态对话框,他需要自己写消息循环
他们的参数都一样,说明他们又是宏,他们背后都是在调用CreateWindow
上面我们说了模态对话框和非模态对话框,这里我们来分析分析这两个概念以及区别
- 模态对话框创建后一定要在用户关闭对话框后, 才能对父窗口进行用户操作
- 非模态对话框创建之后,不需要等待窗口关闭,也可以对父窗口进行窗口操作
对话框和窗口的区别
乍一看对话框就是窗口,我们也可以说对话框是 "次一级的窗口",我们常见的对话框没有最大最小化按键,具体的视觉上区别就不一一阐述
窗口 | 对话框 | |
函数返回值 | 返回LRESULT(也就是返回LONG) | 返回BOOL |
消息处理 | 不处理WM_INITDLALOG | 不处理WM_CREAT,WM_DESTORY,WM_PAINT |
不处理消息如何处理 | 调用DefWindowProc处理程序不处理的消息 | 直接返回0 (return 0) |
那么对话框主要处理的消息有:
WM_INITDIALOG 对话框初始化时候操作
WM_COMMAND 响应对话框上的控件一些处理操作
模态对话框的显示创建
要调用DiaologBox,在对话框处理函数中(switch语句中)处理WM_INITDIALOG和WM_COMMAND。并且模态对话框的关闭要调用EndDiaolog关闭对话框
消息框是模态对话框的一种特殊形式,也就是我们常见的MessageBox函数生成的消息框,我们这里只看他的参数含义
MessageBox(拥有该消息的窗口句柄, 消息框中显示的字符串, 标题字符串, 指定消息框的内容 )
非模态对话框的显示创建
要调用CreateDialog完成创建
不要忘记我们还要自己去写消息循环,由于非模态对话框并不禁止应用程序向其他窗口发消息,因此,在WinMain函数的消息循环中必须包含截获发往非模态对话框的消息
具体代码如下
while(GetMessage(&msg,NULL,0,0))
{
if (! IsDiaologMessage(hdlg,&msg)) //如果是发往对话框的消息,取反 代表其他不属于对话框的消息,就进入循环,发给其他窗口
{ //这里也就说明为什么非模态对话框,父窗口可以相应用户操作
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
关闭对话框函数
DestroyWindow(); 关闭非模态对话框,退出消息循环,结束进程,但不等于退出运行
我们总结下关闭窗口或者对话框的函数
EndDiaolog(); 关闭模态对话框,调用函数中关闭对话框,关闭后会有一个返回值给父窗口
DestroyWindow(); 关闭非模态对话框,退出消息循环,结束进程,但不等于退出运行
PostQuitMessage(); 退出运行,关闭程序
15. 控件的使用
经过学习可以知道,控件的产生可以有两种方式,第一种是我们使用CreateWindow函数去创建处理 另一种就是用可视化编程创建出来
这里要说明一点,所有控件的ID只有一个,句柄是不定的
所以对于控件的使用,我们往往是根据ID找句柄操作他们,所以我们需要大量使用GetDigItem()函数来操作控件
GetDigItem(父窗口句柄,控件ID)
那么父窗口的句柄如何找到呢??
第一种: HWND hWnd = FindWindow(NULL, L"当前窗口的名字");
第二种,如果知道子窗口句柄,找父窗口句柄方法 GetParent()参数为子窗口句柄
控件消息
上面我们在分析消息的时候,以及提及了控件消息的相关信息,这里我们来深入的分析下控件消息
控件的本质也是窗口,既然是窗口那么他也有回调函数,我们在创建控件的时候并没有指定他的回调函数,那么控件的消息谁处理了呢?
他其实是发送到了父窗口中,所以我们应该在主窗口的回调函数里面去处理控件的消息,控件消息主要分为两种类型WM_COMMAND和WM_NOTIFY,我们上面说过。控件分为两种类型,一种是标准控件,一种是通用控件,所以消息的处理也是不同的
我们如果按下一个按钮,或者鼠标单击,那么WINDOWS将会发送一个WM_COMMAND消息给父窗口,我们这里就分析下WM_COMMAND消息参数
消息来源 | wParam(高16位) | wParam(低16位) | IParam |
菜单 | 0 | 菜单ID | 0 |
快捷键 | 1 | 快捷键ID | 0 |
控件 | 控件通知码 | 控件ID | 控件句柄 |
这里的控件通知码 如果说BN_CLICKED 区分具体是什么消息种类 我们在WM_COMMAND 中写switch语句,其中的case 后面就可写控件通知码
标准控件的分类
- 按钮类控件
BS_PUSHBUTTON(普通按钮)
BS_CHECKBOX(复选框)
BS_RADIO(单选框)
如何控制?
Button控件,只需要响应父窗口的WM_COMMAND消息,并且通过wParam知道按得是哪一个按钮发送的消息就可以
这里我们必须要用ID去控制按钮,因为句柄是随时可以改变的
<wiz_code_mirror>
case WM_COMMAND:
{//当消息是WM_COMMAND的时候,wParam的低16位是子控件ID
DWORD dwId = LOWORD(wParam);
switch (dwId)
{
case 1000:
MessageBox(0, TEXT("你干嘛"), TEXT("警告"), 0);
break;
case 1001:
MoveWindow(hWnd, 200, 200, 600, 500, TRUE);
break;
case 1002:
{
WCHAR WindowNamebuf[100] = {};
GetWindowText(hWnd, WindowNamebuf, 100);
wcscat_s(WindowNamebuf, 100, L"hehe");
SetWindowText(hWnd, WindowNamebuf);
}
CheckBox控件一般只需要对其控件窗口的句柄进行发消息操作就可以设置
这里说明下,通过句柄操作确实可以,但是每次句柄都会改变,这里的句柄会是变量,但是句柄的获得是靠ID得来的,用定量去获得变量,所以这里传入句柄是可以的
使其被选择上 | SendMessage(控件窗口句柄,BM_SETCHECK,1,0); |
使其取消选择 | SendMessage(控件窗口句柄,BM_SETCHECK,1,0); 再次发送点击消息即可 |
获取其状态 | SendMessage(控件窗口句柄,BM_SETCHECK,0,0); |
它和CheckBox差不多
这里添加一个函数,ComboBox_GetText(hWnd,szText,64); 获取选项内容
它和CheckBox的区别就是,一个选其他全不选,所以我们就要为这些按钮分组
分组的步骤,Ctrl+D,按顺序点你的Radio Box,在每一组的第一个按钮将属性group至为TRUE
- 文本框
获取文本框内容 | GetDlgItemText(hWnd,ID,buf); |
设置文本框内容 | SetDlgItemText(hWnd,ID,buf); |
- ComboBox
向下拉组合框添加一项 | ComboBox_AddString(hwnd,szBuff); |
返回当前选择的行号 | int dex = ComboBox_GetCursel(hwnd) |
删除一行 | ComboBox_DeleteString(hwnd_cbb,index); |
根据文本找到第几行 | int dex=ComboBox_FindString(hwnd,indexstart,szBuff); |
- Picture Control
首先,要有一个.bmp格式图片,并将其加载到资源当中
其次,调用LoadBitMap函数将其加载,并得到一个控制图片的句柄,句柄类型是HBITMAP
显示图片SendMessage(hPicCtrl,STM_SETIMAGE,IMAGE_BITMAP,(LPARAM)hBitMap);
通用控件分类
通用控件通知父窗口消息不再是WM_COMMAND而是WM_NOTIFY
创建通用控件的方法依然有两种,动态创建和资源拖拽
动态创建也就是我们用代码去实现,通常的步骤有:
1.包含<CommCtrl.h>头文件
2.载入ComCtrl32.lib
3.调用InitCommonControls初始化通用控件
4.使用CreateWindowsEX创建通用控件
下面我们对应于之前所说的,来分析分析为什么通用控件给主窗口回调函数发消息需要WM_NOTIFY 而不是WM_COMMAND
我们指定对于WM_COMMADN消息,其附加的消息是之前的表格所示
消息来源 | wParam(高16位) | wParam(低16位) | IParam |
菜单 | 0 | 菜单ID | 0 |
快捷键 | 1 | 快捷键ID | 0 |
控件 | 控件通知码 | 控件ID | 控件句柄 |
这里我们通过wParam来区分菜单,快捷键或者控件通知码,然而我们如果选中了List控件的某一行,我们发现我们需要知道我们选中的是哪一行,这下WM_COMMAND就不能满足我们的要求,于是,WM_NOTIFY就出现了
现在我们将所有的信息都存放到 NMHDR 结构体中,该结构体指针通过LPARAM通知到父窗口
typedef struct tagNMHDR
{
HWND hwndFrom; // 控件句柄.
UINT_PTR idFrom; // 控件 ID.
UINT code; // NM_ code.
} NMHDR;
但是我们需要知道ListView选中的行和列
typedef struct tagNMLISTVIEW{NMHDR hdr; // NMHDR.int iItem; // 行号.int iSubItem; // 列号.UINT uNewState;UINT uOldState;UINT uChanged;POINT ptAction;LPARAM lParam;} NMLISTVIEW, *LPNMLISTVIEW;它的第一个字段就是NMHDR
所以WM_NOTIFY消息的附加消息就如下所示
消息类型 | WPARAM | LPARAM |
WM_NOTIFY | 发生WM_NOTIFY消息控件的ID | NMHDR指针 |
通用控件的使用
- 进度条
设置进度 | SendMessage(hPosControl,PBM_SETPOS,数值,0); |
获取进度 | int nPos=SendMessage(hPosControl,PBM_GETPOS,0,0); |
- 滑块
滑块的具体API函数 P58页
- List Control
这里有两个宏,分别是添加行和列
ListView_InsertItem(hWnd,LVITEM); //行
ListView_InsertColumn(hWnd,nIndex,LPVCOLUMN); //列
还有一个给列表设置文本
ListView_SetItemText(hWnd,行号,列号,文本);
其本质都是SendMessage()
16. 控件消息的截获
控件的消息处理函数,系统以及帮我们定义好了,但是我们还是可以去修改原来的消息处理函数,这时候就要用到SetWindowLong来
LONG SetWindowLong (
HWND hwnd, //窗口句柄
int index, //索引值
LONG dwNewLong //新值
);
我们可以根据索引值来修改不同的属性,窗口ID,消息处理函数,风格,扩展风格,对话框消息处理函数,用户数据。。。