先安装Visual C++,再安PlatForm SDK,安装完之后点Visual Studio Registration.
Registration之后,就可以编译源程序了,
自己编译时,注意它的源代码的后缀名为".cpp",不是".c".否则会产生很多错误.
关于第一章中的一些问题,
windowsx.h头文件:(全部都是网上查的)
http://www.codeproject.com/win32/msgcrackwizard.asp
第一部分:
介绍:
WINDOWSX.H 头文件为W32SDK的程序员提供方便(工具?)
很多初中级程序员用C/C++编写Windwos API的程序时,经常面对面条式的switch...case语句块
当你在Window过程(回调函数、下称过程)中加入大量诸如WM_COMMAND or WM_CHAR的消息捕获时。真是一场噩梦。
关于上千行代码的Window过程的问题,随着 C/C++ 7.0 编译器和Windows SDK for Windows 3.1发行时带的一个头文件而被解决。
这个头文件是
:
.在C程序中使用STRICT宏进行严格的类型检查。
.在windows程序中用宏简化公共性操作。
.使用控件宏同windows控件进行通讯。
.windows环境下的消息解析器(message crackers)(是一个方便的、可移植的、类型安全的处理消息的方法)以及和他相关的参数和返回值。
因为消息解析器向导是用于消息解析器的,其他由这头文件带来的一些有用的宏,我就跳过不讲了,如果你想看看关于
WINDOWSX.H的简要介绍,可以看 MS Knowledge Base Article #83456.
(http://support.microsoft.com/default.aspx?scid=http://support.microsoft.com:80/support/kb/articles/q83/4/56.asp)
好,让我们说下消息解析器的优点,当然也包括为什么这里提供的这个工具是如此有用。
当你使用W32 SDK编程,用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 头文件
http://msdn2.microsoft.com/zh-cn/library/c426s321(VS.80).aspx
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 P U体系结构来编译这些程序时, A N S I为默认选择,这样程序可以在Windows 98上执行。但对其他C P U体系结构建立程序就要用U n i c o d e,这样程序可以占用较少的内存,并且执行得更快。
为了对x 8 6体系结构建立U n i c o d e版本,只需将定义U N I C O D E的那一行代码的注释符去掉,并重建程序。通过在CmnHd r. 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 d o w s . h时,在我编译其余代码时就设置第4级警告。在第4级警告上,编译程序对那些我不认为有问题的内容发出“警告”,这样我通过使用#pragma warning指令显式地告诉编译程序忽略某些良性的警告错。
Pragma消息帮助宏
使用chMsg宏,例如#pragma chMSG(Fix this later),这个宏让编译程序输出源代码文件的名字,以及p r a g m a出现的行号。使用Microsoft Visual Developer Studio,在输出窗口上双击这一行,将会自动定位到相应文件的确切位置上。还有一个方便之处, c h M S G宏不要求对文本串使用引号。.
chINRANGE和chDIMOF宏
chINRANGE 宏用来查看一个数值是否在另外两个数值之间.
chDIMOF只是返回一个数组中元素的数目,这个宏是用s i z e o f操作符先计算整个数组的字节数,然后再用这个数除以数组中一个数据项所占的字节数,从而得出结果。
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 E RT宏。这个宏测试由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 h V E R I F Y都要对表达式进行测试。
chHANDLE_DLGMSG宏
当你通过对话框使用消息分流器时,不应该使用微软的Wi n d o w s X . h 头文件中的H A N D L E _ M S G宏,因为这个宏并不能返回T R U E或FA L S E来指出消息是否由对话框的过程来处理。我定义的c h H A N D L E _ D L G M S G宏会通知窗口消息的返回值,适当地处理返回值,以便在一个对话框过程中使用。
chSETDLGICONS宏
由于多数示例程序使用一个对话框作为主窗口,你必须手工改变对话框图标,以便让它正确地显示在Ta s k b a r(任务条)、任务切换窗口和程序本身的标题上。当对话框接收到一个W M _ I N I T D I A L O G消息时,总要调用c h S E T D L G I C O N S宏,以正确设置图标。
OS版本检查内联函数
本书的大多数示例程序可运行在所有平台上,但也有一些程序要求一些Windows 95和Windows 98所不支持的特性,有些程序要求一些只在Windows 2000中提供的特性。每个程序在初始化时要检查宿主系统的版本,如果要求更适用的操作系统时,就显示一个通知。
对那些不能在Windows 95和Windows 98上运行的程序,你会看到,在程序的_ t Wi n M a i n函数中有一个对Wi n d o w s 9 x N o t A l l o w e d函数的调用。对于要求Windows 2000的示例程序,你会看到在程序的_ t Wi n M a i n函中有一个对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建立的,但可能在Windows 98系统上运行。所以我建立了一个CUnicodeSupported C++类。这个类的构造函数只是检查宿主系统是不是对U n i c o d e有良好的支持,如果不是,就显示一个消息框,并且进程结束。
读者会看到在C m n H d r. h中,我建立了这个类的一个全局的静态实例。当我的程序启动时,C / C + +运行时库启动代码调用这个对象的构造函数。如果这个构造函数检测到操作系统完全支持U n i c o d e,构造函数返回而程序继续执行。通过建立这个类的全局实例,我不需要在每个示例程序的源代码模块中再增加特殊的代码。对于非U n i c o d e的程序建立,不需要声明或实例化上述的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项目.