windowsx.h 和windows.h的区别

 我来答
huanglenzhi
2014-12-21 · 知道合伙人数码行家
huanglenzhi
知道合伙人数码行家
采纳数:117538 获赞数:517196
长期从事计算机组装,维护,网络组建及管理。对计算机硬件、操作系统安装、典型网络设备具有详细认知。

向TA提问 私信TA
展开全部
很多初中级程序员用C/C++编写WindwosAPI的程序时,经常面对面条式的switch...case语句当你在Window过程(回调函数、下称过程)中加入大量诸如WM_COMMAND or WM_CHAR的消息捕获时。真是一场噩梦。
关于上千行代码的Window过程的问题,随着 C/C++ 7.0 编译器和Windows SDK for
Windows 3.1发行时带的一个头文件而被解决。这个头文件是<windowsx.h>
以及所包含的大量的有用的宏。按照微软的说法:这些头文件所带来的便利可重复用于下面这些地方(Groups)
:.在C程序中使用STRICT宏进行严格的类型检查。
.在windows程序中用宏简化公共性操作。
.使用控件宏同windows控件进行通讯。
.windows环境下的消息解析器(message
crackers)(是一个方便的、可移植的、类型安全的处理消息的方法)以及和他相关的参数和返回值。
因为消息解析器向导是用于消息解析器的,其他由这头文件带来的一些有用的宏,我就跳过不讲了

让我们说下消息解析器的优点,当然也包括为什么这里提供的这个工具是如此有用。
当你使用W32SDK编程,用windows过程(通常叫做WndProc)处理窗口和对话框消息时,使用swich-case来捕获你需要处理的消息是非常普遍的做法。假设你想处理WM_COMMAND,
WM_KEYUP, WM_CLOSE and WM_DESTROY消息,你是这样作的:

LRESULT CALLBACK MainWndProc (HWND hwnd, UINT msg,
WPARAM wParam, LPARAM
lParam)
{
switch(msg)
{
case WM_COMMAND:
// ...
break;

case WM_KEYUP:
// ...
break;

case WM_CLOSE:
// ...
break;

case WM_DESTROY:
//...
break;

default:
return DefWindowProc(hwnd, msg, wParam,
lParam);
}
}

这是自从Windows1.0诞生以来处理消息最常见的风格了。而且很肯定,它工作得很好。
但问题是,当你加入一个或多个复杂的特色到你的程序中时,如MDI,OLE
公共控件等等,结果形成了一个上千行的Window过程,你开始用PageDn和PageUp来查找你想要修改的消息处理代码了。

消息解析器的第一个好处就是:他把面条式的case标签转换成类似MFC中易于维护和处理的函数。第二个好处是:处理函数中合适的参数。你可以简单使用switch(id)代替原先的switch(LOWORD(wparam)),因为消息解析器传递给你的是"已解析"的参数,他等价于LOWORD(wparam)。

HANDLE_MSG 这个消息处理宏在windowx.h中的定义如下:
#define HANDLE_MSG(hwnd, message, fn)
\
case (message) : return HANDLE_##message((hwnd), (wParam), (lParam),
(fn))
你想要把你的代码做成"消息解析"版的,你需要提供一个解析宏HANDLE_MSG及其函数来处理你的消息.在window过程里HANDLE_MSG宏需要三个参数:窗口句柄(hwnd),消息(WM_XXXX), 处理你消息的函数(function)。
为更好地解释这个:看下面,我们把上面那段代码转换成了下面的代码:

LRESULT CALLBACK MainWndProc (HWND hwnd, UINT msg, WPARAM wParam, LPARAM
lParam)
{
switch(msg)
{
HANDLE_MSG (hwnd, WM_COMMAND,
OnCommand);
HANDLE_MSG (hwnd, WM_KEYUP, OnKeyup);
HANDLE_MSG
(hwnd, WM_CLOSE, OnClose);
HANDLE_MSG (hwnd, WM_DESTROY,
OnDestroy);
default:
return DefWindowProc(hwnd, msg, wParam,
lParam);
}
}

哇,太好了,紧凑且易于管理的window过程。现在你可以去定义你的消息处理函数了(OnKeyUp, OnClose, and
OnDestroy)
还有一个真正的好处,你可以在visual studio IDE 环境中直接跳转到消息处理函数。

有个问题是:当你每加入一个消息处理,你必须在windowx.h中查找相关参数的定义。

因为消息处理参数的格式是明确的而不能由你随心所欲。但在头文件中进行重复搜索是乏味且易出错的。

消息解析向导工具用于解决这个:他允许你粘贴你需要的函数参数,
而你如果只是打草稿,他也会在在你消息处理中写上一个模板化的window或对话框过程
(??这句有疑问)消息前驱宏(message fowarding): WINDOWSX.H 的另一个特色
(消息前向?)
WINDOWSX.H另一个特色是消息前驱的可能性,它是用于"解压"消息处理参数到其他函数调用(如PostMessage,
SendMessage,
CallWindowProc等)所需要的合适的WPARAM和LPARAM值。
假设我们想用SendMessage发送一个WM_COMMAND消息到父窗口,"模拟"一个在名为IDC_USERCTL控件上的双击(通过发送BN_DBLCLK的通知码)
我们通常这么做的:

SendMessage (hwndParent, WM_COMMAND,
MAKEWPARAM(IDC_USERCTL, BN_DBLCLK),
// WPARAM 的低16位是控件ID,高16位是通知码
(LPARAM)GetDlgItem(hwnd, ID_USERCTL));
//LPARAM
为控件句柄
这是相当复杂的语法。SendMessage希望WPARAM参数的低字是控件ID而高字是通知码,LPARAM参数是控件句柄,句柄我们通过GetDlgItem这个API函数得到。
上述代码可以被转换为WINDOWSX.H的消息前驱宏,FORWARD_WM_xxxxx。
对于每个消息,消息前向宏用同样的方式"打包"消息解析向导创建的函数参数,
并且传递给你的处理函数"已解压"的参数(LPARAM/WPARAMs)。
例如:对于一个myWnd窗口的WM_COMMAND消息,消息解析器向导将生成如下的函数原形:

void myWnd_OnCommand (HWND hwnd, int id, HWND hwndCtl, UINT
codeNotify)

那么,这些解析的参数也同样用于消息前驱宏,这样上面那使人混乱的SendMessage调用可以简化为:

FORWARD_WM_COMMAND (hwndParent, IDC_USERCTL,
GetDlgItem(hwnd,
ID_USERCTL), BN_DBLCLK, SendMessage);

使用所有这些消息解析器支持的消息简单可行。(第一部分完)

在使用消息分流器来处理一个消息之前,应该打开Wi n d o w s X . h文件并搜索要处理的消息。例如,如果搜索W M _ C O M M A N
D,将会找到文件中包含下面代码行的部分:

/* void Cls_OnCommand(HWND hwnd, int id, HWND hwndCtl, UINT codeNotify)
*/
#define HANDLE_WM_COMMAND(hwnd, wParam, lParam, fn) \
((fn)((hwnd),
(int)(LOWORD(wParam)), (HWND)(lParam), (UINT)HIWORD(wParam)), 0L)
#define
FORWARD_WM_COMMAND(hwnd, id, hwndCtl, codeNotify, fn) \

(void)(fn)((hwnd), WM_COMMAND, MAKEWPARAM((UINT)(id),(UINT)(codeNotify)),
(LPARAM)(HWND)(hwndCtl))
第一行是注释行,展示要编写的函数原型。下一行是H A N D L E _ W M _
*宏,我们已经讨论过。最后一行是消息转发器( f o r w a r d e r)。假定在你处理W M _ C O M M A N
D消息时,你想调用默认的窗口过程,并让它为你做事。这个函数应该是这个样子:

void Cls_OnCommand(HWND hwnd, int id, HWND hwndCtl, UINT codeNotify)
{

//Do some normal processing.

//Do default processing.

FORWARD_WM_COMMAND(hwnd, id, hwndCtl, codeNotify, DefWindowProc);

}

F O RWA R D _ W M _ *宏将分流开的消息参数重新构造成等价的w P a r a m和l P a r a
m。然后这个宏再调用你提供的函数。在上面的例子中,宏调用D e f Wi n d o w P r o c函数,但你可以简单地使用S e n d M e s s
a g e或P o s t M e s s a g e。实际上,如果你想发送(或登记)一个消息到系统中的任何窗口,可以使用一个F O RWA R D _ W M
_ *宏来帮助合并各个参数。

应注意Windows.h和Windowsx的不同之处.

2.tchar.h 头文件
3.CmnHdr.h 头文件

它包含宏及链接程序指令.

读者要想建立本书的示例程序,必须要对编译程序和链接程序的开关选项进行设置,笔者已经将设置方面的细节放在了CmmHdr.h头文件中了

因为无法将所有的设置都放在这个头文件里,我们对每个示例程序的项目设置做了一些改变。对每个项目,我们显示Project
Settings对话框,然后做下面所说的改变。
? 在G e n e r a l栏,设定Output Files目录,这样所有最终的. e x e和.
d l l文件都在一个目录之下。
? 在C / C + +栏,选择Code Generation 条目,并对Use Run-Time
Library 字段选择Multithreaded DLL。

注意要对每个项目的D e b u g建立和R e l e a s e建立都做上述两个改变。

所有的示例程序都要包含C m m H d r.
h头文件,并且要在其他头文件之前包含.这是因为CmnHdr.h头文件中有一些链接程序指令,比如#define _WIN32_WINNT 0x0500
(它是Windows版本建立选项)的定义必须放在Windows.h之前,才能调用了Microsoft Windows
2000中提供的新函数.否则编译程序将产生错误。微软用_ W I N 3 2 _ W I N N
T符号来保护这些函数,以使程序员开发的应用程序能够运行在Windows 98及Windows NT的多个版本上。

Unicode建立选项.

笔者编写的所有这些示例程序既可按A N S I来编译,也可按U n i c o d e来编译。当针对x 8 6 C PU体系结构来编译这些程序时, A N S I为默认选择,这样程序可以在Windows 98上执行。但对其他C P U体系结构建立程序就要用U n i c o de,这样程序可以占用较少的内存,并且执行得更快。

为了对x 8 6体系结构建立U n i c o d e版本,只需将定义U N I C O D E的那一行代码的注释符去掉,并重建程序。通过在CmnHdr. h定义U N I C O D E宏,可以很容易地控制如何建立示例程序。关于U n i c o d e的详细内容,可参见第2章。

窗口定义和第四级警告
本节我确保警告级设定为3,而且C m nH d r. h包含标准的Wi n d o w s . h头文件。当包含了Wi n do w s . h时,在我编译其余代码时就设置第4级警告。在第4级警告上,编译程序对那些我不认为有问题的内容发出“警告”,这样我通过使用#pragma
warning指令显式地告诉编译程序忽略某些良性的警告错。

Pragma消息帮助宏
使用chMsg宏,例如#pragma chMSG(Fix this
later),这个宏让编译程序输出源代码文件的名字,以及p r a g m a出现的行号。使用Microsoft Visual DeveloperStudio,在输出窗口上双击这一行,将会自动定位到相应文件的确切位置上。还有一个方便之处, c h M S G宏不要求对文本串使用引号。.

chINRANGE和chDIMOF宏chINRANGE宏用来查看一个数值是否在另外两个数值之间.
chDIMOF只是返回一个数组中元素的数目,这个宏是用s i z e of操作符先计算整个数组的字节数,然后再用这个数除以数组中一个数据项所占的字节数,从而得出结果。

chBEGINTHREADEX宏
多线程示例程序都使用了微软的C/C + +运行时函数库中的_ b e g i n t h r e a d e
x函数,而不是操作系统的C r e a t e T h r e a d函数。我使用这个函数是因为_ b e g i n t h r e a d e
x函数为新线程做好了准备,使新线程能够使用C / C + +运行时函数库中的函数,而且还因为它保证在线程返回时清除每个线程的C / C +
+运行时库信息.
尽管_ b e g i n t h r e a d e x函数用的参数值同C r e a t e T h r e a
d函数用的参数值是一样的,但二者的参数的数据类型都不相匹配。
为了避免编译程序警告,我在C m n H d r. h中定义了一个c h B E G I N
T H RE A D E X宏,替我执行所有这些转换:

chMB宏c h M B宏只是显示一个消息框。消息框的标题是调用进程可执行代码的全路径名

chASSERT和chVERIFY宏在我开发这些示例程序时,为了查找潜在的问题,我在整个代码中多处使用c h A S S ERT宏。这个宏测试由x所标识的表达式是否为T R U E,如果不是,则显示一个消息框指出失败的文件、行和表达式。在程序的发行建立中,这个宏什么也不做。c h V
E R I F Y宏与c h A S S E RT宏差不多,区别在于不论是调试建立(debug build)还是发行建立(release build),c hV E R I F Y都要对表达式进行测试。

chHANDLE_DLGMSG宏

当你通过对话框使用消息分流器时,不应该使用微软的Wi n d o w s X . h 头文件中的H A N D L E _ M SG宏,因为这个宏并不能返回T R U E或FA L S E来指出消息是否由对话框的过程来处理。我定义的c h H A N D L E _ D L G M SG宏会通知窗口消息的返回值,适当地处理返回值,以便在一个对话框过程中使用。

chSETDLGICONS宏

由于多数示例程序使用一个对话框作为主窗口,你必须手工改变对话框图标,以便让它正确地显示在Ta s k b ar(任务条)、任务切换窗口和程序本身的标题上。当对话框接收到一个W M _ I N I T D I A L O G消息时,总要调用c h S E T D L GI C O N S宏,以正确设置图标。

OS版本检查内联函数

本书的大多数示例程序可运行在所有平台上,但也有一些程序要求一些Windows 95和Windows 98所不支持的特性,有些程序要求一些只在Windows2000中提供的特性。每个程序在初始化时要检查宿主系统的版本,如果要求更适用的操作系统时,就显示一个通知。

对那些不能在Windows 95和Windows 98上运行的程序,你会看到,在程序的_ t Wi n M a i n函数中有一个对Wi n d o ws 9 x N o t A l l o w e d函数的调用。对于要求Windows 2000的示例程序,你会看到在程序的_ t Wi n M a in函中有一个对c h Wi n d o w s 2 0 0 0 R e q u i r e d函数的调用。

确认宿主系统是否支持Unicode有一种办法能够知道我的程序是对U n i c o d e建立的,但可能在Windows98系统上运行。所以我建立了一个CUnicodeSupported C++类。这个类的构造函数只是检查宿主系统是不是对U n i c o de有良好的支持,如果不是,就显示一个消息框,并且进程结束。

读者会看到在C m n H d r. h中,我建立了这个类的一个全局的静态实例。当我的程序启动时,C / C ++运行时库启动代码调用这个对象的构造函数。如果这个构造函数检测到操作系统完全支持U n i c o de,构造函数返回而程序继续执行。通过建立这个类的全局实例,我不需要在每个示例程序的源代码模块中再增加特殊的代码。对于非U n i c o de的程序建立,不需要声明或实例化上述的C + +类。让程序只管运行就是。

强制链接程序寻找(w)WinMain进入点函数
我在C m n H d r. h中加入了一个p r a g m a,强制链接程序去寻找( w )
Wi n M a i n进入点函数,即使是用Visual C++建立了一个Win32 ConsoleA p p l i c a t i o n项目.
本回答被提问者和网友采纳
已赞过 已踩过<
你对这个回答的评价是?
评论 收起
收起 1条折叠回答
推荐律师服务: 若未解决您的问题,请您详细描述您的问题,通过百度律临进行免费专业咨询

为你推荐:

下载百度知道APP,抢鲜体验
使用百度知道APP,立即抢鲜体验。你的手机镜头里或许有别人想知道的答案。
扫描二维码下载
×

类别

我们会通过消息、邮箱等方式尽快将举报结果通知您。

说明

0/200

提交
取消

辅 助

模 式