在《windows核心编程》中的1.2节发现了自己以前未曾使用过的工具MC.exe(message compiler,vs2010中已集成),经过查找资料得知此工具可以利用mc文件生成消息资源(包括.h文件,.rc资源文件以及bin文件)我们可以将此消息资源添加到自己创建的dll中,这样当在程序中调用此模块出现错误时我们就可以使用GetLastError获得错误代码了,当然也可以利用FormatMessage获得相关的错误消息文本。
在具体讲mc文件的编写和编译之前,有必要讲一下window函数的错误处理。我们在应用程序中调用windows函数时难免会出现各种错误(比如打开文件时路径下并没发现所指定的文件)等等,这时我们可以使用getlasterror()获得错误代码,当然由于windows中的错误代码太多了(win32中已经从0排到了11031),因此除了常见的错误代码我们几乎很难通过错误代码直接联想到具体的错误信息,这时我们可以使用FormatMessage()函数获取错误文本信息。
我们可以在winerror.h中找到错误代码,当然我们也可以创建自己的错误代码,但需要按照错误代码的标准格式来定义错误代码,错误代码是一个32位的数字,下表描述了其中的各个字段:
位 | 31-30 | 29 | 28 | 27-16 | 15-0 |
内容 | 严重性 | ms/客户 | reserved | facility代码 | 异常代码 |
含义 | 0=成功 1=信息提示 2=警告 3=错误 | 0=ms定义的错误代码 1=客户定义的错误代码 | 必须为0 | 钱256个由ms保留 | ms/客户定义的代码 |
注:表中ms是指microsoft
那么对于我们自己编写的模块(如dll),怎么实现此功能呢,我们如何定义自己的错误代码呢,这时mc的功能就体现出来了。
关于mc文件的格式,大家可以去网上找,此文件中的内容主要包括:
1、注释:以分号开头,然后后面接注释文字,为了在生成的*.h文件中也以注释状态出现,勿必在分号后面加上一个//。同样,对多行注释:每行都以分号开头,但是为了同样的目的,建议注释的开始在分号后面加上/*,结束时在分号后加上*/
2、头:一个消息文本文件包含一个头,头部定义一些名字标识以及语言标识供后面消息定义使用它在定义名字和语言识别。一个头包括0个或多个下面的声明:
MessageIdTypedef=type----用于消息标识定义的类型 注:在*.h文件中可看到此类定义。
SeverityNames=(name=number[:name])----设置消息码中第31、30位,即Severity部分。
FacilityNames=(name=number[:name])----设置消息码中27--16位,即Facility部分,
LanguageNames=(name=number:filename)----设置消息所用语言标识,可设置多个语言版本。
OutputBase=number----生成消息常量数值格式,比如指定16,生成16进制的数,指定10,生成10进制的数等。
3、消息主体
消息主体包含以下0个或多个声明,其中MessageId标识一个消息定义的开始,必须存在,Severity和Facility声明是可选的。
MessageId=[number|+number]----消息序号标识,这项必须要有,但是值是可选的。如果没有指定值,此值等于上一个Facility加上1,如果在指定的值前面带上了一个+号,则用上一个Facility加上此值来生成MessageId。
Severity=name
Facility=Application。----在头部声明FacilityNames指定的一个名字,这个声明是可选的。如果没有指定值,则使用在消息定义块中最后指定的那个值。
SymbolicName=name----一个标识符,在*.h文件中可看到,如#define name ((type)0xnnnnnnnn)。
OutpubBase={number}----生成消息常量数值格式,比如指定16,生成16进制的数,指定10,生成10进制的数等。
Language=name----在头部声明LanguageNames指定的一个名字,此项是可选项,如果没有指定值,则使用在消息定义块中最后指定的那个值。
Message Text----消息文本。
.(点号) ----消息定义终止符,注:此终止符为英文输入模式下输入,否则使用mc工具进行编译时会提示无终止符的。
(此部分详细内容可参见 https://blog.csdn.net/s634772208/article/details/46402677?utm_source=copy)
mc文件中的内容也借鉴了文章中的内容,如下:
;//***** Sample.mc *****
;//This is the header section.
MessageIdTypedef=DWORD
SeverityNames=(Success=0x0:STATUS_SEVERITY_SUCCESS
Informational=0x1:STATUS_SEVERITY_INFORMATIONAL
Warning=0x2:STATUS_SEVERITY_WARNING
Error=0x3:STATUS_SEVERITY_ERROR
)
FacilityNames=(System=0x0:FACILITY_SYSTEM
Runtime=0x2:FACILITY_RUNTIME
Stubs=0x3:FACILITY_STUBS
Io=0x4:FACILITY_IO_ERROR_CODE
)
LanguageNames=(English=0x409:MSG00409)
LanguageNames=(Chinese=0x804:MSG00804)
; // The following are message definitions.
MessageId=0x1
Severity=Error
Facility=Runtime
SymbolicName=MSG_BAD_COMMAND
Language=English
You have chosen an incorrect command.
.
Language=Chinese
你选择了一个不正常的命令。
.
MessageId=0x2
Severity=Warning
Facility=Io
SymbolicName=MSG_BAD_PARM1
Language=English
Cannot reconnect to the server.
.
Language=Chinese
无法连接服务器。
.
MessageId=0x3
Severity=Success
Facility=System
SymbolicName=MSG_STRIKE_ANY_KEY
Language=English
Press any key to continue . . . %0
.
Language=Chinese
按任意键继续...
.
MessageId=0x4
Severity=Error
Facility=System
SymbolicName=MSG_CMD_DELETE
Language=English
File %1 contains %2 which is in error
.
Language=Chinese
文件 %1 包含 %2 r损坏。
.
MessageId=0x5
Severity=Informational
Facility=System
SymbolicName=MSG_RETRYS
Language=English
There have been %1!d! attempts with %2!d!%% success%! Disconnect from the server and try again later.
.
Language=Chinese
未知错误!无法连接服务器,请稍后重试!
.
将上述内容在记事本中编辑后改名为mcfile.mc(注意将扩展名显示出来并修改)即可,这样我们就有了mc文件。
然后打开vs2010命令行界面,定位到mc文件所在目录,然后使用mc命令对其进行编译:
此时可以发现在原来mc文件的目录下多了.h,.rc文件以及两个bin文件(分别对应中文和英文):
下面创建dll项目,把消息资源文件添加到dll中:
在vs2010下新建win32项目,在应用程序类型中选择dll,单击“完成”,然后将.h文件和.rc文件添加到项目中(在解决方案管理器中添加即可),注意同时还要添加bin文件(将bin文件拷贝至项目文件目录下即可),build项目,生成dll。至此我们已经生成了包含消息资源文件的dll。下面继续建立项目以测试此dll是否可用。
建立win32控制台项目,源码如下:
#include "stdafx.h"
#include <iostream>
using namespace std;
#ifdef _UNICODE
#define COUT wcout
#else
#define COUT cout
#endif
int _tmain(int argc, _TCHAR* argv[])
{
DWORD dwError = 0;
DWORD dwLanguageId = MAKELANGID( LANG_NEUTRAL, SUBLANG_NEUTRAL );
//解决问题:wcout输出时显示不了中文
COUT.imbue( std::locale( "chs" ) );
HLOCAL lpMsgTextBuf = NULL;
COUT << TEXT("输入一个自定义的错误代码(如:3221356545(0xC0020001) ):");
while( cin >> dwError )
{
lpMsgTextBuf = NULL;
HMODULE ghResDll = LoadLibrary( TEXT("mcdll.dll") );
if ( NULL == ghResDll )
{
COUT << TEXT("加载消息模块失败!") << endl;
return -1;
}
BOOL bOk = FormatMessage( FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_ALLOCATE_BUFFER,
ghResDll, dwError, dwLanguageId, (LPTSTR)&lpMsgTextBuf, 0, NULL );
if ( !bOk )
{
COUT << TEXT("error ") << GetLastError() << endl;
}
if ( NULL != lpMsgTextBuf )
{
cout << (LPTSTR)lpMsgTextBuf << endl;
LocalFree( lpMsgTextBuf );
}
COUT << TEXT("输入一个自定义的错误代码(如:3221356545(0xC0020001) ):");
}
return 0;
}
build项目生成exe,注意要将生成的dll拷贝至debug目录下。可以输入mcfile.h中定义的错误代码,可以看到屏幕上会显示相应的错误文本信息。说明dll模块成功实现了添加消息资源功能,可以查看错误代码及错误信息。
-----------------------------------------------------------------------------over------------------------------------------------------------------------------------------