GetLastError () द्वारा दिए गए त्रुटि कोड से त्रुटि संदेश कैसे प्राप्त करें?


138

विंडोज एपीआई कॉल के बाद, मैं टेक्स्ट फॉर्म में अंतिम त्रुटि संदेश कैसे प्राप्त कर सकता हूं?

GetLastError() पूर्णांक मान लौटाता है, पाठ संदेश नहीं।


वहाँ दृश्य स्टूडियो में उपकरण अनुभाग में एक exe त्रुटि देखने का उपयोग करें जो यह बहुत अच्छी तरह से करते हैं जब आपको केवल डीबगिंग के लिए त्रुटि से संदेश की आवश्यकता होती है।
कोल्डकैट

@ColdCat: डिबगिंग के लिए केवल एक @err,hrघड़ी जोड़ना बहुत आसान है , और डीबगर स्वचालित रूप से अंतिम त्रुटि कोड को मानव-पठनीय प्रतिनिधित्व में बदल देता है। ,hrफॉर्मेट स्पेसिफायर किसी भी अभिव्यक्ति है कि एक अभिन्न मूल्य के लिए मूल्यांकन करता है, उदाहरण के लिए एक के लिए काम करता 5,hrघड़ी प्रदर्शित करेगा "ERROR_ACCESS_DENIED:। पहुंच अस्वीकृत कर दी"
IInspectable

2
से GetLastError()प्रलेखन: " प्रणाली त्रुटि कोड के लिए एक त्रुटि स्ट्रिंग प्राप्त करने के लिए, का उपयोग FormatMessage()कार्य करते हैं। "। MSDN पर अंतिम-त्रुटि कोड उदाहरण पुनर्प्राप्त करना देखें ।
रेमी लेबेउ

जवाबों:


145
//Returns the last Win32 error, in string format. Returns an empty string if there is no error.
std::string GetLastErrorAsString()
{
    //Get the error message, if any.
    DWORD errorMessageID = ::GetLastError();
    if(errorMessageID == 0)
        return std::string(); //No error message has been recorded

    LPSTR messageBuffer = nullptr;
    size_t size = FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
                                 NULL, errorMessageID, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPSTR)&messageBuffer, 0, NULL);

    std::string message(messageBuffer, size);

    //Free the buffer.
    LocalFree(messageBuffer);

    return message;
}

2
मेरा मानना ​​है कि आपको वास्तव (LPSTR)&messageBufferमें इस मामले में पारित करने की आवश्यकता है , क्योंकि अन्यथा कोई रास्ता नहीं है FormatMessageA आवंटित बफर को इंगित करने के लिए इसके मूल्य को बदल सकता है।
काइलोटन १०'१४ को

2
ओह, वाह, हाँ, यह थोड़े अजीब है। यह सूचक को कैसे संशोधित करेगा? लेकिन इसे पॉइंटर का पता (पॉइंटर-टू-ए-पॉइंटर) पास करते हुए, लेकिन इसे रेगुलर पॉइंटर में डालना ... विचित्र अजीबता। सिर के लिए धन्यवाद, इसे अपने कोड बेस (और मेरे उत्तर) में तय किया। बहुत सूक्ष्म पकड़।
जमिन ग्रे

1
बहुत बहुत धन्यवाद, आपका उदाहरण MSDN से एक की तुलना में बहुत स्पष्ट है। अधिक, MSDN से एक भी संकलन नहीं कर सका। यह कुछ भी शामिल strsafe.hहैडर, कि नहीं है सुरक्षित , सब पर उस में एक संकलक त्रुटियों की एक गुच्छा के कारण winuser.hऔर winbase.h
हाय-एंजेल

2
कुछ त्रुटि आईडी समर्थित नहीं हैं। उदाहरण के लिए, 0x2EE7, ERROR_INTERNET_NAME_NOT_RESOLVED FormatMessage: 0x13D को कॉल करते समय एक नई त्रुटि का कारण बनता है, सिस्टम 2% के लिए संदेश फ़ाइल में संदेश संख्या 0x% 1 के लिए संदेश पाठ नहीं ढूँढ सकता है।
ब्रेंट

3
इस कार्यान्वयन के मुद्दे: 1 GetLastErrorसंभावित रूप से बहुत देर से बुलाए जाते हैं। 2कोई यूनिकोड समर्थन नहीं। 3अपवाद सुरक्षा गारंटी को लागू किए बिना अपवादों का उपयोग।
IInspectable

65

कुछ टिप्पणियों को ध्यान में रखने के लिए अद्यतन (11/2017)।

आसान उदाहरण:

wchar_t buf[256];
FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
               NULL, GetLastError(), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), 
               buf, (sizeof(buf) / sizeof(wchar_t)), NULL);

3
@ हाय-एंजेल - उदाहरण मानता है कि आप यूनिकोड परिभाषित हैं। 'फ़ॉर्मेटमेज़ेज' वास्तव में एक मैक्रो है जो एएनएसआई / एमबीसीएस कैरेक्टर बफ़र्स के लिए 'फ़ॉर्मेटमैसेजए' या यूटीएफ 16 / यूएनआईसीओडीई बफ़र्स के लिए 'फ़ॉर्मेटमैसेज डब्ल्यू' के आधार पर फैलता है, यह इस बात पर निर्भर करता है कि आवेदन कैसे संकलित है। मैंने आउटपुट बफर प्रकार (wchar_t) से मेल खाने वाले संस्करण को स्पष्ट रूप से लागू करने के लिए ऊपर दिए गए उदाहरण को संपादित करने की स्वतंत्रता ली।
बुक्स

2
FORMAT_MESSAGE_IGNORE_INSERTS जोड़ें: "यदि आप प्रारूप स्ट्रिंग के नियंत्रण में नहीं हैं, तो आपको% 1 को समस्या पैदा करने से रोकने के लिए FORMAT_MESSAGE_IGNORE_INSERTS पास करना होगा।" blogs.msdn.microsoft.com/oldnewthing/20071128-00/?p=24353
Roi Danton

1
इस कार्यान्वयन के मुद्दे: 1महत्वपूर्ण FORMAT_MESSAGE_IGNORE_INSERTSध्वज को निर्दिष्ट करने में विफलता । 2 GetLastErrorसंभावित रूप से बहुत देर से बुलाया। 3256 कोड इकाइयों के लिए संदेश के विपरीत प्रतिबंध। 4कोई त्रुटि से निपटने।
IInspectable

1
sizeof (buf) को ARRAYSIZE (buf) होना चाहिए क्योंकि FormatMessage TCHARs में बफर के आकार की अपेक्षा करता है, बाइट्स में नहीं।
काई

1
हम के 256बजाय का उपयोग नहीं कर सकते (sizeof(buf) / sizeof(wchar_t)? क्या यह स्वीकार्य और सुरक्षित है?
बैटलस्टेड



14

GetLastError एक संख्यात्मक त्रुटि कोड देता है। एक वर्णनात्मक त्रुटि संदेश प्राप्त करने के लिए (जैसे, उपयोगकर्ता को प्रदर्शित करने के लिए), आप फॉर्ममैसेज को कॉल कर सकते हैं :

// This functions fills a caller-defined character buffer (pBuffer)
// of max length (cchBufferLength) with the human-readable error message
// for a Win32 error code (dwErrorCode).
// 
// Returns TRUE if successful, or FALSE otherwise.
// If successful, pBuffer is guaranteed to be NUL-terminated.
// On failure, the contents of pBuffer are undefined.
BOOL GetErrorMessage(DWORD dwErrorCode, LPTSTR pBuffer, DWORD cchBufferLength)
{
    if (cchBufferLength == 0)
    {
        return FALSE;
    }

    DWORD cchMsg = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
                                 NULL,  /* (not used with FORMAT_MESSAGE_FROM_SYSTEM) */
                                 dwErrorCode,
                                 MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
                                 pBuffer,
                                 cchBufferLength,
                                 NULL);
    return (cchMsg > 0);
}

C ++ में, आप std :: string class का उपयोग करके इंटरफ़ेस को काफी सरल बना सकते हैं:

#include <Windows.h>
#include <system_error>
#include <memory>
#include <string>
typedef std::basic_string<TCHAR> String;

String GetErrorMessage(DWORD dwErrorCode)
{
    LPTSTR psz{ nullptr };
    const DWORD cchMsg = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM
                                         | FORMAT_MESSAGE_IGNORE_INSERTS
                                         | FORMAT_MESSAGE_ALLOCATE_BUFFER,
                                       NULL, // (not used with FORMAT_MESSAGE_FROM_SYSTEM)
                                       dwErrorCode,
                                       MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
                                       reinterpret_cast<LPTSTR>(&psz),
                                       0,
                                       NULL);
    if (cchMsg > 0)
    {
        // Assign buffer to smart pointer with custom deleter so that memory gets released
        // in case String's c'tor throws an exception.
        auto deleter = [](void* p) { ::LocalFree(p); };
        std::unique_ptr<TCHAR, decltype(deleter)> ptrBuffer(psz, deleter);
        return String(ptrBuffer.get(), cchMsg);
    }
    else
    {
        auto error_code{ ::GetLastError() };
        throw std::system_error( error_code, std::system_category(),
                                 "Failed to retrieve error message string.");
    }
}

नोट: ये फ़ंक्शन HRESULT मानों के लिए भी काम करते हैं। बस DWORD dwErrorCode से पहले पैरामीटर को HRESULT hResult में बदलें। बाकी कोड अपरिवर्तित रह सकता है।


ये कार्यान्वयन मौजूदा उत्तरों पर निम्नलिखित सुधार प्रदान करते हैं:

  • पूरा नमूना कोड, कॉल करने के लिए केवल एपीआई का संदर्भ नहीं।
  • C और C ++ कार्यान्वयन दोनों प्रदान करता है।
  • यूनिकोड और एमबीसीएस परियोजना सेटिंग्स दोनों के लिए काम करता है।
  • एक इनपुट पैरामीटर के रूप में त्रुटि कोड लेता है। यह महत्वपूर्ण है, क्योंकि थ्रेड का अंतिम त्रुटि कोड केवल अच्छी तरह से परिभाषित बिंदुओं पर मान्य है। एक इनपुट पैरामीटर कॉलर को प्रलेखित अनुबंध का पालन करने की अनुमति देता है।
  • उचित अपवाद सुरक्षा को लागू करता है। अपवाद का उपयोग करने वाले अन्य सभी समाधानों के विपरीत, यह कार्यान्वयन रिटर्न वैल्यू का निर्माण करते समय एक अपवाद को फेंकने की स्थिति में मेमोरी को लीक नहीं करेगा।
  • FORMAT_MESSAGE_IGNORE_INSERTSझंडे का उचित उपयोग । FORMAT_MESSAGE_IGNORE_INSERTS ध्वज का महत्व देखेंअधिक जानकारी के लिए ।
  • कुछ अन्य उत्तरों के विपरीत उचित त्रुटि हैंडलिंग / त्रुटि रिपोर्टिंग, जो चुपचाप त्रुटियों को अनदेखा करती है।


इस जवाब को स्टैक ओवरफ्लो डॉक्यूमेंटेशन से शामिल किया गया है। निम्नलिखित उपयोगकर्ताओं ने उदाहरण के लिए योगदान दिया है: स्टैप्ट्र , अजय , कोडी ग्रे II , IInspectable


1
फेंकने के बजाय std::runtime_error, मैं यह फेंकने का सुझाव देता हूं कि असफल कॉल के बाद वापसी मूल्य std::system_error(lastError, std::system_category(), "Failed to retrieve error message string.")कहां lastErrorहोगा । GetLastError()FormatMessage()
zett42 11

FormatMessageसीधे आपके सी संस्करण का उपयोग करने का क्या फायदा है ?
मीका विडेनमैन

@mic: यह पूछने जैसा है: "C में फ़ंक्शन का क्या लाभ है?" कार्य अमूर्तता प्रदान करके जटिलता को कम करते हैं। इस स्थिति में, अमूर्तता 7 से 3 तक मापदंडों की संख्या को कम कर देती है, संगत झंडे और मापदंडों को पारित करके एपीआई के प्रलेखित अनुबंध को लागू करता है, और प्रलेखित त्रुटि रिपोर्टिंग तर्क को एन्कोड करता है। यह एक संक्षिप्त इंटरफ़ेस प्रदान करता है, जो आसानी से शब्दार्थ का अनुमान लगाता है, जिससे आपको दस्तावेज़ पढ़ने के लिए 11 मिनट का निवेश करने की आवश्यकता होती है ।
IInspectable

मुझे यह उत्तर पसंद है, यह बहुत स्पष्ट है कि कोड की शुद्धता पर सावधानीपूर्वक ध्यान दिया गया है। मेरे दो सवाल हैं। 1) ::HeapFree(::GetProcessHeap(), 0, p)डेलीटर में आप इसका उपयोग क्यों करते हैं , ::LocalFree(p)जैसा कि प्रलेखन से पता चलता है? 2) मुझे एहसास है कि प्रलेखन यह इस तरह से करने के लिए कहता है, लेकिन reinterpret_cast<LPTSTR>(&psz)सख्त अलियासिंग नियम का उल्लंघन नहीं करता है?
तेह जोए

@teh: प्रतिक्रिया के लिए धन्यवाद। प्रश्न 1): ईमानदारी से, मुझे नहीं पता कि यह सुरक्षित है। मुझे याद नहीं है कि किसने या क्यों कॉल किया HeapFreeथा, जबकि यह अभी भी SO प्रलेखन में एक विषय था। मुझे जांच करनी होगी (हालाँकि प्रलेखन स्पष्ट प्रतीत होता है, कि यह है सुरक्षित नहीं है )। प्रश्न 2): यह दो गुना है। MBCS कॉन्फ़िगरेशन के लिए, के LPTSTRलिए एक उपनाम है char*। वह कास्ट हमेशा सुरक्षित है। यूनिकोड कॉन्फ़िगरेशन के लिए, कास्टिंग wchar_t*किसी भी दर पर गड़बड़ है। मुझे नहीं पता कि यह सी में सुरक्षित है, लेकिन इसकी सबसे अधिक संभावना है कि यह सी ++ में नहीं है। मुझे इसकी भी जाँच करनी होगी।
IInspectable

13

सामान्य तौर पर, आपको उपयोग करने की आवश्यकता होती है FormatMessage Win32 त्रुटि कोड से टेक्स्ट में बदलने के करने की ।

MSDN प्रलेखन से :

एक संदेश स्ट्रिंग प्रारूपित करता है। फ़ंक्शन को इनपुट के रूप में एक संदेश परिभाषा की आवश्यकता होती है। संदेश परिभाषा फ़ंक्शन में पारित बफर से आ सकती है। यह पहले से लोड किए गए मॉड्यूल में एक संदेश तालिका संसाधन से आ सकता है। या कॉल करने वाला फ़ंक्शन संदेश की परिभाषा के लिए सिस्टम के संदेश तालिका संसाधन (ओं) को खोजने के लिए कह सकता है। फ़ंक्शन एक संदेश पहचानकर्ता और एक भाषा पहचानकर्ता के आधार पर संदेश तालिका संसाधन में संदेश परिभाषा पाता है। फ़ंक्शन स्वरूपित संदेश पाठ को आउटपुट बफर में कॉपी करता है, यदि अनुरोध किया गया है तो किसी भी सम्मिलित सम्मिलित अनुक्रम को संसाधित करता है।

FormatMessage की घोषणा:

DWORD WINAPI FormatMessage(
  __in      DWORD dwFlags,
  __in_opt  LPCVOID lpSource,
  __in      DWORD dwMessageId, // your error code
  __in      DWORD dwLanguageId,
  __out     LPTSTR lpBuffer,
  __in      DWORD nSize,
  __in_opt  va_list *Arguments
);

7

यदि आप c # का उपयोग कर रहे हैं, तो आप इस कोड का उपयोग कर सकते हैं:

using System.Runtime.InteropServices;

public static class WinErrors
{
    #region definitions
    [DllImport("kernel32.dll", SetLastError = true)]
    static extern IntPtr LocalFree(IntPtr hMem);

    [DllImport("kernel32.dll", SetLastError = true)]
    static extern int FormatMessage(FormatMessageFlags dwFlags, IntPtr lpSource, uint dwMessageId, uint dwLanguageId, ref IntPtr lpBuffer, uint nSize, IntPtr Arguments);

    [Flags]
    private enum FormatMessageFlags : uint
    {
        FORMAT_MESSAGE_ALLOCATE_BUFFER = 0x00000100,
        FORMAT_MESSAGE_IGNORE_INSERTS = 0x00000200,
        FORMAT_MESSAGE_FROM_SYSTEM = 0x00001000,
        FORMAT_MESSAGE_ARGUMENT_ARRAY = 0x00002000,
        FORMAT_MESSAGE_FROM_HMODULE = 0x00000800,
        FORMAT_MESSAGE_FROM_STRING = 0x00000400,
    }
    #endregion

    /// <summary>
    /// Gets a user friendly string message for a system error code
    /// </summary>
    /// <param name="errorCode">System error code</param>
    /// <returns>Error string</returns>
    public static string GetSystemMessage(int errorCode)
    {
        try
        {
            IntPtr lpMsgBuf = IntPtr.Zero;

            int dwChars = FormatMessage(
                FormatMessageFlags.FORMAT_MESSAGE_ALLOCATE_BUFFER | FormatMessageFlags.FORMAT_MESSAGE_FROM_SYSTEM | FormatMessageFlags.FORMAT_MESSAGE_IGNORE_INSERTS,
                IntPtr.Zero,
                (uint) errorCode,
                0, // Default language
                ref lpMsgBuf,
                0,
                IntPtr.Zero);
            if (dwChars == 0)
            {
                // Handle the error.
                int le = Marshal.GetLastWin32Error();
                return "Unable to get error code string from System - Error " + le.ToString();
            }

            string sRet = Marshal.PtrToStringAnsi(lpMsgBuf);

            // Free the buffer.
            lpMsgBuf = LocalFree(lpMsgBuf);
            return sRet;
        }
        catch (Exception e)
        {
            return "Unable to get error code string from System -> " + e.ToString();
        }
    }
}

मैंने इस कोड काम की पुष्टि की है। टीएस को इस जवाब को स्वीकार नहीं करना चाहिए?
स्वदेव

2
यदि यह आगे फेंकने के लिए आवश्यक है, तो Win32Exception के साथ C # में इसे करने का एक सरल तरीका है
SerG

2
@swdev: क्यों किसी को भी एक प्रश्न के सी # में एक जवाब में चिह्नित स्वीकार करना चाहिए या c ++ ? जैसा कि यह खड़ा है, यह उत्तर पूछे जाने वाले प्रश्न को भी संबोधित नहीं करता है।
IInspectable

1
खैर, मुझे याद नहीं है कि इस प्रस्तावित उत्तर पर मतदान हुआ था। मैंने @ स्वदेव के तर्क में स्पष्ट दोष को संबोधित किया। लेकिन जब से आप मुझ पर विश्वास नहीं करने जा रहे हैं, मैं अब आपको यह साबित करने जा रहा हूं: यहां, एक और डाउन-वोट है। यह एक मेरे लिए है, क्योंकि यह उत्तर - जबकि यह उपयोगी हो सकता है एक अलग प्रश्न दिया जाता है - बस उपयोगी नहीं है जो प्रश्न पूछा जा रहा था।
IInspectable

2
"मुझे लगता है कि आपके पास स्पष्ट से परे की पेशकश करने के लिए उपयोगी अंतर्दृष्टि है" - वास्तव में, मेरे पास है
IInspectable

4

यदि आपको MBCS के साथ-साथ यूनिकोड का समर्थन करने की आवश्यकता है, तो Mr.C64 का उत्तर पर्याप्त नहीं है। बफर को TCHAR घोषित किया जाना चाहिए, और LPTSTR को कास्ट किया जाना चाहिए। ध्यान दें कि यह कोड उस कष्टप्रद न्यूलाइन से नहीं निपटता है जो Microsoft त्रुटि संदेश पर लागू होता है।

CString FormatErrorMessage(DWORD ErrorCode)
{
    TCHAR   *pMsgBuf = NULL;
    DWORD   nMsgLen = FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER |
        FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
        NULL, ErrorCode, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
        reinterpret_cast<LPTSTR>(&pMsgBuf), 0, NULL);
    if (!nMsgLen)
        return _T("FormatMessage fail");
    CString sMsg(pMsgBuf, nMsgLen);
    LocalFree(pMsgBuf);
    return sMsg;
}

इसके अलावा, संक्षिप्तता के लिए मुझे निम्नलिखित विधि उपयोगी लगती है:

CString GetLastErrorString()
{
    return FormatErrorMessage(GetLastError());
}

यदि CStringकोई अपवाद अपवाद नहीं करता है, तो यह कार्यान्वयन कॉल द्वारा आवंटित मेमोरी को लीक कर देता है FormatMessage
IInspectable

यह सच है, लेकिन मैंने कई वर्षों से इस कोड का उपयोग किया है और यह कभी समस्या नहीं रही। एकमात्र मामला जिसमें CString ctor को फेंकने की संभावना है, वह मेमोरी आवंटित करने में विफल है, और अधिकांश MFC कोड - जिसमें Microsoft द्वारा प्रदान किया गया सामान शामिल है - स्मृति स्थितियों से बाहर नहीं निकलता है जैसा कि आप चाहते हैं। सौभाग्य से अधिकांश पीसी में अब इतनी मेमोरी है कि आपको इसे इस्तेमाल करने के लिए बहुत मेहनत करनी होगी। कोई भी उपयोग जो एक अस्थायी CString उदाहरण उत्पन्न करता है (CString को वापस करने सहित) यह जोखिम चलाता है। यह एक जोखिम / सुविधा व्यापार बंद है। इसके अलावा अगर यह एक संदेश हैंडलर में होता है, तो अपवाद पकड़ा जाएगा।
शिकार 31

इस टिप्पणी में से अधिकांश गलत है, क्षमा करें। "यह मेरे साथ कभी नहीं हुआ" बनाने के लिए एक बहुत कमजोर बिंदु है, खासकर जब आप जानते हैं कि कोड कैसे विफल हो सकता है। या तो किसी प्रक्रिया के लिए आवंटित उपलब्ध स्थान पर मेमोरी की मात्रा का कोई प्रभाव नहीं है। RAM केवल एक प्रदर्शन अनुकूलन है। एनआरवीओ को अनुमति देने के लिए कोड लिखे जाने पर कॉपी-एलिसन एक अस्थायी आवंटन को रोकता है। एक संदेश हैंडलर में अपवादों को पकड़ा गया है या नहीं, यह प्रक्रिया बिटनेस और बाहरी सेटिंग्स पर निर्भर करता है। मैंने एक उत्तर प्रस्तुत किया है जो दिखाता है, कि जोखिम प्रबंधन असुविधा का कारण नहीं बनता है।
IInspectable

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

1
मुझे खेद है कि कोड आपके लिए उपयोगी नहीं है। लेकिन टीबीएच यह मेरी समस्याओं की सूची में काफी कम है।
११:२३ पर पीड़ित

3
void WinErrorCodeToString(DWORD ErrorCode, string& Message)
{
char* locbuffer = NULL;
DWORD count = FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ALLOCATE_BUFFER, NULL, ErrorCode,
    0, (LPSTR)&locbuffer, 0, nullptr);
if (locbuffer)
{
    if (count)
    {
        int c;
        int back = 0;
        //
        // strip any trailing "\r\n"s and replace by a single "\n"
        //
        while (((c = *CharPrevA(locbuffer, locbuffer + count)) == '\r') ||
            (c == '\n')) {
            count--;
            back++;
        }

        if (back) {
            locbuffer[count++] = '\n';
            locbuffer[count] = '\0';
        }

        Message = "Error: ";
        Message += locbuffer;
    }
    LocalFree(locbuffer);
}
else
{
    Message = "Unknown error code: " + to_string(ErrorCode);
}
}

क्या आप कुछ स्पष्टीकरण भी जोड़ सकते हैं?
रॉबर्ट

1
इस कार्यान्वयन के मुद्दे: 1यूनिकोड समर्थन नहीं। 2त्रुटि संदेश का अनुचित स्वरूपण। यदि कॉलर को लौटे स्ट्रिंग को संसाधित करने की आवश्यकता है, तो यह बस ऐसा कर सकता है। आपका कार्यान्वयन कॉलर को बिना किसी विकल्प के छोड़ देता है। 3अपवादों का उपयोग लेकिन उचित अपवाद सुरक्षा की कमी। यदि std::stringऑपरेटर अपवादों को फेंक देते हैं, तो उनके द्वारा आवंटित बफर FormatMessageलीक हो जाता है। कॉल करने वाले के संदर्भ में ऑब्जेक्ट पास करने के बजाय 4बस वापस क्यों नहीं किया जाता है std::string?
IInspectable

1

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

#include <system_error>

std::string GetLastErrorAsString(){
    DWORD errorMessageID = ::GetLastError();
    if (errorMessageID == 0) {
        return std::string(); //No error message has been recorded
    } else {
        return std::system_category().message(errorMessageID);
    }
}

केवल एक छोटी सी खिड़की है जहाँ कॉलिंग GetLastErrorएक सार्थक परिणाम उत्पन्न करती है। यह C ++ होने के साथ, यहां एकमात्र सुरक्षित विकल्प कॉलर को अंतिम त्रुटि कोड प्रदान करना है। यह निश्चित रूप से मदद नहीं करता है, कि कोड GetLastError दो बार कॉल प्रस्तुत करता है । इसके अलावा, सुविधाजनक रहते हुए, C ++ 'व्यापक चरित्र समर्थन की अंतर्निहित कमी error_categoryइंटरफ़ेस को सार्वभौमिक रूप से उपयोगी बनाने में विफल रहती है । यह सिर्फ C ++ के छूटे हुए अवसरों के लंबे इतिहास को जोड़ता है।
IInspectable

बेकार कॉल के बारे में अच्छी बात है GetLastError। लेकिन मुझे GetLastErrorयहां कॉल करने या कॉल करने वाले के बीच अंतर नहीं दिखता है। C ++ और wchar के बारे में: आशा न छोड़ें, Microsoft केवल ऐप्स को UTF-8 की अनुमति देना शुरू कर रहा है ।
क्रॉनिकल

"मुझे कोई अंतर नहीं दिखता है" - निम्नलिखित कॉल साइट पर विचार करें log_error("error", GetLastErrorAsString());:। यह भी विचार करें कि log_errorपहला तर्क प्रकार का है std::stringGetLastErrorआपके द्वारा कॉल किए जा रहे बिंदु पर एक सार्थक मूल्य पर कब्जा करने के लिए (अदृश्य) रूपांतरण कैटर कॉल ने आपकी गारंटी को गिरा दिया ।
IInspectable

आपको क्यों लगता है कि std :: string constructor एक Win32 फ़ंक्शन को कॉल करता है?
शाम

1
mallocHeapAllocप्रक्रिया के लिए कॉल हीप। प्रक्रिया ढेर बढ़ने योग्य है। यदि इसे बढ़ने की आवश्यकता है, तो यह अंततः VirtualAlloc को कॉल करेगा , जो कॉलिंग थ्रेड के अंतिम त्रुटि कोड को सेट करता है । अब वह पूरी तरह से गायब है, जो है: C ++ एक माइनफील्ड है। यह क्रियान्वयन सिर्फ उसी को जोड़ता है, जिसमें निहित गारंटी के साथ एक इंटरफ़ेस प्रदान करके इसे आसानी से पूरा नहीं किया जा सकता है। यदि आप मानते हैं कि कोई समस्या नहीं है, तो प्रस्तावित समाधान की शुद्धता को साबित करना आपके लिए आसान होना चाहिए । सौभाग्य।
IInspectable

0

मैं इसे यहाँ छोड़ दूँगा क्योंकि मुझे बाद में इसका उपयोग करने की आवश्यकता होगी। यह एक छोटे बाइनरी संगत उपकरण के लिए एक स्रोत है जो असेंबली, सी और सी ++ में समान रूप से अच्छी तरह से काम करेगा।

GetErrorMessageLib.c (GetErrorMessageLib.dll के लिए संकलित)

#include <Windows.h>

/***
 * returns 0 if there was enough space, size of buffer in bytes needed
 * to fit the result, if there wasn't enough space. -1 on error.
 */
__declspec(dllexport)
int GetErrorMessageA(DWORD dwErrorCode, LPSTR lpResult, DWORD dwBytes)
{    
    LPSTR tmp;
    DWORD result_len;

    result_len = FormatMessageA (
        FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_ALLOCATE_BUFFER,
        NULL,
        dwErrorCode,
        LANG_SYSTEM_DEFAULT,
        (LPSTR)&tmp,
        0,
        NULL
    );        

    if (result_len == 0) {
        return -1;
    }

    // FormatMessage's return is 1 character too short.
    ++result_len;

    strncpy(lpResult, tmp, dwBytes);

    lpResult[dwBytes - 1] = 0;
    LocalFree((HLOCAL)tmp);

    if (result_len <= dwBytes) {
        return 0;
    } else {
        return result_len;
    }
}

/***
 * returns 0 if there was enough space, size of buffer in bytes needed
 * to fit the result, if there wasn't enough space. -1 on error.
 */
__declspec(dllexport)
int GetErrorMessageW(DWORD dwErrorCode, LPWSTR lpResult, DWORD dwBytes)
{   
    LPWSTR tmp;
    DWORD nchars;
    DWORD result_bytes;

    nchars = dwBytes >> 1;

    result_bytes = 2 * FormatMessageW (
        FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_ALLOCATE_BUFFER,
        NULL,
        dwErrorCode,
        LANG_SYSTEM_DEFAULT,
        (LPWSTR)&tmp,
        0,
        NULL
    );    

    if (result_bytes == 0) {
        return -1;
    } 

    // FormatMessage's return is 1 character too short.
    result_bytes += 2;

    wcsncpy(lpResult, tmp, nchars);
    lpResult[nchars - 1] = 0;
    LocalFree((HLOCAL)tmp);

    if (result_bytes <= dwBytes) {
        return 0;
    } else {
        return result_bytes * 2;
    }
}

इनलाइन संस्करण (GetErrorMessage.h):

#ifndef GetErrorMessage_H 
#define GetErrorMessage_H 
#include <Windows.h>    

/***
 * returns 0 if there was enough space, size of buffer in bytes needed
 * to fit the result, if there wasn't enough space. -1 on error.
 */
static inline int GetErrorMessageA(DWORD dwErrorCode, LPSTR lpResult, DWORD dwBytes)
{    
    LPSTR tmp;
    DWORD result_len;

    result_len = FormatMessageA (
        FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_ALLOCATE_BUFFER,
        NULL,
        dwErrorCode,
        LANG_SYSTEM_DEFAULT,
        (LPSTR)&tmp,
        0,
        NULL
    );        

    if (result_len == 0) {
        return -1;
    }

    // FormatMessage's return is 1 character too short.
    ++result_len;

    strncpy(lpResult, tmp, dwBytes);

    lpResult[dwBytes - 1] = 0;
    LocalFree((HLOCAL)tmp);

    if (result_len <= dwBytes) {
        return 0;
    } else {
        return result_len;
    }
}

/***
 * returns 0 if there was enough space, size of buffer in bytes needed
 * to fit the result, if there wasn't enough space. -1 on error.
 */
static inline int GetErrorMessageW(DWORD dwErrorCode, LPWSTR lpResult, DWORD dwBytes)
{   
    LPWSTR tmp;
    DWORD nchars;
    DWORD result_bytes;

    nchars = dwBytes >> 1;

    result_bytes = 2 * FormatMessageW (
        FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_ALLOCATE_BUFFER,
        NULL,
        dwErrorCode,
        LANG_SYSTEM_DEFAULT,
        (LPWSTR)&tmp,
        0,
        NULL
    );    

    if (result_bytes == 0) {
        return -1;
    } 

    // FormatMessage's return is 1 character too short.
    result_bytes += 2;

    wcsncpy(lpResult, tmp, nchars);
    lpResult[nchars - 1] = 0;
    LocalFree((HLOCAL)tmp);

    if (result_bytes <= dwBytes) {
        return 0;
    } else {
        return result_bytes * 2;
    }
}

#endif /* GetErrorMessage_H */

डायनेमिक usecase (मान लिया गया कि त्रुटि कोड मान्य है, अन्यथा -1 चेक की आवश्यकता है):

#include <Windows.h>
#include <Winbase.h>
#include <assert.h>
#include <stdio.h>

int main(int argc, char **argv)
{   
    int (*GetErrorMessageA)(DWORD, LPSTR, DWORD);
    int (*GetErrorMessageW)(DWORD, LPWSTR, DWORD);
    char result1[260];
    wchar_t result2[260];

    assert(LoadLibraryA("GetErrorMessageLib.dll"));

    GetErrorMessageA = (int (*)(DWORD, LPSTR, DWORD))GetProcAddress (
        GetModuleHandle("GetErrorMessageLib.dll"),
        "GetErrorMessageA"
    );        
    GetErrorMessageW = (int (*)(DWORD, LPWSTR, DWORD))GetProcAddress (
        GetModuleHandle("GetErrorMessageLib.dll"),
        "GetErrorMessageW"
    );        

    GetErrorMessageA(33, result1, sizeof(result1));
    GetErrorMessageW(33, result2, sizeof(result2));

    puts(result1);
    _putws(result2);

    return 0;
}

नियमित उपयोग का मामला (मान लिया गया है कि त्रुटि कोड मान्य है, अन्यथा -1 रिटर्न चेक की आवश्यकता है):

#include <stdio.h>
#include "GetErrorMessage.h"
#include <stdio.h>

int main(int argc, char **argv)
{
    char result1[260];
    wchar_t result2[260];

    GetErrorMessageA(33, result1, sizeof(result1));
    puts(result1);

    GetErrorMessageW(33, result2, sizeof(result2));
    _putws(result2);

    return 0;
}

उदाहरण के लिए असेंबली ग्नू के साथ मिनगॉव 32 (फिर, मान लिया गया कि त्रुटि कोड मान्य है, अन्यथा -1 चेक की आवश्यकता है)।

    .global _WinMain@16

    .section .text
_WinMain@16:
    // eax = LoadLibraryA("GetErrorMessageLib.dll")
    push $sz0
    call _LoadLibraryA@4 // stdcall, no cleanup needed

    // eax = GetProcAddress(eax, "GetErrorMessageW")
    push $sz1
    push %eax
    call _GetProcAddress@8 // stdcall, no cleanup needed

    // (*eax)(errorCode, szErrorMessage)
    push $200
    push $szErrorMessage
    push errorCode       
    call *%eax // cdecl, cleanup needed
    add $12, %esp

    push $szErrorMessage
    call __putws // cdecl, cleanup needed
    add $4, %esp

    ret $16

    .section .rodata
sz0: .asciz "GetErrorMessageLib.dll"    
sz1: .asciz "GetErrorMessageW"
errorCode: .long 33

    .section .data
szErrorMessage: .space 200

परिणाम: The process cannot access the file because another process has locked a portion of the file.


1
यह वास्तव में कुछ भी उपयोगी नहीं जोड़ता है। इसके शीर्ष पर, यह FormatMessageबिना किसी स्पष्ट कारण के ANSI संस्करण को कॉल करता है , और मनमाने ढंग से खुद को 80 वर्णों तक सीमित करता है, फिर से, बिना किसी कारण के। मुझे डर है, यह मददगार नहीं है।
IInspectable

आप सही हैं मैं उम्मीद कर रहा था कि कोई भी यूनिकोड संस्करण की कमी के बारे में ध्यान नहीं देगा। मैं जाँचता हूँ कि कैसे गन्नू में एक यूनिकोड स्ट्रिंग को परिभाषित किया जाए और मेरे समाधान को संशोधित किया जाए। बेईमानी के बारे में खेद है।
दिमित्री

ठीक यूनिकोड संस्करण है। और यह मनमाना कारण नहीं है; सभी त्रुटि संदेश या तो 80 वर्णों से कम हैं, या पढ़ने योग्य नहीं हैं, और त्रुटि कोड त्रुटि संदेश की तुलना में अधिक महत्वपूर्ण है। कोई मानक त्रुटि संदेश नहीं है जो कि 80 वर्णों से अधिक है, इसलिए यह एक सुरक्षित धारणा है, और जब ऐसा नहीं होता है, तो यह मेमोरी को लीक नहीं करता है।
दिमित्री

3
"सभी त्रुटि संदेश या तो 80 वर्णों से कम हैं, या पढ़ने के लायक नहीं हैं" - यह गलत है। के लिए त्रुटि संदेश ERROR_LOCK_VIOLATION(33) है: "प्रक्रिया फ़ाइल तक नहीं पहुँच सकती क्योंकि किसी अन्य प्रक्रिया ने फ़ाइल के एक हिस्से को लॉक कर दिया है।" यह दोनों स्पष्ट रूप से 80 वर्णों से अधिक लंबा है, और बहुत अधिक पढ़ने लायक है, यदि आप किसी मुद्दे को हल करने की कोशिश कर रहे हैं और यह निदान लॉग फ़ाइल में मिल रहा है। यह उत्तर मौजूदा उत्तरों पर कोई पर्याप्त मूल्य नहीं जोड़ता है।
IInspectable

यह कोई कम भोली नहीं है। यह अक्सर कम ही विफल होता है। विधानसभा कार्यान्वयन को छोड़कर जो बफर आकार के बारे में है (200 वर्णों के लिए कमरा होने का दावा है, जब इसमें केवल 100 के लिए कमरा है)। फिर से, यह कुछ भी पर्याप्त नहीं जोड़ता है, यह पहले से ही किसी भी अन्य उत्तर में नहीं है। विशेष रूप से, यह इस उत्तर से भी बदतर है । वहां बुलेट सूची देखें और निरीक्षण करें, जो आपके प्रस्तावित कार्यान्वयन को जारी करता है, उन शीर्ष पर जो मैंने अभी बताया था।
IInspectable
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.