Überprüfen des Quellcodes für WPF-Beispiele von Microsoft

    Um den PVS-Studio Code Analyzer, der neben C ++ auch das Überprüfen von C # -Projekten gelernt hat, bekannt zu machen, haben wir uns entschlossen, den WPF-Quellcode der von Microsoft angebotenen Beispiele zu überprüfen.



    Mit der Veröffentlichung von Windows Vista wurde ein neues System zum Erstellen schöner Clientanwendungen eingeführt - die Windows Presentation Foundation (WPF). Dieses Grafiksubsystem ist seit Version 3.0 in .NET Framework enthalten. Es verwendet die XAML-Markup-Sprache und hat die veralteten WinForms ersetzt. Meiner Meinung nach bestand der Hauptnachteil von WinForms darin, dass die gesamte Zeichnung auf dem Zentralprozessor ausgeführt wurde. WPF agierte logischer und zeichnete seine DirectX-Komponenten. Jetzt hat WPF WinForms fast abgelöst und Sie können universelle Schnittstellen für drei Plattformen gleichzeitig erstellen (PC, XBOXOne, Winphone).

    Um den Quellcode der WPF-Beispiele von Microsoft ( Quelle ) zu analysieren , haben wir den statischen Code-Analysator von PVS-Studio Version 6.05 verwendet .

    Ein interessantes Merkmal dieser Lösung ist, dass sie neben C # -Projekten auch Projekte enthält, die in C ++ geschrieben wurden. Ich habe von dieser Funktion nur durch die Liste der von PVS-Studio gefundenen Fehler erfahren. Das PVS-Studio-Plug-In für Visual Studio wurde ohne zusätzliche Manipulationen durch den Benutzer analysiert und in Nachrichten angezeigt, die sich sowohl auf C # - als auch auf C ++ - Code beziehen.

    Abbildung 1. Das PVS-Studio-Fenster zeigt gleichzeitig Warnungen in Bezug auf C # - und C ++ - Code des überprüften Projekts an.


    Abbildung 1. Das PVS-Studio-Fenster zeigt gleichzeitig Warnungen in Bezug auf C # - und C ++ - Code des getesteten Projekts an (klicken Sie zum Vergrößern auf das Bild).

    C # -Fehler


    1. Fehler beim Kompilieren der Bedingungen einer if-Anweisung

    Fehler beim Vergleichen von Dingen mit Dingen sind unter Programmierern ein sehr häufiges Problem. Gehen wir sie durch.

    In diesem Quellcode gibt es zwei absolut identische Bedingungen:
    public int Compare(GlyphRun a, GlyphRun b)
    {
      ....
      if (aPoint.Y > bPoint.Y) //<==
      {
        return -1;
      }
      else if (aPoint.Y > bPoint.Y) //<==
      {
        result = 1;
      }
      else if (aPoint.X < bPoint.X)
      {
        result = -1;
      }
      else if (aPoint.X > bPoint.X)
      {
        result = 1;
      }
      ....
    }

    V3003 Die Verwendung des Musters 'if (A) {...} else if (A) {...}' wurde erkannt. Es ist wahrscheinlich, dass ein logischer Fehler vorliegt. Check lines: 418, 422. txtserializerwriter.cs 418

    Was genau sie im zweiten Fall vergleichen wollten, ist nicht ganz klar, aber eindeutig nicht das, was sie geschrieben haben.

    Wir schreiben Umfragen für null ohne eine Spur in den Bedingungen, als wir das Programm vor Notsituationen schützen. Wir können sagen, dass die meisten if- Bedingungen genau auf Null von Feldern oder Variablen überprüft werden. Gleichzeitig sind manchmal Prüfungen nicht nur unnötig, sondern können auch einen logischen Fehler enthalten:
    public static string FindNumeric(string content)
    {
      string[] values = content.Split(' ');
      if (values != null)
      {
        return values[0];
      }
      return "none";
    }

    V3022 Der Ausdruck 'values! = Null' ist immer wahr. Util.cs 287

    Wir können davon ausgehen, dass der Autor überprüfen wollte, ob Werte mehr als 0 Elemente enthalten, aber ich konnte mir keine Situation vorstellen , in der Split ein leeres Array zurückgibt. Auf jeden Fall war es völlig überflüssig, hier nach null zu suchen.

    Wie gesagt, das Projekt enthält Code aus C ++ und C # -Diagnose. Ich hatte den Eindruck, dass es der C ++ - Programmierer war, der am nächsten Code beteiligt war.
    private void LoadNotes()
    {
      var fs = new FileStream("NotesFile", FileMode.Open);
      if (fs != null)
      {
        ....
    }

    V3022 Der Ausdruck 'fs! = Null' ist immer wahr. MainWindow.cs 66

    Tatsächlich ist diese Option selbst in C ++ fehlerhaft, aber in C # sieht sie im Allgemeinen „seltsam“ aus. Warum es so falsch ist, in C ++ zu schreiben, wird im Artikel " Überprüfen des Quellcodes von 7-Zip mit PVS-Studio " beschrieben, aber wir werden weiterhin C # -Code analysieren.

    Wir konnten uns solchen Situationen nicht entziehen. Die Lösung hat zwei nahezu identische Funktionen (dank „Kopieren-Einfügen“) mit demselben Fehler gefunden:
    private void SerializeObjectTree(object objectTree)
    {
      TextWriter writer = new StreamWriter(_stream);
      try
      {
        string fileContent =
         XamlRtfConverter.ConvertXamlToRtf(
             XamlWriter.Save(objectTree));
        writer.Write(fileContent);
      }
      finally
      {
        if (writer != null)
          writer.Close();
      }
    }

    V3022 Der Ausdruck 'writer! = Null' ist immer wahr. htmlserializerwriter.cs 324

    Nun, ein Writer ist keine Nullreferenz ...

    Ein Fehler in Ausnahmesituationen zu werfen ist keine schlechte Entscheidung. Die Hauptsache ist, keinen Fehler in der Bedingung zu machen, wenn Sie eine Ausnahme auslösen müssen, da Sie für den Benutzer Ihrer Anwendung einen sehr unangenehmen Eindruck hinterlassen können, wenn das Programm aus heiterem Himmel abstürzt.
    protected Size SizeParser(string o)
    {
      ....
      if (sizeString.Length == 0 || sizeString.Length != 2)
       throw new ApplicationException("Error: a size should 
               contain two double that seperated by a space 
               or ',' or ';'");
      ....
    }

    V3023 Betrachten Sie die 'sizeString.Length == 0 || sizeString.Length! = 2 'Ausdruck. Der Ausdruck ist zu groß oder enthält einen Druckfehler. MainWindow.cs 140

    Nach dem Text des Fehlers zu urteilen, ist es in diesem Fall nicht erforderlich , nach 0 zu suchen. Es genügte, sizeString.Length auf Ungleichung 2 zu prüfen .

    In den langen Körpern der if-Anweisung ist es sehr schwierig, sinnlose Prüfungen mit den Augen zu erkennen.
    private static void WriteElement(....)
    {
      if (htmlWriter == null)
      {
        ....
      }
      else
      {
         if (htmlWriter != null && htmlElementName != null)
         {
           ....
      ....
    }

    V3063 Ein Teil des bedingten Ausdrucks ist immer wahr: htmlWriter! = Null. HtmlFromXamlConverter.cs 491

    Dies ist kein Problem für den Analysator. Dank unseres bevorzugten Copy-Paste- Verfahrens wurde der Fehler übrigens in zwei Projekten gleichzeitig gefunden: HtmlToXamlDemo und DocumentSerialization .

    Sinnlose Prüfungen finden sich natürlich nicht nur in langen Funktionen, sondern auch in zwei oder drei Zeilen.
    private void OnFlipPicTimeline(object sender, EventArgs e)
    {
      var clock = (Clock) sender;
      if (clock.CurrentState == ClockState.Active) // Begun case
      {
        return;
      }
      if (clock.CurrentState != ClockState.Active) // Ended case
      {
        ....
      }
    }

    V3022 Expression 'clock.CurrentState != ClockState.Active' is always true. MainWindow.cs 103

    В принципе, ничего страшного, но, когда после идет инструкция if вложенная еще в один if и еще и еще… Хочется избавиться хотя бы от бессмысленных проверок для лучшего восприятия кода, который, на самом деле, читается гораздо чаше чем пишется.

    Давайте немного передохнём. И посмотрим на вот такую необычную функцию, которая мне встретилась. Тело функции приведено полностью:
    private void UpdateSavings()
    {
      Savings = TotalIncome - (Rent + Misc + Food);
      if (Savings < 0)
      {
      }
      else if (Savings >= 0)
      {
      }
    }

    V3022 Expression 'Savings >= 0' is always true. NetIncome.cs 98

    Также было найдено много (более 60) сравнений вещественных чисел (double) с конкретным 0.
    if (leftConst != null && leftConst.Value == 0)
    {
      // 0 + y;  return y;
      return newRight;
    }

    К примеру:
    • V3024 Ein seltsamer genauer Vergleich: leftConst.Value == 0. Verwenden Sie einen Vergleich mit definierter Genauigkeit: Math.Abs ​​(A - B) <Epsilon. AddExpression.cs 34
    • V3024 Ein seltsamer genauer Vergleich: leftConst.Value == 1. Verwenden Sie einen Vergleich mit definierter Genauigkeit: Math.Abs ​​(A - B) <Epsilon. MultExpression.cs 42
    • V3024 Ein seltsamer genauer Vergleich: leftConst.Value == -1. Verwenden Sie einen Vergleich mit definierter Genauigkeit: Math.Abs ​​(A - B) <Epsilon. MultExpression.cs 47
    • usw...

    Artikel sind nicht genug, um alle Zeilen zu bringen. Diese Warnung ist auf der 3. Ebene, weil Ihre Relevanz hängt stark von den Besonderheiten des Programms ab. Wenn mathematische Berechnungen an Zahlen durchgeführt werden (Manipulation des Wertes), kann nicht garantiert werden, dass wir eine bestimmte Zahl erhalten: -1, 0, 1. Eine Abweichung von 0,00000000001 führt bereits zu einem falschen Vergleichsergebnis. Wenn die Logik des Programms jedoch darin besteht, diskrete Werte in reelle Zahlen ( doppelt ) zu schreiben , sind solche Überprüfungen kein Fehler.

    2. Fehler bei der Initialisierung und beim Zuweisen von Variablen

    Eine Funktion ist eine großartige Sache, mit der Sie nicht nur doppelten Code entfernen, sondern auch das Verständnis des Codeabschnitts vereinfachen können, in dem diese Funktion verwendet wird. Es ist besonders wichtig, dass die Funktion genau die Aufgabe ausführt, die in ihrem Namen und ihrer Aufrufsignatur beschrieben ist. Das kommt aber nicht immer vor. Nehmen Sie zum Beispiel den folgenden Codeabschnitt. Die Funktion ist vollständig zum besseren Verständnis der Situation vorgesehen.
    public bool OpenDocument(string fileName)
    {
      Microsoft.Win32.OpenFileDialog dialog;
      // If there is a document currently open, close it.
      if (this.Document != null)  CloseFile();
      dialog = new Microsoft.Win32.OpenFileDialog();
      dialog.CheckFileExists = true;
      dialog.InitialDirectory = GetContentFolder();
      dialog.Filter = this.OpenFileFilter;
      bool result = (bool)dialog.ShowDialog(null);
      if (result == false)  return false;
      fileName = dialog.FileName; //<==
      return OpenFile(fileName);
    }

    V3061 Der Parameter 'fileName' wird vor der Verwendung immer im Methodentext neu geschrieben. ThumbViewer.xaml.cs 192

    Der Name der Datei, die Sie öffnen möchten, wird direkt vor der ersten Verwendung von fileName = dialog.FileName gefräst . Ja, in diesem Fall wird ein Dialogfeld geöffnet und eine Benutzerdatei ausgewählt. Warum benötigen Sie dann einen Parameter, der eigentlich nicht verwendet wird?

    Zeitmangel und Copy-Paste führen manchmal zu sehr seltsamen Designs:
    public MailSettingsDialog()
    {
      ....
      _timerClock = _timerClock = new DispatcherTimer(); 
      ....
    }

    V3005 Die Variable '_timerClock' wird sich selbst zugewiesen. MailSettingsDialog.cs 56

    Es scheint, dass dies kein schrecklicher Tippfehler ist, aber es lässt die Idee aufkommen , ob wir dort „zum zweiten Mal schreiben“. Na zum Beispiel, wie hier:
    private void DumpAllClipboardContentsInternal()
    { 
      ....
      if (dataObject == null)
      {
        clipboardInfo.Text =
          clipboardInfo.Text =
            "Can't access clipboard now! 
              \n\nPlease click Dump All Clipboard 
                  Contents button again.";
      } 
      else 
      {
         ....
    }

    V3005 Die Variable ' clipboardInfo.Text ' wird sich selbst zugewiesen. MainWindow.cs 204 Im

    Allgemeinen ist der Code voll von seltsamen Zuweisungen:
    private void DoParse(string commandLine)
    {
      ....
      strLeft = strRight = string.Empty;
      strLeft = strs[0];
      strRight = strs[1];
      ....
    }

    V3008 Die Variable 'strLeft' wird zweimal nacheinander mit Werten belegt. Vielleicht ist das ein Fehler. Zeilen

    prüfen : 55, 54. CommandLine.cs 55 V3008 Der Variablen 'strRight' werden zweimal nacheinander Werte zugewiesen. Vielleicht ist das ein Fehler. Check lines: 56, 54. CommandLine.cs 56

    strLeft und strRight sind nur lokale Variablen vom Typ string .

    Naja oder hier ist das weitere Codebeispiel eher falsch. Aus irgendeinem Grund wurden so viele Dinge darin gezählt, und dann wurden sie erneut gezählt und in dieselbe Variable geschrieben.
    private object InvokMethod(....)
    {
      arg = commandLine.Substring(
        commandLine.IndexOf("(", StringComparison.Ordinal) + 1,
          commandLine.IndexOf(")", 
            StringComparison.Ordinal) - 
            (commandLine.IndexOf("(", 
              StringComparison.Ordinal) + 1));
      arg = commandLine.Substring(
        commandLine.IndexOf("(", 
          StringComparison.Ordinal) + 1);
    }

    V3008 Der Variablen 'arg' werden nacheinander zweimal Werte zugewiesen. Vielleicht ist das ein Fehler. Check lines: 176, 173. CommandLine.cs 176

    Und viele, viele weitere solche oder ähnliche Beispiele für bedeutungslose Primärzuweisungen:
    private void DrawFormattedText(DpiScale dpiInfo)
    {
      ....
      Geometry geometry = new PathGeometry();
      geometry = formattedText.BuildGeometry(
         new System.Windows.Point(0, 0));
      ....
    }
    • V3008 Der Variablen 't' werden nacheinander zweimal Werte zugewiesen. Vielleicht ist das ein Fehler. Zeilen überprüfen: 141, 115. TrackBall.cs 141
    • V3008 Der Variablen 't' werden nacheinander zweimal Werte zugewiesen. Vielleicht ist das ein Fehler. Zeilen überprüfen: 141, 115. TrackBall.cs 141
    • V3008 Der Variablen 'columnSpan' werden nacheinander zweimal Werte zugewiesen. Vielleicht ist das ein Fehler. Überprüfen Sie die Zeilen: 2115, 2101. HtmlToXamlConverter.cs 2115
    • V3008 Der Variablen '_timerInterval' werden nacheinander zweimal Werte zugewiesen. Vielleicht ist das ein Fehler. Zeilen prüfen: 52, 47. ClientForm.cs 52
    • V3008 Der Variablen 'matrix1' werden nacheinander zweimal Werte zugewiesen. Vielleicht ist das ein Fehler. Überprüfen Sie die Zeilen: 126, 125. MainWindow.cs 126
    • V3008 Der Variablen 'matrixResult' werden nacheinander zweimal Werte zugewiesen. Vielleicht ist das ein Fehler. Überprüfen Sie die Zeilen: 140, 138. MainWindow.cs 140
    • V3008 Der Variablen 'matrixResult' werden nacheinander zweimal Werte zugewiesen. Vielleicht ist das ein Fehler. Überprüfen Sie die Zeilen: 351, 349. MainWindow.cs 351
    • V3008 Der Variablen 'matrixResult' werden zweimal nacheinander Werte zugewiesen. Vielleicht ist das ein Fehler. Überprüfen Sie die Zeilen: 369, 367. MainWindow.cs 369
    • V3008 Der Variablen 'pointResult' werden zweimal nacheinander Werte zugewiesen. Vielleicht ist das ein Fehler. Überprüfen Sie die Zeilen: 480, 478. MainWindow.cs 480
    • V3008 Der Variablen 'columnSpan' werden nacheinander zweimal Werte zugewiesen. Vielleicht ist das ein Fehler. Check lines: 1930, 1917. htmltoxamlconverter.cs 1930
    • V3008 Der Variablen 'Geometrie' werden nacheinander zweimal Werte zugewiesen. Vielleicht ist das ein Fehler. Überprüfen Sie die Zeilen: 56, 55. MainWindow.xaml.cs 56
    • V3008 Der Variablen 'pathGeometry' werden nacheinander zweimal Werte zugewiesen. Vielleicht ist das ein Fehler. Überprüfen Sie die Zeilen: 66, 65. MainWindow.xaml.cs 66

    Es hat keinen Sinn, jedes Beispiel zu schreiben, umso mehr warten wichtige und schwerwiegende Fehler auf uns.

    3. Ein paar heterogene Fehler Beim

    Auslösen von Ausnahmen ist es wichtig, den Aufrufstapel beizubehalten, damit später die Protokolle nachvollzogen werden können - "und was der Benutzer grundsätzlich gestürzt ist". Nicht jeder weiß, wie man es richtig macht.
    public static object InvokePropertyOrMethod(....)
    {
      try
      {
         ....
      }
      catch (MissingMethodException e)
      {
        ....
        throw e;
      }
      catch (AmbiguousMatchException e)
      {
         throw e;
      }
      return resultObject;
    }

    V3052 Das ursprüngliche Ausnahmeobjekt 'e' wurde verschluckt. Stapel der ursprünglichen Ausnahme könnte verloren gehen. ReflectionUtils.cs 797

    V3052 Das ursprüngliche Ausnahmeobjekt 'e' wurde verschluckt. Stapel der ursprünglichen Ausnahme könnte verloren gehen. ReflectionUtils.cs 806

    Wenn Sie gemäß der Norm eine Ausnahme übergeben, wird der Funktionsaufrufstapel mit throw e; , verlieren wir den Aufrufstapel, der vor dem Abfangen der Ausnahme im catch-Block war . Um alle Call - Stack zu speichern und die weitere Fortsetzung, müssen Sie nur ein einziges Wort schreiben Wurf im Block fangen alle.

    Manchmal sind Prüfungen überflüssig und manchmal reichen sie einfach nicht aus, wie zum Beispiel im folgenden Code:
    private static void ParseCssFontFamily(....)
    {
      ....
      if (fontFamilyList == null && fontFamily.Length > 0)
      {
        if (fontFamily[0] == '"' || fontFamily[0] == '\'')
        {
          // Unquote the font family name
          fontFamily = 
            fontFamily.Substring(1, fontFamily.Length - 2);
          ....
    }

    V3057 Die Funktion 'Substring' kann den Wert '-1' empfangen, wenn ein nicht negativer Wert erwartet wird. Untersuche das zweite Argument. HtmlCSSParser.cs 645

    Hier wird nicht überprüft, ob fontFamily.Length größer als 1 ist. Wenn Sie 2 von fontFamily.Length subtrahieren , erhalten Sie einen Wert kleiner als 0. In solchen Fällen gibt diese Funktion eine ArgumentOutOfRangeException aus .

    Es wäre sicherer, einen Scheck auszustellen:
    if (fontFamilyList == null && fontFamily.Length > 1)

    4. WPF-Fehler

    DependencyProperty ist eine der bemerkenswertesten Funktionen von WPF. Das Erstellen von Eigenschaften, mit denen der Entwickler sofort über Änderungen informiert werden kann, ist unglaublich praktisch. Das Wichtigste ist jedoch, die Unterschrift nicht für ihre Beschreibung zu verwechseln. Dies ist besonders wichtig, wenn Beispiele gezeigt werden, da sich die Menschen genau an ihnen orientieren.
    public double Radius
    {
      get { return (double) GetValue(RadiusProperty); }
      set { SetValue(RadiusProperty, value); }
    }
    public static readonly DependencyProperty 
      RadiusProperty = DependencyProperty.Register(
        "RadiusBase",
        typeof (double),
        typeof (FireworkEffect),
        new FrameworkPropertyMetadata(15.0));

    V3045 WPF: Die Namen der registrierten Eigenschaft 'RadiusBase' und der Eigenschaft 'Radius' stimmen nicht überein. FireworkEffect.cs 196

    In diesem speziellen Fall wird der Name entsprechend den Eigenschaften registriert nicht den Namen der Eigenschaft-Wrapper für den Zugriff DependencyProperty Code entspricht. Diese Option führt zu großen Problemen beim Arbeiten mit XAML-Markups. Mit WPF können Sie über XAML auf die einfache Radius- Eigenschaft zugreifen und den Wert von dort lesen. Änderungen an dieser Eigenschaft werden jedoch nicht von XAML übernommen.

    Tatsächlich gibt es in PVS-Studio eine Reihe von Diagnosen zum Erkennen von Fehlern in der Signatur der Erstellung von DependencyProperty [ 3044 , 3045 , 3046, 3047 , 3048 , 3049 ]. Die meisten Fehler dieser Art führen zu einem Programmabsturz, sobald die Verwendung einer Klasse mit diesen Abhängigkeitseigenschaften beginnt. Daher wurden diese Diagnosedaten speziell entwickelt, um Sie vor dem Durchsuchen und Analysieren langer Schritte von Signaturen zu bewahren, insbesondere nach dem Kopieren. Dazu müssen Sie natürlich regelmäßig den Code überprüfen und nicht nur die endgültige Version des Programms analysieren.

    Betrachten wir eine weitere interessante Operation. In diesem Fall hat die neue V3095-Diagnose funktioniert. Diese Diagnose zeigt die Stellen an, an denen wir zuerst auf die Variable zugreifen und sie dann auf Null prüfen .
    private static XmlElement AddOrphanListItems(....)
    {
      Debug.Assert(htmlLiElement.LocalName.ToLower() == "li");
      ....
      XmlNode htmlChildNode = htmlLiElement;
      var htmlChildNodeName = htmlChildNode == null 
          ? null 
          : htmlChildNode.LocalName.ToLower();
      ....
    }

    V3095 Das Objekt 'htmlLiElement' wurde verwendet, bevor es gegen null verifiziert wurde. Überprüfen Linien: 916, 936. HtmlToXamlConverter.cs 916

    In diesem Fall wird in dem Zustand des ternären Operators, überprüfen wir , dass die Variable htmlChildNode sein kann null . In diesem Fall ist die Variable htmlChildNode lediglich eine Referenz auf die Variable htmlLiElement . Es war jedoch die Variable htmlLiElement , auf die wir zugegriffen haben, ohne nach null zu suchen . Als Ergebnis haben wir entweder Code, der niemals ausgeführt wird, oder sogar eine NullReferenceException in der Zeile htmlLiElement.LocalName.ToLower () .

    Zusätzlich zu den beschriebenen Fehlern wird viel Aufmerksamkeit auf die Diagnose unter der Nummer V3072 gelenkt , mit der das Vorhandensein von Feldern eines Typs erkannt werden soll, der die IDisposable- Schnittstelle implementiert , aber die Klasse selbst, in der die Felder deklariert sind, hat keine solche Implementierung.
    internal class Email
    {
      private readonly SmtpClient _client;
      ....
    }

    V3072 Die Klasse 'Email', die IDisposable-Mitglieder enthält, implementiert IDisposable selbst nicht. Prüfen Sie: _client. Email.cs 15

    C IDisposable war schon immer eng gewesen. Aufgrund kritischer Fehler, die auf eine unsachgemäße Verwendung zurückzuführen sind, sind die Finalize- Speichervorgänge abgeschlossen , zumindest in Standardklassen. Programmierer verstopfen, vergessen, verpassen oder achten einfach nicht auf Felder mit einem Typ, der diese Schnittstelle implementiert. Es ist für einen Drittanbieter schwierig, einen solchen Code zu rechtfertigen oder das Vorhandensein eines Fehlers darin zuzugeben, aber es gibt einige Muster, die es wert sind, beachtet zu werden. In dieser Lösung gab es nicht wenige solcher Auslöser.
    • V3072 Die Klasse 'HtmlLexicalAnalyzer', die IDisposable-Member enthält, implementiert IDisposable selbst nicht. Untersuchen Sie: _inputStringReader. HtmlLexicalAnalyzer.cs 16
    • V3072 Die Klasse 'MainWindow', die IDisposable-Member enthält, implementiert IDisposable selbst nicht. Überprüfen Sie: _customersTableAdapter, _nwDataSet ... MainWindow.cs 15
    • V3072 Die Klasse 'MainWindow', die IDisposable-Member enthält, implementiert IDisposable selbst nicht. Überprüfen Sie: _listControl. MainWindow.cs 14
    • V3072 Die 'ThumbViewer'-Klasse, die IDisposable-Member enthält, implementiert IDisposable selbst nicht. Überprüfen Sie: _annStore, _annotationBuffer. ThumbViewer.xaml.cs 31
    • V3072 Die Klasse 'HtmlLexicalAnalyzer', die IDisposable-Member enthält, implementiert IDisposable selbst nicht. Untersuchen Sie: _inputStringReader. htmllexicalanalyzer.cs 24
    • V3072 Die Klasse 'MainWindow', die IDisposable-Member enthält, implementiert IDisposable selbst nicht. Überprüfen Sie: _store. MainWindow.cs 20
    • V3072 Die Klasse 'MainWindow', die IDisposable-Member enthält, implementiert IDisposable selbst nicht. Überprüfen Sie: _customCursor. MainWindow.cs 14
    • V3072 Die Klasse 'MainWindow', die IDisposable-Member enthält, implementiert IDisposable selbst nicht. Überprüfen Sie: _speechSynthesizer. MainWindow.cs 14

    C ++ - Fehler


    1. Fehler beim Entwerfen der Bedingungen der if-Anweisung

    Für mich war es natürlich eine Entdeckung, dass ich C ++ - Projekte in dieser Lösung finden würde, aber dennoch gibt es Fehler und wir wollen sie uns ansehen.

    Wie in C # beginnen wir mit verschiedenen Vergleichen und schauen uns sofort genau den C ++ - Fehler an, den ich etwas höher im C # -Block erwähnt habe.
    STDMETHOD(CreateInstance)(....)
    {
      ....
      T *obj = new T();
      if (NULL != obj)
      {
        ....
    }

    V668 Es gibt die keinen Sinn in der Prüfung der ‚obj‘ Null - Zeiger Against, wie mit dem Speicher wurde die Zugeteilte mit dem die ‚neuen‘ des Betreibers. Die Ausnahme wird im Fall eines Speicherzuordnungsfehlers generiert. classfactory.h 76

    Konnte der neue Operator keinen Speicher zuordnen, wird gemäß dem C ++ - Sprachstandard eine Ausnahme std :: bad_alloc () ausgelöst. Daher ist es nicht sinnvoll, auf Gleichheit mit Null zu prüfen, weil Der obj- Zeiger wird niemals NULL sein . Wenn kein Speicher zugewiesen werden kann, tritt eine Ausnahme auf, die auf einer höheren Ebene behandelt werden kann, und die NULL- Gleichheitsprüfung kann einfach gelöscht werden. Wenn Ausnahmen in der Anwendung unerwünscht sind, können Sie den neuen Operator verwendendas wirft keine Ausnahmen ( T * obj = new (std :: nothrow) T () ): In diesem Fall können Sie den Rückgabewert auf Null überprüfen. Es gab 4 weitere ähnliche Überprüfungen in Lösung:
    • V668 Es hat keinen Sinn, den Zeiger 'colors' gegen null zu testen, da der Speicher mit dem Operator 'new' zugewiesen wurde. Die Ausnahme wird im Fall eines Speicherzuordnungsfehlers generiert. aitdecoder.cpp 182
    • V668 Es hat keinen Sinn, den 'Pixel'-Zeiger gegen Null zu testen, da der Speicher mit dem Operator' new 'zugewiesen wurde. Die Ausnahme wird im Fall eines Speicherzuordnungsfehlers generiert. aitencoder.cpp 157
    • V668 Es hat keinen Sinn, den Zeiger 'colors' gegen null zu testen, da der Speicher mit dem Operator 'new' zugewiesen wurde. Die Ausnahme wird im Fall eines Speicherzuordnungsfehlers generiert. aitencoder.cpp 221
    • V668 Es hat keinen Sinn, den 'Bytes'-Zeiger gegen null zu testen, da der Speicher mit dem Operator' new 'zugewiesen wurde. Die Ausnahme wird im Fall eines Speicherzuordnungsfehlers generiert. aitencoder.cpp 275

    Zusätzliche Bedingungen sind in beiden Programmiersprachen relevant:
    if (bitmapLock && bitmap)
    {
      if(bitmapLock)
      {
        bitmapLock->Release();
        bitmapLock = NULL;
      }
    }

    V571 Wiederkehrende Prüfung. Die Bedingung 'bitmapLock' wurde bereits in Zeile 104 überprüft. Aitdecoder.cpp 106

    Einige C # -Programmierer wissen nicht, dass die folgenden beiden Vorgänge für einen Nullable- Typ äquivalent sind:
    • _isInDesignMode! = null
    • _isInDesignMode.HasValue

    Und sie machen ähnliche Prüfungen:
    if (_isInDesignMode != null && _isInDesignMode.HasValue)

    In C ++ möchten sie sinnlos nach einem Zeiger auf Null suchen, bevor sie Speicher freigeben, der an der Adresse zugewiesen ist, auf die er verweist.
    static HRESULT OutputColorContext(....)
    {
      ....
      if (pixels)
        delete[] pixels;
      ....
    }

    V809 Es ist nicht erforderlich, zu überprüfen, ob ein Zeigerwert nicht NULL ist. Das Häkchen bei "Wenn (Pixel)" kann entfernt werden. aitencoder.cpp 189
    static HRESULT OutputBitmapPalette(....)
    {
      ....
      if (colors)
        delete[] colors;
      ....
    }

    V809 Es ist nicht erforderlich, zu überprüfen, ob ein Zeigerwert nicht NULL ist. Das Kontrollkästchen "Wenn (Farben)" kann entfernt werden. aitencoder.cpp 241
    static HRESULT OutputColorContext(....)
    {
      if (bytes)
        delete[] bytes;
    }

    V809 Es ist nicht erforderlich, zu überprüfen, ob ein Zeigerwert nicht NULL ist. Das 'if (bytes)' Häkchen kann entfernt werden. aitencoder.cpp 292

    2. Logischer Fehler

    Der folgende Code zeigt eine sehr interessante Situation des logischen Vergleichs, obwohl Sie dies auf den ersten Blick nicht sagen werden:
    STDMETHODIMP AitDecoder::QueryCapability(....)
    {
      ....
      // If this is our format, we can do everything
      if (strcmp(bh.Name, "AIT") == 0)
      {
         *pCapability = 
           WICBitmapDecoderCapabilityCanDecodeAllImages ||
           WICBitmapDecoderCapabilityCanDecodeThumbnail ||
           WICBitmapDecoderCapabilityCanEnumerateMetadata ||
           WICBitmapDecoderCapabilitySameEncoder;
      }
      ....
    }

    V560 Ein Teil des bedingten Ausdrucks ist immer wahr. aitdecoder.cpp 634

    Diagnose berücksichtigt, dass ein Teil des Ausdrucks immer wahr und richtig ist, weil Die Wörter WICBitmapDecoderCapabilityCanDecodeXXX sind einfach Aufzählungswerte mit dem Namen WICBitmapDecoderCapabilities :
    enum WICBitmapDecoderCapabilities
    {
      WICBitmapDecoderCapabilitySameEncoder = 0x1,
      WICBitmapDecoderCapabilityCanDecodeAllImages = 0x2,
      WICBitmapDecoderCapabilityCanDecodeSomeImages = 0x4,
      WICBitmapDecoderCapabilityCanEnumerateMetadata = 0x8,
      WICBitmapDecoderCapabilityCanDecodeThumbnail = 0x10,
      WICBITMAPDECODERCAPABILITIES_FORCE_DWORD = 0x7fffffff
    };

    Infolgedessen ist es wahrscheinlich, dass jemand die Zeichen und anstelle des bitweisen ODER "|" schrieb ein logisches ODER "||". Im Gegensatz zum C # -Compiler verschluckte C ++ es leicht.

    3. Fehler bei der Initialisierung und beim Zuweisen von Variablen

    Natürlich bleiben häufig initialisierte Variablen nach dem Refactoring zweimal hintereinander.
    STDMETHODIMP BaseFrameEncode::WritePixels(....)
    {
       result = S_OK;
       ....
       result = factory->CreateBitmapFromMemory(....);
    }

    V519 Die Variable 'result' wird zweimal nacheinander mit Werten belegt. Vielleicht ist das ein Fehler. Überprüfen Sie die Zeilen: 269, 279. baseencoder.cpp 279

    Wenn die Variablen über mehrere Codezeilen initialisiert werden, können Sie leicht verstehen, warum sie übersehen wurden, aber manchmal gehen diese beiden Zeilen hintereinander:
    STDMETHODIMP AitFrameEncode::Commit()
    {
       HRESULT result = E_UNEXPECTED;
       result = BaseFrameEncode::Commit();
       ....
    }

    V519 Die Variable 'result' wird zweimal nacheinander mit Werten belegt. Vielleicht ist das ein Fehler. Überprüfen Sie die Zeilen: 320, 321. aitencoder.cpp 321

    Fazit


    Es wird angenommen, dass C # -Code weniger fehleranfällig ist als C ++; In bestimmten Fällen ist dies der Fall. Eine interessante Tatsache ist jedoch, dass der Großteil der Fehler nicht in spezifischen Konstruktionen, sondern in einfachen Begriffen liegt. Zum Beispiel in den Bedingungen einer if-Anweisung . Mit dem statischen Codeanalysator für PVS-Studio C, C ++ und C # können Sie die Qualität Ihres Codes kontrollieren und auf jeden Fall vor schwerwiegenden Fehlern schützen, die Ihren Benutzer erreichen können.


    Wenn Sie diesen Artikel mit einem englischsprachigen Publikum teilen möchten, verwenden Sie bitte den Link zur Übersetzung: Alferov Vitalii. Der Quellcode von WPF-Beispielen von Microsoft wurde überprüft .

    Haben Sie den Artikel gelesen und eine Frage?
    Oft werden unseren Artikeln die gleichen Fragen gestellt. Wir haben die Antworten hier gesammelt: Antworten auf Fragen von Lesern von Artikeln über PVS-Studio, Version 2015 . Bitte beachten Sie die Liste.

    Jetzt auch beliebt: