使用MC.EXE定义错误码

学着写点东西,过程比结论更让人印象深刻。


《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=(namenumber[: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;

}

你可能感兴趣的:(windows开发)