2015-03-02 33 views
0

我不確定是否在執行過程中遇到了根本性問題,或者在Windows API中存在與格式化系統錯誤消息有關的錯誤。如何使用FormatMessage函數填充系統錯誤消息的va_list?

我有一個Windows API包裝方法的FormatMessage函數。我的實現允許調用者傳入附加參數以允許系統錯誤消息的格式化。

方法簽名如下:

bool setFormattedMessage(int arguments, ...)

在這個方法中我有下面的代碼塊不會出現爲我所有的單元測試工作:

if (arguments) 
{ 
    va_list argumentsList; 
    va_start(argumentsList, arguments); 

    size = FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | 
     FORMAT_MESSAGE_FROM_SYSTEM, NULL, code, language, (LPWSTR)&buffer, 0, 
     &argumentsList); 

    va_end(argumentsList); 
} 

注意: codelanguage是在實例化列表中設置的類的成員(使用GetLastErrorLANGIDFROMLCID(GetThreadLocale())

這裏有一些單元測試通過,沒有任何問題:

如果我創建類的實例爲34的錯誤代碼,我會得到下面的,如果我調用該方法爲test.setFormattedMessage(0)(注意,這沒有按」 t進入由於if (arguments)線而發佈的代碼塊):

錯誤的軟盤在驅動器中。插入%2(卷序列號:%3)插入驅動器%1.`

如果我調用方法test.setFormattedMessage(3, L"C:", L"disk 2", L"ABC123")我得到如下:

錯誤的軟盤驅動器中。將磁盤2(卷序列號:ABC123)插入驅動器C:。

然而,對於包括這樣的例子作爲%hs%08lx或我碰上試圖格式化文本的問題的錯誤消息。

採取的錯誤代碼573的例子當傳遞參數0到我得到了下面的文字(如預期)的方法:

{丟失的系統文件}所需的系統文件%HS是壞或失蹤。

但是,如果我通過在(1, "test")我結束了:

{丟失的系統文件}所需的系統文件HS是損壞或丟失。

事實上,我已經發現,不管是否我傳遞"test"L"test",的charwchar_t等副本我總是與上述錯誤信息而告終。創建的消息與傳遞(0)相同,但將%從字符串中刪除。

我發現所有其他參數的相同問題,除非它們被編號(例如%1)。我完全停留在這一點上,以至於我是否犯了根本性錯誤,或者在FormatMessage函數中確實存在缺陷。

我是,我可以在更換傳爲%hs佔位符以同樣的方式下的印象,我會做它swprintf

char * t = "file 123"; 
wchar_t a[2000]; 
int v = swprintf(a, 2000, L"Blah blah %hs", t); 
std::wstring someText(a, v); 

等等等等文件123

+0

我看不到有任何證據表明超出'%1','%2'等任何內容都將被視爲格式字符串。文檔在哪裏說它會?我認爲你在這裏的任務基本上是徒勞的。 – 2015-03-02 15:15:12

+0

@DavidHeffernan - https://msdn.microsoft.com/en-us/library/windows/desktop/ms679351(v=vs.85).aspx - 查找'%n!格式字符串!' - 我從這些評論中推斷出/筆記,我可以通過整數,「短字符風格」('%hs')字符串等。 – camelCase 2015-03-02 16:01:18

+2

我不會那樣讀。 '%hs'不符合法案。現在,'%1!hs!'可能會。 – 2015-03-02 16:08:15

回答

3

如果你讀了FormatMessage() documentation,它明確規定:

安全備註

如果調用函數時不FORMAT_MESSAGE_IGNORE_INSERTS,參數參數必須包含足夠的參數,以滿足所有的插入序列消息字符串,並且它們必須是正確的類型。因此,不要使用啓用了插入的不受信任或未知的消息字符串,因爲它們可以包含比參數提供的更多的插入序列,或可能包含錯誤類型的插入序列。 特別是,從API返回任意系統錯誤代碼並使用FORMAT_MESSAGE_FROM_SYSTEM而不使用FORMAT_MESSAGE_IGNORE_INSERTS是不安全的。

您的代碼使用FORMAT_MESSAGE_FROM_SYSTEM而不使用FORMAT_MESSAGE_IGNORE_INSERTS,這是不安全的。

所以,除非絕對相信,你知道的任何給定的系統錯誤代碼完全相同的格式,並決定其格式化參數傳遞之前,你的代碼檢查該錯誤代碼,你絕對肯定微軟將永遠不會改變該操作系統版本之間的錯誤消息的格式,然後你不能安全地使用帶有系統錯誤的格式化參數,所以甚至不要嘗試。當您通過FORMAT_MESSAGE_FROM_HMODULE使用您自己的錯誤消息來訪問您自己的EXE/DLL模塊資源或使用FORMAT_MESSAGE_FROM_STRING在內存中提供您自己的錯誤消息字符串時,格式化參數只能安全使用。

+0

+1「,並且您絕對相信Microsoft不會在操作系統版本之間更改該錯誤消息的格式」 - 我們將廣泛測試我們的實現,這是我們需要從Windows XP中考慮的事情,即將發佈的Windows 10以及非英語語言。 – camelCase 2015-03-03 05:12:32

1

根據David Heffernan的澄清(請參閱原始問題中的註釋),可以確定FormatMessage函數不支持將系統錯誤消息佔位符替換爲unl他們被編號(%1)。因此,諸如%hs的佔位符將變爲hs

請注意,只有當您不使用FORMAT_MESSAGE_IGNORE_INSERTS標誌時,上述內容才爲真。

我已經通過使用該鏈接,系統錯誤信息檢查:

https://msdn.microsoft.com/en-us/library/windows/desktop/ms681381(v=vs.85).aspx

這裏列出的錯誤消息不會出現混合佔位符樣式(錯誤消息將使用編號的佔位符或使用替代格式)。

對於編號佔位符,可以繼續使用原始問題中描述的功能。對於使用替代樣式的錯誤消息,可以使用vswprintf,但只能在調用FormatMessage函數和FORMAT_MESSAGE_IGNORE_INSERTS標誌(在填充的緩衝區上調用vswprintf,並記得重新初始化va_list)後才能使用。

當支持國際化時,總體方法也許有一個弱點。不同語言的佔位符順序可能有所不同。

的努力大大增加,如果你真的需要打電話FormatMessage不使用FORMAT_MESSAGE_IGNORE_INSERTS標識來檢索系統錯誤消息。除了上面描述的內容之外,如果您的va_list與佔位符的數量(以及可能的類型)不匹配,您也可以獲得C0000005異常。