学着写点东西,过程比结论更让人印象深刻。
《Windows核心编程》第一章最后提到:我们可以向自己的模块添加错误代码,说用MC.EXE工具。用Everything搜索了下,在我这是:“C:\Program Files (x86)\Microsoft SDKs\Windows\v7.0A\bin\MC.Exe”。
忽然想到,公司里基本都是bool的错误判断,就觉得确实需要这样一种方法,因为它能够让开发这样写:
bool Func() {std::ifstream ifs("D:\\xx.csv");
if(! ifs) {
SetLastError(MSG_CANNOT_OPEN_FILE);
return false;
}
//...
}
调用GetLastError()配合FormatMessage((),就能显示你定义好的错误描述了,比如:“无法打开文件xxx,该文件可能被占用”。
然后我就搜索这方面的blog,自己摸索着写,现在记录下这个过程。
1,用nodpad++新建一个文档,保存为test.mc。
2,这个mc文件的语法,网上有很多bolg有写,但大多是翻译了MSDN的内容,所以你可以直接去MSDN看。打开MSDN(我用的是本地MSDN),搜索SeverityNames,Message Text File那个就是了。
如果你看过《windows核心编程》几遍了,那应该知道错误码的格式,还有
我照网上定义了一个mc文件,大致是这样:
;//文件开始。 单行注释用 ;//
MessageIdTypedef=DWORD
SeverityNames=( ;//错误码第31-30位
Success=0x0:STATUS_SEVERITY_SUCCESS ;//语法:SeverityNames=(name= number[:name]),第一个name是在本文件用的别名,下面类似
Informational=0x1:STATUS_SEVERITY_INFORMATIONAL
Warning=0x2:STATUS_SEVERITY_WARNING
Error=0x3:STATUS_SEVERITY_ERROR
)
FacilityNames=(;//错误码第27-16位
System=0x0:FACILITY_SYSTEM
Runtime=0x2:FACILITY_RUNTIME
Stubs=0x3:FACILITY_STUBS
Io=0x4:FACILITY_IO_ERROR_CODE
)
;//语言,先在代码里用DWORD dwLangId = MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US);调试看是多少,转16进制。
;// dwLangId = MAKELANGID(LANG_CHINESE, SUBLANG_CHINESE_SIMPLIFIED); 中文
LanguageNames=(English=0x409:MSG00409)
LanguageNames=(Chinese=0x804:MSG00804)
;//下面是消息定义
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=Error
Facility=System
SymbolicName=MSG_CMD_DELETE
Language=English
File %1 contains %2 which is in error
. ;//这里有个点
Language=Chinese
文件 %1 包含 %2 已损坏。
. ;//这里有个点
3,我把它保存在桌面上了,在桌面按住shift,右键,打开命令行,输入:mc.exe -a -A test.mc
提示无法解析的命令,因为系统找不到mc.exe这个东西,找到你的mc.exe路径,把它拷贝到环境变量的path下即可。
然后再执行,它生成了test.h, test.rc, MSG00409.bin, MSG00804.bin这几个文件。
打开.h文件,发现它和
很像,但是我的汉字都是乱码,这是编码问题,用nodpad++打开test.mc,选 格式-编码字符集-中文-gb2312(下次一开始就这样),然后再重写你的汉字部分,再去生成,如果没有汉字最好办了。
打开test.h,有这样一条 #define MSG_BAD_COMMAND ((DWORD)0xC0020001L)
在calc.exe中看它的二进制是:1100 0000 0000 0010 000000000000 0001
我一开始就有两个疑问:1,第29位(Microsoft定义/用户定义)为什么不设置,难道不冲突? 2,第27-16位一共可以设置4096个值,前256个值是微软的,但例子上写着Runtime=0x2:FACILITY_RUNTIME,这个不冲突吗?
后来我把0xC0020001L在
中搜索了一下,我靠!没找到!第一个疑问是真不冲突,原因是:微软也没有用完所有定义,这个碰巧是没定义的值。 第二个疑问可能在下面解决(因为我不知道算不算解决了)。
4,建了一个控制台程序,把test.h和test.rc加入工程中,然后编写代码:
HANDLE GetHandle_Test(TCHAR* tch) {
if(! tch) {
//在winerror.h中并没有找到0xC0020001L错误码,所以逃过冲突;
SetLastError(MSG_BAD_COMMAND);
return nullptr;
}
HANDLE hFile;
return hFile;
}
#include
int _tmain()
{
DWORD dwLangId = MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US);
DWORD errId;
HLOCAL szErr = nullptr;
HANDLE h = GetHandle_Test(nullptr);
if(nullptr == h) {
errId = GetLastError();
BOOL bOk = FormatMessage(FORMAT_MESSAGE_FROM_HMODULE |
FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_ALLOCATE_BUFFER,
nullptr, errId, dwLangId, (PTSTR)&szErr, 0, nullptr);
cout << "hello\n";
}
//std::wstring str((PTSTR)szErr);
cout << "ok\n" ;
return 0;
}
这个程序bOk一直是0(失败),原来是我传了FORMAT_MESSAGE_FROM_HMODULE标识,但是hMoudel参数又传了nullptr。于是我加了:HANDLE hModule = GetModuleHandle(nullptr); 然后把第一个nullptr换成hModule,终于返回39了!虽然我不知道这是什么意思。然后把注释的那句打开,真的是MSG_BAD_COMMAND对应的那句话:“You have chosen an incorrect command.”。
《windows核心编程》代码是这样的:
bOk = //上面写的;
if(! bOk) {
HMODULE hDll = LoadLibraryEx(_T("netmsg.dll"), NULL, DONT_RESOLVE_DLL_REFERENCES);
if(hDll != NULL) {
bOk = FormatMessage(//....);
FreeLibrary(hDll);
}
}
再加上《核心》第一章最后几句:每个DLL或.exe都可以有自己的一套错误代码——看来是可以重复的!也就是我可以像上面这样,加载一个消息资源dll(里面都是错误码),在里面找,就算和系统冲突了,但我就只找这里面的。所以第二个疑问我就算解惑了。
5,在实际工程中使用
建立一个dll工程,把.h和.rc文件拖进去,再建一个cpp文件,写一句代码#include "test.h"(我的*.mc文件生成的),然后就导出了dll。
现在,我的程序变成这样了:
#include "test.h"
#include
HMODULE g_hmsgdll;
HANDLE GetHandle_Test(TCHAR* tch) {
if(! tch) {
//在winerror.h中并没有找到0xC0020001L错误码,所以逃过冲突;
SetLastError(MSG_BAD_COMMAND);
return nullptr;
}
HANDLE hFile;
return hFile;
}
//函数封装;
void PrintWhatWrong() {
DWORD errId = GetLastError();
DWORD dwLangId = MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US);
HLOCAL szErr = nullptr;
BOOL bOk = FormatMessage(FORMAT_MESSAGE_FROM_HMODULE |
FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_ALLOCATE_BUFFER,
g_hmsgdll, errId, dwLangId, (PTSTR)&szErr, 0, nullptr);
//log4j,输出窗口,控制台等;
}
int _tmain()
{
//每个dll都可以有一份错误代码,但这些错误代码放在一个dll里也不过分吧;
//其实是过分的:因为在svn的协同工作中,只要这个dll发生变化,大家都要更新;如果分模块,就只需更新自己相关的了;
g_hmsgdll = LoadLibraryEx(_T("testmsg.dll.dll"), nullptr,
DONT_RESOLVE_DLL_REFERENCES | LOAD_LIBRARY_AS_DATAFILE);
if(!g_hmsgdll) {
return(-1);
}
HANDLE h = GetHandle_Test(nullptr);
if(nullptr == h) {
PrintWhatWrong();//现在,有异常就可以这样做了;
}
cout << "ok\n" ;
return 0;
}