使用mc.exe建立訊息資源並將其新增在自己的dll中(附帶測試程式)
在《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------------------------------------------------------------------------------------------