2016-05-31 43 views
2

我有一套在Win32API和MFC上使用的異常類,它們捕獲當前的Win32錯誤代碼(GetLastError())並且制定一條關於它發生的位置的人類可讀信息。管理參數默認分配的規則是什麼?

在ctor開始工作之前,我依賴於捕獲當前錯誤代碼的能力,假設在調用ctor之前必須解析ctor的參數。

但是我遇到了當前版本中的問題,在VS2013中將編譯工具從120_xp切換到120(我不是100%確定這是更改的來源 - 它可能會暫時擱置一段時間與平臺工具集更改無關)。但是,我會認爲沒有一個是相關的 - 即C++會要求首先解決所有參數,然後執行函數調用,以便下面的默認參數error始終具有當前的錯誤代碼調用構造函數(這可能會改變它):

​​

這裏的調用上下文:

m_file = CreateFile(filename, FILE_GENERIC_READ, FILE_SHARE_READ, nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr); 
if (!m_file) // NOTE: m_file is smart handle and it's operator bool() knows that the invalid file handle is "false" 
    throw CWin32APIErrorException(__FUNCTION__, _T("CreateFile"), GetLastError()); 

如果我改變調用環境迫使當前錯誤代碼捕獲,我其實做得到錯誤2:

m_file = CreateFile(filename, FILE_GENERIC_READ, FILE_SHARE_READ, nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr); 
if (!m_file) 
{ 
    auto error = GetLastError(); 
    throw CWin32APIErrorException(__FUNCTION__, _T("CreateFile"), error); 
} 

FormatString簡單,它保留了當前的錯誤代碼:

CStringW FormatString(const wchar_t * format, ...) 
{ 
    const DWORD dwError = ::GetLastError(); 
    va_list args; 
    va_start(args, format); 
    CStringW str; 
    str.FormatV(format, args); 
    ::SetLastError(dwError); 
    return str; 
} 

我遇到的問題是,我看到錯誤122:緩衝液供應到系統調用太小。但是調用此異常的錯誤代碼是錯誤2:找不到文件。

我更喜歡我的軟件報告「文件未找到」,這是更正確和可操作的。

所以: 1.我錯了,C++保證所有參數在函數調用(ctor)被調用之前解決? 2.還能在哪裏調用來作出越來越有可能改變當前錯誤代碼(CString的構造函數(S)是唯一的其他東西CWin32APIErrorException::CWin32APIErrorException()構造函數。

含義是CString的構造函數顯然是改變當前錯誤之前被調用? !唉

有人可以讓我知道我錯了,在我的理解

謝謝

+4

參數評測的順序是不確定的,所以是的,CString的構造函數可以讓你的方式。 –

+0

謝謝雷蒙德。我剛剛有一個預感 - 事實證明,這是因爲我們正在編譯unicode - 所以'__FUNCTION__'正在被轉換爲CString ctor中的一個寬字符串,這引發了一些導致內部錯誤122的事情。 ( – Mordachai

+0

)這會比較容易修復,因爲你將字符串文字傳遞給了構造函數,所以它不需要'CString'參數,只需使用'LPCTSTR'(使用預處理器將'__FUNCTION__'宏轉換爲)解決了構造參數時執行任意代碼的問題你是否還有其他調用上下文需要傳遞動態生成的字符串? –

回答

1

我要去的情況下,離開這裏了這一點,幫助別人有類似的問題?!

在我上面的例子中,我正在編譯UNICODE,我的CWin32APIErrorException::CWin32APIErrorException(const CString & source, ...被調用時使用throw CWin32APIErrorException(__FUNCTION__, ...,然後調用CStringW(const char *)的構造函數,它在內部調用MultiByteToWideChar(),它使用第一次調用null模式獲得所需的緩衝區大小,然後再次調用分配給該大小的緩衝區。

第一次調用總是將當前錯誤設置爲122:緩衝區太小。

因此,我看到的行爲。

編譯時_MBCS,因爲__FUNCTION__字面沒有被內部轉換,我們沒有看到這個問題 - CStringA(const char *)沒有覆蓋當前的錯誤代碼,因爲沒有轉換是必要的。

這可能是因爲在將來的任何時間CString構造函數可以任意地具有改變當前錯誤的副作用,因爲任何其他原因...因此具有任何種類的構造對象類型意味着它固有地是容易得到錯誤的錯誤代碼,除非來電確保先佔領它,並通過對所捕獲的值:

if (!SomeWinAPIFunction(...)) 
{ 
    auto error = GetLastError(); // make sure we report the correct error! 
    throw CWin32APIErrorException(__FUNCTION__, "SomeWinAPIFunction", error); 
} 

這有可能改變CWin32APIErrorException構建函數採取const char *並分別const wchar_t *以避免CString的建設在捕獲當前錯誤代碼之前發生。

該方法的問題在於排列快速疊加(窄,窄),(窄,寬),(寬,窄),(寬,寬)。這只是兩個參數。我的實際類有三個(一個可選的參數)。

而最後一個最好留作CString,因爲它非常可能在運行時動態創建,在編譯時不知道,與前兩個參數不同。因此,即使ctor接口需要簡單的char *或wchar_t * ...,這意味着此問題未得到解決,它很可能會導致更改當前錯誤代碼的可能性。

最終,我無法想出一種方式捕捉錯誤報告所需的其他參數,並保證能夠正常工作 - 所以我只是將接口改爲使用顯式參數(沒有默認值)迫使我瀏覽我們的所有代碼,並確保我們在調用網站捕獲了正確的錯誤代碼並將其傳遞。

例如:

if (!::DestroyAcceleratorTable(m_hAccel)) 
     if (!m_hAccel) 
     { 
      auto error = GetLastError(); 
      throw CWinAPIErrorException(__FUNCTION__, "DestroyAcceleratorTable", FormatString(_T("%X"), m_hAccel), error); 
     } 
+0

一個快速提示:我可以使用'_T(__ FUNCTION __)'來避免' CString'轉換ctor,並且它會和MBCS編譯模型一樣工作(也很糟糕)。然而,我認爲現在是迫使呼叫者認識到這個問題的更深層次的時候了(儘管如果我想出了一個萬無一失的機制,我會把它全部隱藏起來)。 – Mordachai