2011-12-29 16 views
5

मैं mingw g ++ 4.6.1 के साथ -O0, WinXP SP2 का उपयोग कर रहा हूं।GetLastError() 0 या 2 को वापस कैसे कहा जाता है इस पर निर्भर करता है?

Minimal working example is here.

जी ++ --with-dwarf2 --disable-sjlj-अपवादों के साथ कॉन्फ़िगर किया गया है।

GetLastError() रिटर्न 0 या कैसे अपवाद फेंक दिया जाता है पर 2 depeding:

throw runtime_error(error_message()); 

फर्जी "त्रुटि कोड: 0" छपा है, और

const string msg = error_message(); 

throw runtime_error(msg); 

प्रिंट "त्रुटि कोड: 2" के रूप में अपेक्षित होना।

सबसे पहले, मैंने सोचा कि GetLastError() दो बार आह्वान किया गया है लेकिन डिबगिंग से पता चलता है कि यह बिल्कुल एक बार लागू किया जाता है।

क्या चल रहा है?

+0

यदि आप 'error_message (GetLastError())' कॉल करने के लिए अपना कोड संशोधित करते हैं तो क्या होता है? –

+0

आप निश्चित रूप से महसूस करते हैं कि "GetLastError()", C++ कोशिश/पकड़ अपवाद, और Win32 "संरचित अपवाद हैंडलिंग" (एसईएच) सभी तीन अलग-अलग (संबंधित, लेकिन अलग) चीजें हैं, है ना? आप आम तौर पर एक या दूसरे का उपयोग करते हैं, लेकिन आप * एक दूसरे के साथ संयोजन के साथ * आम तौर पर * एक साथ * का उपयोग नहीं करना चाहिए। – paulsm4

+0

@GregHewgill मैं GetLastError() को छिपाना चाहता हूं, और मैं इसके बजाय error_message() का उपयोग करना चाहता हूं। – Ali

उत्तर

9

ऐसा नहीं है कि कोड है कि एक throw कहीं अंदर ही Win32 एपीआई फ़ंक्शन को कॉल सेट, कि 0. यह करने के लिए अंतिम-त्रुटि मान रीसेट करता है error_message() करने के लिए अपने कॉल से पहले हो रहा हो सकता है संभव है।

कॉलिंग GetLastError() स्वचालित रूप से अंतिम-त्रुटि मान को 0 पर रीसेट कर देता है, इसलिए दो बार कॉल करना सुरक्षित है।

चाहे आपका कंपाइलर/रनटाइम कोड उत्पन्न करता है जो Win32 API फ़ंक्शन को कॉल करता है, वह आपके विशिष्ट रनटाइम पर होगा। , अभी तक बेहतर

const string msg = error_message(); 
throw runtime_error(msg); 

अपने कोड के भविष्य के पाठकों के लिए यह GetLastError()error_message() बाहर कॉल करने के लिए उपयोगी होगा:

const string msg = error_message(GetLastError()); 
throw runtime_error(msg); 
उसे सुरक्षित रखने और नहीं इस पर निर्भर करती है, दो बयान संस्करण का उपयोग करने के लिए

इस तरह, पाठक संबंधित Win32 API कॉल के तुरंत बाद GetLastError() कॉल देखेंगे, जहां यह संबंधित है।

8

यदि आप असेंबली कोड जेनरेट करते हैं, तो यह स्पष्ट हो जाता है कि क्या हो रहा है। निम्नलिखित सी ++ कोड:

hDevice = CreateFileA(path, // drive to open 
    // etc... 
    ); 

if (hDevice == INVALID_HANDLE_VALUE) // cannot open the drive 
{                
    throw runtime_error(error_message()); 
} 

(कम से कम डिफ़ॉल्ट अनुकूलन का उपयोग करके) विधानसभा कोड का एक खंड उत्पन्न करता है:

call [email protected] # 
LEHE4: 
    sub esp, 28 #, 
    mov DWORD PTR [ebp-12], eax # hDevice, D.51673 
    cmp DWORD PTR [ebp-12], -1 # hDevice, 
    jne L5 #, 
    mov DWORD PTR [esp], 8 #, 

    call ___cxa_allocate_exception # // <--- this call is made between the 
              # // CreateFile() call and the 
              # // error_message() call 

    mov ebx, eax  # D.50764, 
    lea eax, [ebp-16] # tmp66, 
    mov DWORD PTR [esp], eax  #, tmp66 
LEHB5: 
    call __Z13error_messagev # 

आप एक कॉल करने के लिए बनाया ___cxa_allocate_exception देखने के अपवाद के लिए कुछ स्मृति ब्लॉक आवंटित करने के लिए फेंके । वह फ़ंक्शन कॉल GetLastError() स्थिति बदल रहा है।

जब सी ++ कोड लगता है:

hDevice = CreateFileA(path, // drive to open 
    // etc... 
    ); 

if (hDevice == INVALID_HANDLE_VALUE) // cannot open the drive 
{                
    const string msg = error_message(); 

    throw runtime_error(msg); 
} 

तो आप निम्नलिखित उत्पन्न विधानसभा मिलती है:

call [email protected] # 
    sub esp, 28 #, 
    mov DWORD PTR [ebp-12], eax # hDevice, D.51674 
    cmp DWORD PTR [ebp-12], -1 # hDevice, 
    jne L5 #, 
    lea eax, [ebp-16] # tmp66, 
    mov DWORD PTR [esp], eax  #, tmp66 
    call __Z13error_messagev # 
LEHE4: 
    sub esp, 4 #, 
    mov DWORD PTR [esp], 8 #, 

    call ___cxa_allocate_exception # // <--- now this happens *after* 
             //  error_message() has been called 

जो विफल रहा है CreateFile() कॉल और error_message() करने के लिए कॉल के बीच एक बाहरी फ़ंक्शन को कॉल नहीं करता ।

इस तरह की समस्या कुछ वैश्विक स्थिति जैसे GetLastError() या errno का उपयोग करके त्रुटि प्रबंधन के साथ मुख्य समस्याओं में से एक है।

+0

+1 आपके प्रयासों के लिए धन्यवाद। इस मुद्दे को हल करने के लिए एक साफ समाधान का सुझाव दे सकते हैं? या ग्रेग हेगिल का जवाब सबसे अच्छा है जो हम कर सकते हैं? – Ali

+0

ग्रेग हेगिल का जवाब इस तक पहुंचने का सही तरीका है (यह जवाब वास्तव में ग्रेग के बिंदु को ठोस बनाने की कोशिश कर रहा था)। चूंकि आपके पास वास्तव में नियंत्रण नहीं है कि एक कंपाइलर 'फेंक' (या उस मामले के लिए कोई अन्य कथन) को लागू करने के लिए क्या करता है, तो आपके पास 'GetLastError() 'प्राप्त करने के मिश्रण में' फेंक 'कथन नहीं दिया जा सकता है। स्थिति। आपको 'फेंक' से पहले सख्ती से 'GetLastError() 'करना होगा। –

+0

+1 विश्लेषण के लिए धन्यवाद। –

संबंधित मुद्दे