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


90

इसके बिना :

  • 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);
 }

जवाबों:


134

यहां सिस्टम से त्रुटि संदेश प्राप्त करने का उचित तरीका है 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ध्वज का उपयोग है । एमएसडीएन थोड़ा स्पष्ट नहीं है कि सम्मिलन का उपयोग कैसे किया जाना चाहिए, लेकिन रेमंड चेन ने नोट किया कि सिस्टम संदेश को पुनर्प्राप्त करते समय आपको उनका उपयोग कभी नहीं करना चाहिए , क्योंकि आपके पास यह जानने का कोई तरीका नहीं है कि सिस्टम किस अपेक्षा को सम्मिलित करता है।

FWIW, यदि आप Visual C ++ का उपयोग कर रहे हैं, तो आप _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 का हिस्सा सीधे नहीं।


8
खबरदार: यह कोड एक Win32 त्रुटि कोड के स्थान पर hResult का उपयोग करता है: वे अलग-अलग चीजें हैं! आपको वास्तव में होने वाली तुलना में पूरी तरह से अलग त्रुटि का पाठ मिल सकता है।
आंद्रेई बेलोगॉर्टसेफ़

1
बहुत बढ़िया बिंदु, @Andrei - और वास्तव में, भले ही त्रुटि है एक Win32 त्रुटि, इस दिनचर्या ही सफल होगा अगर यह एक है प्रणाली त्रुटि - एक मजबूत त्रुटि हैंडलिंग तंत्र त्रुटि के स्रोत के बारे में पता करने की आवश्यकता होगी, कोड की जांच FormatMessage को कॉल करने से पहले और शायद इसके बजाय अन्य स्रोतों को क्वेरी करें।
शोग

1
@AndreiBelogortseff मुझे कैसे पता चलेगा कि प्रत्येक मामले में क्या उपयोग करना है? उदाहरण के लिए, RegCreateKeyExएक रिटर्न LONG। इसके डॉक्स कहता है कि मैं उपयोग कर सकते हैं FormatMessageत्रुटि को पुनः प्राप्त करने के लिए, लेकिन मैं कास्ट करने के लिए है LONGएक में HRESULT
सीएसएल

FormatMessage () एक DWORD, @csl, एक अहस्ताक्षरित पूर्णांक लेता है जिसे एक मान्य त्रुटि कोड माना जाता है। सभी रिटर्न मान नहीं - या उस मामले के लिए HRESULTS - मान्य त्रुटि कोड होंगे; सिस्टम मानता है कि आपने सत्यापित किया है कि यह फ़ंक्शन को कॉल करने से पहले है। RegCreateKeyEx के लिए डॉक्स निर्दिष्ट करना चाहिए कि जब रिटर्न मान को एक त्रुटि के रूप में व्याख्या किया जा सकता है ... उस चेक को पहले करें , और उसके बाद ही फॉर्ममैसेज को कॉल करें।
शोग

1
MSDN वास्तव में अब थोड़े समान कोड का अपना संस्करण प्रदान करता है ।
ahmd0

14

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

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

   // do something with the error...

   //automatic cleanup when error goes out of scope
}

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

इसलिए हमेशा ऊपर दिए गए शोग ९ के अनुसार इस प्रकार करें:

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

   // do something with the error...

   //automatic cleanup when error goes out of scope
}

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

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

1
यदि आप इसे सुरक्षित (शायद बहुत कुशल नहीं ) बनाना चाहते हैं, तो आप इसे C ++ में कर सकते हैं:std::wstring strErrorText = _com_error(hresult).ErrorMessage();
ahmd0

11

इसे इस्तेमाल करे:

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);
}

शून्य हैंडललेस्ट्रोर (हॉर्सल्ट)?
आरोन

1
निश्चित रूप से आप इन रूपांतरणों को स्वयं बना सकते हैं।
oefe

@Atklin: यदि आप किसी पैरामीटर से hresult का उपयोग करना चाहते हैं, तो आपको स्पष्ट रूप से पहली पंक्ति (GetLastError ()) की आवश्यकता नहीं है।
डेविड हनक

4
GetLastError किसी HResult को वापस नहीं करता है। यह एक Win32 त्रुटि कोड देता है। नाम PrintLastError पसंद कर सकते हैं क्योंकि यह वास्तव में कुछ भी नहीं संभालता है। और FORMAT_MESSAGE_IGNORE_INSERTS का उपयोग करना सुनिश्चित करें।
रोब कैनेडी

अपने लोगों की मदद के लिए धन्यवाद :) - बहुत सराहना की
एरोन

5

यह अधिकांश जवाबों के अतिरिक्त है, लेकिन इसके बजाय फ़ंक्शन का उपयोग LocalFree(errorText)करने के लिए HeapFree:

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

MSDN साइट से :

विंडोज 10 :
लोकलफ्री आधुनिक एसडीके में नहीं है, इसलिए इसका उपयोग परिणाम बफर को मुक्त करने के लिए नहीं किया जा सकता है। इसके बजाय, HeapFree (GetProcessHeap (), आबंटित माल) का उपयोग करें। इस मामले में, यह मेमोरी पर LocalFree को कॉल करने के समान है।

अद्यतन
मैंने पाया कि LocalFreeएसडीके के संस्करण १०.०.१०२४०.० (विनबेसेह में लाइन ११० Win) है। हालाँकि, चेतावनी अभी भी ऊपर दिए गए लिंक में मौजूद है।

#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सिस्टम संदेशों में लाइन टूट को ठीक करने के लिए ध्वज का उपयोग करने का भी सुझाव दूंगा।

MSDN साइट से :

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

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

FormatMessage केवल ERROR_SYSTEM_PROCESS_TERMINATED और ERROR_UNHANDLED_EXCEPTION सिस्टम त्रुटियों के लिए आंशिक संदेश क्यों बनाता है?


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);

}


1
ध्यान दें कि आप _sntprintf_sUNICODE मामले में सही बफर आकार नहीं दे रहे हैं । फ़ंक्शन वर्णों की संख्या लेता है, इसलिए आप चाहते हैं _countofया ARRAYSIZEउर्फ के sizeof(buffer) / sizeof(buffer[0])बजाय sizeof
थफबा

4

C ++ 11 के बाद से, आप इसके बजाय मानक लाइब्रेरी का उपयोग कर सकते हैं FormatMessage:

#include <system_error>

std::string message = std::system_category().message(hr)

2

जैसा कि अन्य उत्तरों में बताया गया है:

  • FormatMessageएक DWORDपरिणाम लेता है HRESULT(आमतौर पर GetLastError()) नहीं।
  • LocalFree द्वारा जारी की गई मेमोरी को रिलीज़ करने के लिए आवश्यक है FormatMessage

मैंने उपरोक्त बिंदु लिए और अपने उत्तर के लिए कुछ और जोड़ दिए:

  • FormatMessageस्मृति को आवंटित करने और आवश्यकतानुसार जारी करने के लिए एक कक्षा में लपेटें
  • ऑपरेटर अधिभार का उपयोग करें (जैसे operator LPTSTR() const { return ...; }कि आपकी कक्षा को एक स्ट्रिंग के रूप में उपयोग किया जा सकता है
class CFormatMessage
{
public:
    CFormatMessage(DWORD dwMessageId,
                   DWORD dwLanguageId = MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT)) :
        m_text(NULL)
    {
        Assign(dwMessageId, dwLanguageId);
    }

    ~CFormatMessage()
    {
        Clear();
    }

    void Clear()
    {
        if (m_text)
        {
            LocalFree(m_text);
            m_text = NULL;
        }
    }

    void Assign(DWORD dwMessageId,
                DWORD dwLanguageId = MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT))
    {
        Clear();
        DWORD dwFlags = FORMAT_MESSAGE_FROM_SYSTEM
            | FORMAT_MESSAGE_ALLOCATE_BUFFER
            | FORMAT_MESSAGE_IGNORE_INSERTS,
        FormatMessage(
            dwFlags,
            NULL,
            dwMessageId,
            dwLanguageId,
            (LPTSTR) &m_text,
            0,
            NULL);
    }

    LPTSTR text() const { return m_text; }
    operator LPTSTR() const { return text(); }

protected:
    LPTSTR m_text;

};

उपरोक्त कोड का अधिक पूर्ण संस्करण यहां देखें: https://github.com/stephenquan/FormatMessage

उपरोक्त वर्ग के साथ, उपयोग बस है:

    std::wcout << (LPTSTR) CFormatMessage(GetLastError()) << L"\n";

0

नीचे दिया गया कोड कोड है C ++ के बराबर मैंने Microsoft के 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);
}
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.