2013-03-12 4 views
5

Ich habe einige Codes, der wie folgt aussieht:brauchen ein robustes Fehlersystem für eine Win32-GUI-Anwendung

void MyClass::OnButtonClick() 
{ 
    int retVal = SomeDialog.DoModal(); 
    if(retVal == MYCLASS_ERROR) 
    { 
     MessageBox("Error"...blah ...blah); 
    } 
    else if(retVal == IDOK) // IDOK is returned on clicking the 'OK' button 
    { 
     MessageBox("All is well"...blah ...blah); 
    } 
} 
  • SomeDialog nur zeigt einen Fortschrittsbalken. Bei jedem Fehler wird der Fortschrittsbalken automatisch durch den Aufruf von EndDialog(MYCLASS_ERROR) geschlossen. Nur bei erfolgreichem Abschluss kann der Benutzer dort auf "OK" klicken.
  • MYCLASS_ERROR ist ein Wert in einem enum, der alle Arten von Rückgabetypen und Status enthält.

Ich fand, dass beim Klicken auf OK in SomeDialog die Fehlermeldung immer noch angezeigt wird! Ich grub ein wenig tiefer und fand, dass MYCLASS_ERROR = IDOK = 1.

Also meine Frage ist, wie soll ich all diese Rückkehr Zustände so definieren, dass sie nicht mit anderen Implementierung des Status kollidiert? Bedeutung, meine Funktionen sollten Werte zurückgeben, die nicht von anderen anderen Funktion zurückgegeben werden (oder so wenige andere Funktionen wie möglich).

Ich dachte an mein Design so zu ändern, dass alle Funktionen nur TRUE oder FALSE zurückgaben. Dies wäre jedoch nicht in allen Fällen möglich. Ich habe auch einiges nach Antworten gesucht und bisher noch keine gefunden.

Danke fürs schauen!

Antwort

3

Es gibt Möglichkeiten, dies zu umgehen, aber sie sind irgendwie hässlich. Das sauberste Design ist normalerweise dasjenige, das das Zusammenstoßen mit Standard Win32-Idiomen vermeidet, und in der Welt von Win32 geben modale Dialoge einen der ID Werte zurück, um die Schaltfläche anzugeben, auf die der Benutzer geklickt hat, wenn er sie abbrach (genau wie Nachrichtenfelder)). Streng genommen ist es der nResult Parameter, der an die EndDialog Funktion übergeben wurde, die zum Schließen des modalen Dialogfelds verwendet wird.

Ich empfehle nicht zu versuchen, diesen Rückgabewert mit zusätzlicher Bedeutung zu überladen. Der Versuch, dies zu tun, führt Sie nur in Schwierigkeiten (zum Beispiel haben Sie wahrscheinlich auch noch nicht bemerkt, dass ein Rückgabewert von -1 bedeutet, dass die Erstellung des Dialogfelds fehlschlug).


Definieren Sie stattdessen eine zusätzliche Elementvariable in Ihrer Dialogklasse und verwenden Sie diese, um Ihre zusätzlichen Informationen zu melden. Bei Erfolg geben Sie IDOK aus dem Dialogfeld zurück. Bei einem Fehler (beliebig Art des Fehlers), geben Sie etwas wie IDCANCEL zurück. Ihr Code innerhalb von OnButtonClick würde dann prüfen, ob der Rückgabewert IDOK oder IDCANCEL ist. Wenn es IDCANCEL ist, müssen Sie auch den Wert der Mitgliedsvariablen abfragen, die Sie dem Dialog für zusätzliche Information hinzugefügt haben.

Hoffentlich machte das Sinn.Wenn nicht, vielleicht wird dieser Code Beispiel (unter der Annahme, dass m_errStatus ist die Membervariable Sie Ihre Unterklasse von CDialog hinzugefügt):

void MyClass::OnButtonClick() 
{ 
    if (SomeDialog.DoModal() == IDOK) 
    { 
     // Success! 
     // The OK button was clicked, so IDOK was returned. 
     MessageBox("All is well"...blah ...blah); 
    } 
    else 
    { 
     // Failure! 
     // Some error occurred, so IDCANCEL (or any other value) was returned. 
     // Determine what to do now, based on additional information that the 
     // dialog set upon failure. 
     switch (SomeDialog.m_errStatus) 
     { 
      case MYCLASS_ERROR_1: 
       MessageBox("Error 1 occurred."); 
       break; 
      case MYCLASS_ERROR_2: 
       MessageBox("Error 2 occurred."); 
       break; 
      // etc. 
     } 
    } 
} 
+0

Hmm das klingt am besten. Aber was ist mit anderen Funktionen? Zum Beispiel Worker-Threads, Hilfsfunktionen usw. Welche Art von Werten sollten sie zurückgeben? Danke für deine Antwort! –

+0

@Anish: Hmm, das ist ein bisschen härter. Sie könnten wahrscheinlich damit fertig werden, einen Ihrer aufgezählten Fehlercodes direkt zurückzugeben, da es für ihre Rückgabewerte noch kein Standard-Idiom gibt. Aber die Fehlerbehandlung über Return-Codes ist ziemlich schwierig zu bekommen, und dies ist nur einer der Gründe. Ehrlich gesagt, ich frage mich, ob Sie nicht nur Ausnahmen verwenden sollten. Das ist schließlich C++, nicht C. –

+0

Ich würde Ausnahmen verwenden, aber ich bin tatsächlich Schnittstelle mit vielen DLLs in C codiert. Also dachte ich, ich würde versuchen, die Dinge einheitlich zu machen. Aber leider scheint es nicht so gut zu funktionieren. –

0

Sie können enum anstelle von macro-defined-Returncode mit Namespace verwenden, um Namenskonflikte zu vermeiden. Schauen Sie sich die Boost Error Handling Policy:

namespace boost { namespace math { namespace policies { 

enum error_policy_type 
{ 
    throw_on_error = 0, // throw an exception. 
    errno_on_error = 1, // set ::errno & return 0, NaN, infinity or best guess. 
    ignore_error = 2, // return 0, NaN, infinity or best guess. 
    user_error = 3 // call a user-defined error handler. 
}; 

}}} // namespaces 
+0

Sein Problem ist nicht Namenskonflikte, es ist * Wert * Konflikte. Wenn Sie 'error_policy_type :: errno_on_error' auf 1 setzen, wird es immer noch mit' IDOK' kollidieren, was ebenfalls 1 entspricht. –

+0

Sie können eine enum ohne Wert deklarieren und so verwenden wie sie ist. [Pro und Contra] (http://stackoverflow.com/questions/4623452/the-use-of-enum-without-a-variable-name) – lucasg

+0

Das ist hier irrelevant. Das Deklarieren von Enums ohne explizite Werte bedeutet lediglich, dass der Compiler sie automatisch zuweist. In 99,99% der Implementierungen wird es genau das tun, was Sie erwarten, und mit 0 beginnen und um 1 erhöhen. –

1

Sie einfach Ihre benutzerdefinierten Fehlercodes definieren könnten nicht „kollidieren“ mit den Windows-Rückgabewerten . Natürlich wissen Sie nicht, wann Microsoft neue Rückgabewerte hinzufügen wird, also wird dies immer ein bisschen flakey sein.

Man könnte so etwas wie dies versucht:

enum MYERR 
{ 
    MYERR_FIRST_ERROR = 0x0F000000, /* large and unlikely to be used */ 
    MYERR_SECOND_ERROR, 
    MYERR_THIRD_ERROR, 
    /* and so on */ 
}; 

Aber, wie Cody Grau oben bemerkt, ich glaube, du bist besser dran mit einem Standard-Fehlercode aus DoModal Rückkehr, wie IDABORT stattdessen die Rückgabecodes von Überlastung so wie das. Dann haben Sie einfach einen sekundären Fehlercode, den Benutzer explizit abrufen müssen, was Ihr eigener "interner" Fehlercode ist.

Verwandte Themen