2009-01-18 5 views
78

के बिना:मुझे C++ में FormatMessage() को ठीक से कैसे उपयोग करना चाहिए?

  • MFC
  • ATL

मैं FormatMessage() का उपयोग कैसे एक HRESULT के लिए त्रुटि पाठ प्राप्त करने के लिए कर सकते हैं?

HRESULT hresult = application.CreateInstance("Excel.Application"); 

if (FAILED(hresult)) 
{ 
    // what should i put here to obtain a human-readable 
    // description of the error? 
    exit (hresult); 
} 

उत्तर

123

यहाँ प्रणाली से वापस एक HRESULT के लिए एक त्रुटि संदेश प्राप्त करने के लिए उचित तरीका है (इस मामले में नामित HRESULT, या आप GetLastError() साथ यह जगह ले सकता है):

LPTSTR errorText = NULL; 

FormatMessage(
    // use system message tables to retrieve error text 
    FORMAT_MESSAGE_FROM_SYSTEM 
    // allocate buffer on local heap for error text 
    |FORMAT_MESSAGE_ALLOCATE_BUFFER 
    // Important! will fail otherwise, since we're not 
    // (and CANNOT) pass insertion parameters 
    |FORMAT_MESSAGE_IGNORE_INSERTS, 
    NULL, // unused with FORMAT_MESSAGE_FROM_SYSTEM 
    hresult, 
    MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), 
    (LPTSTR)&errorText, // output 
    0, // minimum size for output buffer 
    NULL); // arguments - see note 

if (NULL != errorText) 
{ 
    // ... do something with the string `errorText` - log it, display it to the user, etc. 

    // release memory allocated by FormatMessage() 
    LocalFree(errorText); 
    errorText = NULL; 
} 

इस और डेविड हानाक के उत्तर के बीच महत्वपूर्ण अंतर FORMAT_MESSAGE_IGNORE_INSERTS ध्वज का उपयोग है। एमएसडीएन थोड़ा सा अस्पष्ट है कि प्रविष्टि का उपयोग कैसे किया जाना चाहिए, लेकिन Raymond Chen notes that you should never use them सिस्टम संदेश पुनर्प्राप्त करते समय, क्योंकि आपके पास यह जानने का कोई तरीका नहीं है कि सिस्टम किस प्रविष्टि की अपेक्षा करता है।

Fwiw, आप ++ आप _com_error वर्ग का उपयोग करके अपने जीवन को थोड़ा आसान बना सकते हैं विज़ुअल सी उपयोग कर रहे हैं:

{ 
    _com_error error(hresult); 
    LPCTSTR errorText = error.ErrorMessage(); 

    // do something with the error... 

    //automatic cleanup when error goes out of scope 
} 

MFC या ATL का हिस्सा नहीं सीधे जहाँ तक मुझे पता है हूँ।

+4

सावधान रहें: यह कोड Win32 त्रुटि कोड के स्थान पर hResult का उपयोग करता है: वे अलग-अलग चीजें हैं! वास्तव में जो हुआ उससे आप एक पूरी तरह से अलग त्रुटि का पाठ प्राप्त कर सकते हैं। –

+0

उत्कृष्ट बिंदु, @ एंड्रेई - और वास्तव में, अगर त्रुटि * एक Win32 त्रुटि है, तो यह दिनचर्या केवल तभी सफल होगी जब यह एक * सिस्टम * त्रुटि है - एक मजबूत त्रुटि-हैंडलिंग तंत्र को स्रोत के बारे में पता होना चाहिए त्रुटि, FormatMessage को कॉल करने से पहले कोड की जांच करें और इसके बजाय अन्य स्रोतों से पूछें। – Shog9

+0

@AndreiBelogortseff मुझे कैसे पता चलेगा कि प्रत्येक मामले में क्या उपयोग करना है? उदाहरण के लिए, 'RegCreateKeyEx' एक' लंबा 'देता है। इसके दस्तावेज़ कहते हैं कि मैं त्रुटि पुनर्प्राप्त करने के लिए 'FormatMessage' का उपयोग कर सकता हूं, लेकिन मुझे' LONG' को 'HRESULT' में डालना होगा। – csl

9

इस प्रयास करें:

void PrintLastError (const char *msg /* = "Error occurred" */) { 
     DWORD errCode = GetLastError(); 
     char *err; 
     if (!FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, 
          NULL, 
          errCode, 
          MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // default language 
          (LPTSTR) &err, 
          0, 
          NULL)) 
      return; 

     static char buffer[1024]; 
     _snprintf(buffer, sizeof(buffer), "ERROR: %s: %s\n", msg, err); 
     OutputDebugString(buffer); // or otherwise log it 
     LocalFree(err); 
} 
+0

शून्य हैंडललास्टर (घंटा)? – Aaron

+0

हाय डेविड, मैं नहीं बल्कि MFC का उपयोग करेंगे नहीं: "ट्रेस()" ... – Aaron

+1

निश्चित रूप से आप इन adaptions खुद बना सकते हैं। – oefe

13

ध्यान रखें कि आप निम्न कर नहीं कर सकते हैं:

{ 
    LPCTSTR errorText = _com_error(hresult).ErrorMessage(); 

    // do something with the error... 

    //automatic cleanup when error goes out of scope 
} 

के रूप में वर्ग बनाया है और ढेर गलत स्थान को इंगित करने के errorText छोड़ने पर नष्ट हो जाता है। ज्यादातर मामलों में इस स्थान में अभी भी त्रुटि स्ट्रिंग होगी, लेकिन थ्रेडेड अनुप्रयोगों को लिखते समय यह संभावना तेजी से गिर जाती है।

तो हमेशा यह कर के रूप में ऊपर Shog9 द्वारा दिए इस प्रकार है:

{ 
    _com_error error(hresult); 
    LPCTSTR errorText = error.ErrorMessage(); 

    // do something with the error... 

    //automatic cleanup when error goes out of scope 
} 
+6

'_com_error' ऑब्जेक्ट * आपके * दोनों उदाहरणों में स्टैक पर बनाया गया है । जिस शब्द को आप ढूंढ रहे हैं वह * अस्थायी * है। पूर्व उदाहरण में, वस्तु एक अस्थायी है जो कथन के अंत में नष्ट हो जाती है। –

+0

यूप, इसका मतलब था। लेकिन मुझे उम्मीद है कि ज्यादातर लोग कोड से बाहर निकलने में कम से कम सक्षम होंगे। कथन के अंत में तकनीकी रूप से अस्थायी नष्ट नहीं होते हैं, लेकिन अनुक्रम बिंदु के अंत में। (जो इस उदाहरण में एक ही बात है, इसलिए यह सिर्फ विभाजित बाल है।) – Marius

+5

बीटीडब्ल्यू, _com_error को 'comdef.h' में घोषित किया गया है – Francois

4

यहाँ कि यूनिकोड संभालती

void HandleLastError(const TCHAR *msg /* = "Error occured" */) { 
    DWORD errCode = GetLastError(); 
    TCHAR *err; 
    if (!FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, 
         NULL, 
         errCode, 
         MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // default language 
         (LPTSTR) &err, 
         0, 
         NULL)) 
     return; 

    //TRACE("ERROR: %s: %s", msg, err); 
    TCHAR buffer[1024]; 
    _sntprintf_s(buffer, sizeof(buffer), _T("ERROR: %s: %s\n"), msg, err); 
    OutputDebugString(buffer); 
    LocalFree(err); 

}

4

यह डेविड के समारोह का एक संस्करण है अधिकांश उत्तरों के लिए अधिक एक अतिरिक्त है, लेकिनका उपयोग करने के बजाय

::HeapFree(::GetProcessHeap(), NULL, errorText); 

From the MSDN site:

Windows 10: ३०५१८१४३२१० HeapFree फ़ंक्शन का उपयोग
LocalFree आधुनिक एसडीके में नहीं है, तो यह परिणाम बफर मुक्त करने के लिए इस्तेमाल नहीं किया जा सकता। इसके बजाय, हेपफ़्री (GetProcessHeap(), आवंटित संदेश) का उपयोग करें। इस मामले में, यह स्मृति पर स्थानीय फ्री को कॉल करने जैसा ही है।

अद्यतन
मैंने पाया कि LocalFree एसडीके (WinBase.h में लाइन 1108) के संस्करण 10.0.10240.0 में है। हालांकि, ऊपर दिए गए लिंक में चेतावनी अभी भी मौजूद है।

#pragma region Desktop Family or OneCore Family 
#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP | WINAPI_PARTITION_SYSTEM) 

WINBASEAPI 
_Success_(return==0) 
_Ret_maybenull_ 
HLOCAL 
WINAPI 
LocalFree(
    _Frees_ptr_opt_ HLOCAL hMem 
    ); 

#endif /* WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP | WINAPI_PARTITION_SYSTEM) */ 
#pragma endregion 

अद्यतन 2
मैं भी सिस्टम संदेश में पंक्ति विराम को साफ़ रखने के लिए FORMAT_MESSAGE_MAX_WIDTH_MASK ध्वज का उपयोग कर सुझाव है।

From the MSDN site:

FORMAT_MESSAGE_MAX_WIDTH_MASK
समारोह संदेश परिभाषा पाठ में नियमित पंक्ति विराम ध्यान नहीं देता। फ़ंक्शन आउटपुट बफर में संदेश परिभाषा पाठ में हार्ड-कोडेड लाइन ब्रेक स्टोर करता है। फ़ंक्शन कोई नई लाइन ब्रेक उत्पन्न नहीं करता है।

अद्यतन 3
वहाँ 2 विशेष प्रणाली त्रुटि कोड है कि पूरा संदेश वापस नहीं है की सिफारिश की दृष्टिकोण का उपयोग कर प्रतीत होता है:

Why does FormatMessage only create partial messages for ERROR_SYSTEM_PROCESS_TERMINATED and ERROR_UNHANDLED_EXCEPTION system errors?

0

नीचे कोड कोड सी है ++ समकक्ष मैंने Microsoft's ErrorExit() के विपरीत लिखा है लेकिन सभी मैक्रोज़ से बचने और यूनिकोड का उपयोग करने के लिए थोड़ा बदल गया है। अनावश्यक कास्ट और मैलॉक्स से बचने के लिए यहां विचार है। मैं सभी सी कास्ट से बच नहीं पाया लेकिन यह सबसे अच्छा है जो मैं कर सकता था। FormatMessageW() को प्रस्तुत करना, जिसके लिए प्रारूप फ़ंक्शन द्वारा आवंटित पॉइंटर और GetLastError() से त्रुटि आईडी की आवश्यकता होती है। Static_cast के बाद पॉइंटर को सामान्य wchar_t पॉइंटर की तरह इस्तेमाल किया जा सकता है।

#include <string> 
#include <windows.h> 

void __declspec(noreturn) error_exit(const std::wstring FunctionName) 
{ 
    // Retrieve the system error message for the last-error code 
    const DWORD ERROR_ID = GetLastError(); 
    void* MsgBuffer = nullptr; 
    LCID lcid; 
    GetLocaleInfoEx(L"en-US", LOCALE_RETURN_NUMBER | LOCALE_ILANGUAGE, (wchar_t*)&lcid, sizeof(lcid)); 

    //get error message and attach it to Msgbuffer 
    FormatMessageW(
     FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, 
     NULL, ERROR_ID, lcid, (wchar_t*)&MsgBuffer, 0, NULL); 
    //concatonate string to DisplayBuffer 
    const std::wstring DisplayBuffer = FunctionName + L" failed with error " + std::to_wstring(ERROR_ID) + L": " + static_cast<wchar_t*>(MsgBuffer); 

    // Display the error message and exit the process 
    MessageBoxExW(NULL, DisplayBuffer.c_str(), L"Error", MB_ICONERROR | MB_OK, static_cast<WORD>(lcid)); 

    ExitProcess(ERROR_ID); 
} 
संबंधित मुद्दे