2016-05-31 8 views
2

Ich habe eine Reihe von Exception-Klassen für Win32API und MFC, die den aktuellen Win32-Fehlercode (GetLastError()) erfassen und formulieren eine menschenlesbare Nachricht, wo es aufgetreten ist.Welche Regeln gelten für die Standardzuordnung von Argumenten?

Ich verlasse mich auf die Fähigkeit, den aktuellen Fehlercode zu erfassen, bevor der ctor ihre Arbeit zu tun beginnt, unter der Annahme, dass die Argumente für den ctor sicher gelöst werden müssen, bevor der ctor aufgerufen wird.

Aber ich habe Probleme in meinem aktuellen Build, die Compilation Tooling von 120_xp auf 120 in VS2013 geschaltet (Ich bin nicht 100% sicher, dass dies die Quelle der Änderung ist - es könnte für einige Zeit ruhen unabhängig von der Änderung des Plattform-Toolsets).

Allerdings hätte ich gedacht, dass nichts davon relevant ist - dass C++ würde erfordern, dass alle Argumente zuerst aufgelöst werden, dann wird der Funktionsaufruf ausgeführt, so dass das Standardargument error unten immer den aktuellen Fehlercode zuvor hätte die ctor Aufruf (die es möglicherweise ändern könnte):

CWin32APIErrorException::CWin32APIErrorException(const CString & source, const CString & api, DWORD error /*= GetLastError()*/) 
    : CContextException(FormatString(_T("In %s, %s() returned "), source, api), error) 
    , m_source(source) 
    , m_api(api) 
{ 

} 

Hier ist der Aufruf-Kontext:

m_file = CreateFile(filename, FILE_GENERIC_READ, FILE_SHARE_READ, nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr); 
if (!m_file) // NOTE: m_file is smart handle and it's operator bool() knows that the invalid file handle is "false" 
    throw CWin32APIErrorException(__FUNCTION__, _T("CreateFile"), GetLastError()); 

Wenn ich den Aufruf-Kontext die Erfassung des aktuellen Fehlercode zu zwingen, zu ändern, die ich in der Tat zu tun bekommen 2 Fehler:

m_file = CreateFile(filename, FILE_GENERIC_READ, FILE_SHARE_READ, nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr); 
if (!m_file) 
{ 
    auto error = GetLastError(); 
    throw CWin32APIErrorException(__FUNCTION__, _T("CreateFile"), error); 
} 

FormatString ist einfach, und es bewahrt den aktuellen Fehlercode:

CStringW FormatString(const wchar_t * format, ...) 
{ 
    const DWORD dwError = ::GetLastError(); 
    va_list args; 
    va_start(args, format); 
    CStringW str; 
    str.FormatV(format, args); 
    ::SetLastError(dwError); 
    return str; 
} 

Das Problem, das ich erlebt habe, ist, dass ich sehe Fehler 122: Puffer zu Systemaufruf zugeführt wird, zu klein. Der Fehlercode zum Aufrufen dieser Ausnahme lautet jedoch Fehler 2: Datei nicht gefunden.

Ich würde weit bevorzugen, dass meine Software-Bericht "Datei nicht gefunden", die richtiger und umsetzbar ist.

Also: 1. Bin ich falsch über C++ garantieren, dass alle Argumente gelöst werden, bevor der Funktionsaufruf (der Ctor) aufgerufen wird? 2. Wo sonst Anrufe gemacht werden könnten bekommen, dass der aktuellen Fehlercode (CString Ctor (n) sind die einzigen sind andere Dinge vor dem CWin32APIErrorException::CWin32APIErrorException() Ctor aufgerufen ändern könnte.

Was bedeutet, dass CString Ctor offenbar die aktuellen Fehler zu ändern? ! igitt

Kann mir jemand mitteilen, wo ich in meinem Verständnis falsch bin

Danke

+4

Die Reihenfolge der Parameterauswertung nicht spezifiziert ist, also ja, die CString Konstruktor kann in die Quere kommen. –

+0

Danke Raymond. Ich hatte nur eine Ahnung - und es stellt sich heraus, dass wir, weil wir Unicode kompilieren - die '__FUNCTION__' in eine breite Zeichenfolge im CString ctor konvertiert wird, was etwas auslöst, das intern zum Fehler 122 führt. :( – Mordachai

+0

Dies wäre relativ einfach zu beheben. Sie übergeben String-Literale an den Konstruktor, so dass es keine 'CString'-Parameter benötigen. Verwenden Sie einfach' LPCTSTR'. (Verwenden Sie den Präprozessor, um das Makro '__FUNCTION__' in eine umzuwandeln wide string.) Das löst das Problem, dass beliebiger Code ausgeführt wird, um die Parameter zu konstruieren. Haben Sie andere Aufrufkontexte, in denen dynamisch generierte Strings übergeben werden müssen? –

Antwort

1

ich dies hier im Fall verlassen werde es andere mit einem ähnlichen Problem hilft?!:

In meinem Fall oben, kompilierte ich UNICODE, und mein CWin32APIErrorException::CWin32APIErrorException(const CString & source, ... wurde unter Verwendung throw CWin32APIErrorException(__FUNCTION__, ... aufgerufen, der dann CStringW(const char *) Konstruktor anrief, der intern MultiByteToWideChar() aufruft, das das Muster des Anrufs zuerst mit Nullpuffer verwendet, um die erforderliche Größe des Puffers zu erhalten, Rufen Sie dann ein zweites Mal mit dem dieser Größe zugewiesenen Puffer auf.

Der erste Aufruf setzt also immer den aktuellen Fehler auf 122: Puffer zu klein.

Daher das Verhalten, das ich sah.

Wir haben dieses Problem nicht sehen, wenn für _MBCS, da die __FUNCTION__ wörtlichen Kompilieren nicht intern umgewandelt werden - die CStringA(const char *) nicht den aktuellen Fehlercode zu überschreiben, da keine Konvertierung notwendig war.

Es könnte sein, dass jederzeit CString Konstruktor könnte die Nebenwirkung der Änderung der aktuellen Fehler für jede andere Reihe von Gründen haben ... so CWin32APIErrorException nehmen jede Art von konstruierten Objekttyp bedeutet, dass es von sich aus ist anfällig für den falschen Fehlercode, wenn der Anrufer stellt sicher, dass es zu erfassen ersten und passieren auf dem erfassten Wert zu bekommen:

if (!SomeWinAPIFunction(...)) 
{ 
    auto error = GetLastError(); // make sure we report the correct error! 
    throw CWin32APIErrorException(__FUNCTION__, "SomeWinAPIFunction", error); 
} 

es ist möglich, die CWin32APIErrorException ctors zu ändern const char * zu nehmen und separat const wchar_t * zu vermeiden, dass der CString Bau stattfinden, bevor Sie den aktuellen Fehlercode erfassen.

Das Problem bei diesem Ansatz besteht darin, dass sich die Permutationen schnell addieren (eng, schmal), (schmal, breit), (breit, schmal), (breit, breit). Und das ist nur für zwei Argumente. Meine tatsächliche Klasse hat drei (eine optionale für die Argumente).

Und das letzte ist am besten als CString übrig, weil es sehr wahrscheinlich zur Laufzeit dynamisch erstellt wird, nicht zur Kompilierzeit bekannt, im Gegensatz zu den ersten beiden Argumenten. Es wird also sehr wahrscheinlich die Möglichkeit geben, den aktuellen Fehlercode zu ändern, selbst wenn die ctor-Schnittstelle ein einfaches char * oder wchar_t * erfordert ... was bedeutet, dass dieses Problem nicht gelöst ist.

Letztendlich war es mir nicht möglich, den aktuellen Fehler zu erfassen, bevor die anderen für den Fehlerbericht erforderlichen Argumente in einer Weise erfasst wurden, die garantiert funktioniert. Daher habe ich die Schnittstelle geändert, um explizite Argumente zu verwenden (keine Standardwerte), um mich dazu zu zwingen, unseren gesamten Code durchzugehen und sicherzustellen, dass wir den richtigen Fehlercode auf der Aufrufseite erfassen und diesen übergeben.

Zum Beispiel:

if (!::DestroyAcceleratorTable(m_hAccel)) 
     if (!m_hAccel) 
     { 
      auto error = GetLastError(); 
      throw CWinAPIErrorException(__FUNCTION__, "DestroyAcceleratorTable", FormatString(_T("%X"), m_hAccel), error); 
     } 
+0

Eine kurze Anmerkung: Ich hätte '_T (__ FUNCTION __)' verwenden können, um '' zu vermeiden CString' Conversion ctor, und es hätte genauso gut (und schlecht) funktioniert wie beim 'MBCS' Kompilierungsmodell.Ich nahm jedoch an, dass es höchste Zeit war, dies tiefer anzugehen, indem Anrufer dazu gezwungen wurden, sich des Problems bewusst zu sein (obwohl ich ihnen alles vorenthalten hätte, wenn ich einen narrensicheren Mechanismus gefunden hätte). – Mordachai

Verwandte Themen