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



winapi (7)

एमएसडीएन में कुछ नमूना कोड है जो दर्शाता है कि FormatMessage() और GetLastError() एक साथ कैसे उपयोग करें: अंतिम त्रुटि कोड पुनर्प्राप्त करना

https://src-bin.com

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

GetLastError() एक पूर्णांक मान देता है, न कि एक टेक्स्ट संदेश।

धन्यवाद।


Answer #1

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

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 */

गतिशील उपयोग (माना जाता है कि त्रुटि कोड मान्य है, अन्यथा एक -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;
}

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

    .global [email protected]

    .section .text
[email protected]:
    // eax = LoadLibraryA("GetErrorMessageLib.dll")
    push $sz0
    call [email protected] // stdcall, no cleanup needed

    // eax = GetProcAddress(eax, "GetErrorMessageW")
    push $sz1
    push %eax
    call [email protected] // 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.


Answer #2

यदि आपको एमबीसीएस के साथ-साथ यूनिकोड का समर्थन करने की आवश्यकता है, तो श्री सी 64 का जवाब काफी पर्याप्त नहीं है। बफर को टीसीएचएआर घोषित किया जाना चाहिए, और एलपीटीस्ट्रस्ट को डाला जाना चाहिए। ध्यान दें कि यह कोड परेशान नई लाइन से निपटता नहीं है जो माइक्रोसॉफ्ट त्रुटि संदेश में जोड़ता है।

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

Answer #3

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

एमएसडीएन documentation :

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

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

Answer #4

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

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

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

#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) { ::HeapFree(::GetProcessHeap(), 0, 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 तक पहला पैरामीटर बदलें। शेष कोड अपरिवर्तित बनी रह सकती है।

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

  • पूर्ण नमूना कोड, केवल कॉल करने के लिए एपीआई का संदर्भ नहीं।
  • सी और सी ++ कार्यान्वयन दोनों प्रदान करता है।
  • यूनिकोड और एमबीसीएस परियोजना सेटिंग्स दोनों के लिए काम करता है।
  • इनपुट पैरामीटर के रूप में त्रुटि कोड लेता है। यह महत्वपूर्ण है, क्योंकि थ्रेड का अंतिम त्रुटि कोड केवल परिभाषित बिंदुओं पर मान्य है। एक इनपुट पैरामीटर कॉलर को दस्तावेज अनुबंध का पालन करने की अनुमति देता है।
  • उचित अपवाद सुरक्षा लागू करता है। अपवादों का पूरी तरह से उपयोग करने वाले अन्य सभी समाधानों के विपरीत, यह कार्यान्वयन वापसी मूल्य के निर्माण के दौरान अपवाद को फेंकने पर स्मृति को रिसाव नहीं करेगा।
  • FORMAT_MESSAGE_IGNORE_INSERTS ध्वज का उचित उपयोग। अधिक जानकारी के लिए blogs.msdn.microsoft.com/oldnewthing/20071128-00/?p=24353 देखें।
  • उचित त्रुटि हैंडलिंग / त्रुटि रिपोर्टिंग, कुछ अन्य उत्तरों के विपरीत, जो चुपचाप त्रुटियों को अनदेखा करते हैं।

यह उत्तर स्टैक ओवरफ़्लो दस्तावेज़ीकरण से शामिल किया गया है। निम्नलिखित उपयोगकर्ताओं ने उदाहरण में योगदान दिया है: stackptr , Ajay , कोडी ग्रे ♦ , IInspectable ।


Answer #5

कुछ टिप्पणियों को ध्यान में रखकर अपडेट किया गया (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);

Answer #6
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);
}
}




winapi