Se revisó el código fuente de las muestras de WPF de Microsoft

 C Programming >> Programación C >  >> Tags >> WPF
Se revisó el código fuente de las muestras de WPF de Microsoft

Tras el lanzamiento de Windows Vista, la empresa introdujo un nuevo subsistema para representar interfaces de usuario en aplicaciones basadas en Windows:Windows Presentation Foundation (WPF). Este subsistema gráfico forma parte de .NET Framework, a partir de la versión 3.0. Utiliza lenguaje de marcado XAML. Ahora, casi ha reemplazado a los WinForms obsoletos. En nuestra humilde opinión, la principal desventaja de WinForms era el hecho de que hacía todo el renderizado en la CPU. WPF abordó esto de una manera más sensata y dejó que DirectX hiciera la representación de los componentes. Ahora WPF permite la creación de interfaces universales para tres plataformas a la vez (PC, XBOXOne, Winphone) y prácticamente ha desplazado a WinForms.

Para realizar el análisis de los ejemplos de WPF de Microsoft (el código fuente de los ejemplos), utilizamos el analizador de código estático PVS-Studio, versión 6.05.

Una cosa interesante de esta solución es el hecho de que junto con los proyectos escritos en C#, también hay varios proyectos en C++. Pero lo encontramos solo en la lista de errores encontrados por PVS-Studio. El complemento PVS-Studio para Visual Studio, sin ninguna configuración adicional por parte del usuario, realizó el análisis y mostró advertencias para los proyectos C++ y C#.


Figura 1. Como puede ver, en la ventana de PVS-Studio se emiten advertencias para el código C# y C++. (Click en la imagen para agrandar)

Errores de C#

1. Errores cometidos durante la formación de las condiciones de la sentencia if

Para los programadores es un problema común:errores en las comparaciones. Echemos un vistazo a ellos.

En este código hay dos condiciones absolutamente idénticas:

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 Se detectó el uso del patrón 'if (A) {...} else if (A) {...}'. Hay una probabilidad de presencia de error lógico. Verificar líneas:418, 422. txtserializerwriter.cs 418

No está muy claro a qué se refería aquí, pero aparentemente era algo diferente de lo que vemos ahora.

Nos gusta hacer las verificaciones contra null en las condiciones, y así tratar de proteger el programa de eventos de emergencia. Incluso podemos decir que la mayoría de si las condiciones son las null -Comprobaciones de algunos campos o variables. Pero a veces tales controles pueden ser redundantes e incluso contener errores lógicos:

public static string FindNumeric(string content)
{
  string[] values = content.Split(' ');
  if (values != null)
  {
    return values[0];
  }
  return "none";
}

V3022 La expresión ‘values ​​!=null’ siempre es verdadera. Util.cs 287

Podríamos suponer que el autor quería comprobar que los valores tiene más de 0 elementos, pero no pudimos pensar en una situación en la que Dividir devuelve una matriz vacía. De todos modos, la verificación contra nulo es completamente innecesaria aquí.

Como ya hemos dicho, el proyecto contiene código de C++ y C# diagnostics. Tenemos la impresión de que el siguiente código fue escrito por un programador de C++.

private void LoadNotes()
{
  var fs = new FileStream("NotesFile", FileMode.Open);
  if (fs != null)
  {
    ....
}

V3022 La expresión ‘fs !=null’ siempre es verdadera. Ventana principal.cs 66

En realidad, incluso en C++ esta variante es errónea, en C# al menos parecerá “rara”. En el artículo "Comprobación de 7-Zip con el analizador de PVS-Studio" se proporcionan más detalles sobre por qué es incorrecto escribir dicho código. Continuaremos analizando el código C#.

No tenemos que ir muy lejos para encontrar más fragmentos con errores. Había dos funciones prácticamente idénticas en la solución (gracias a copiar y pegar) con el mismo error:

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 La expresión ‘writer !=null’ siempre es verdadera. htmlserializerwriter.cs 324

Escritor no será una referencia nula...

Tirar un error en situaciones excepcionales no es la peor decisión. Pero lo principal es no cometer un error en la condición en la que se debe lanzar la excepción, ya que puede crear una impresión desagradable a los ojos de nuestro usuario, cuando el programa falla repentinamente.

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 Considere inspeccionar 'sizeString.Length ==0 || sizeString.Length !=2’ expresión. La expresión es excesiva o contiene un error tipográfico. Ventana principal.cs 140

A juzgar por el texto del error, la comparación con 0 es excesiva, bastaba con comprobar si sizeString.Length no es igual a 2.

En los largos cuerpos de si instrucción a veces es muy difícil notar comprobaciones sin sentido mientras se revisa el código.

private static void WriteElement(....)
{
  if (htmlWriter == null)
  {
    ....
  }
  else
  {
     if (htmlWriter != null && htmlElementName != null)
     {
       ....
  ....
}

V3063 Una parte de la expresión condicional siempre es verdadera:htmlWriter !=null. HtmlFromXamlConverter.cs 491

No es problema para el analizador. Por cierto, gracias a nuestro querido copiar y pegar, se encontró un error en dos proyectos:HtmlToXamlDemo y Serialización de documentos .

Por supuesto, se pueden encontrar controles sin sentido no solo en funciones largas, sino también dentro de varias cadenas.

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 La expresión ‘clock.CurrentState !=ClockState.Active’ siempre es verdadera. Ventana principal.cs 103

En general, está bastante bien, pero cuando más tarde tenemos un si sentencia anidada en otra sentencia if, y otra... Si tan solo pudiéramos deshacernos de comprobaciones sin sentido para una mejor comprensión del código, que se lee con más frecuencia de lo que se escribe...

Tomemos un breve descanso y echemos un vistazo a una función que hemos encontrado recientemente. Este es el cuerpo de la función:

private void UpdateSavings()
{
  Savings = TotalIncome - (Rent + Misc + Food);
  if (Savings < 0)
  {
  }
  else if (Savings >= 0)
  {
  }
}

V3022 La expresión ‘Ahorro>=0’ siempre es verdadera. NetIncome.cs 98

También hemos encontrado muchas (más de 60) comparaciones de números reales (doble) con un 0 preciso.

if (leftConst != null && leftConst.Value == 0)
{
  // 0 + y;  return y;
  return newRight;
}

Por ejemplo:

  • V3024 Una comparación precisa extraña:leftConst.Value ==0. Considere usar una comparación con precisión definida:Math.Abs(A – B)
  • V3024 Una comparación precisa extraña:leftConst.Value ==1. Considere usar una comparación con precisión definida:Math.Abs(A – B)
  • V3024 Una extraña comparación precisa:leftConst.Value ==-1. Considere usar una comparación con precisión definida:Math.Abs(A – B)
  • y así sucesivamente…

Todas las líneas no caben en un artículo. Esta advertencia es de tercer nivel para nosotros, porque su relevancia depende en gran medida de las características específicas del programa. En caso de que haya evaluaciones matemáticas (manipulaciones con el valor), no hay garantía de que obtengamos un número específico:-1, 0, 1. Pero incluso una ligera desviación en 0.00000000001 conducirá a un resultado incorrecto en las comparaciones. Pero si la lógica del programa presupone escribir valores discretos a los números reales (doble), entonces estas comprobaciones no son un error.

2. Errores en la inicialización y asignación de variables

Las funciones son cosas geniales que ayudan no solo a eliminar el código duplicado, sino que también simplifican la legibilidad del código donde se usa esta función. Es especialmente importante que esta función haga exactamente la tarea que se indica en su nombre y en la firma de la llamada. Pero este no es siempre el caso, por ejemplo, considere el siguiente fragmento de código. Escribiremos la función completa para que pueda comprender la situación con mayor claridad.

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 El parámetro 'fileName' siempre se reescribe en el cuerpo del método antes de usarse. ThumbViewer.xaml.cs 192

El nombre del archivo que debe abrirse se pierde justo antes de su primer uso fileName =dialog.FileName . Sí, se abrirá una ventana de diálogo y se elegirá el archivo de usuario, pero ¿por qué necesitamos un parámetro que no se usa realmente?

La falta de tiempo y el copiar y pegar a veces produce construcciones muy extrañas:

public MailSettingsDialog()
{
  ....
  _timerClock = _timerClock = new DispatcherTimer(); 
  ....
}

V3005 La variable '_timerClock' se asigna a sí misma. MailSettingsDialog.cs 56

Puede que este no parezca el error tipográfico más horrible, pero nos hace pensar, "¿estamos escribiendo en el lugar correcto por segunda vez?" Bueno, por ejemplo, así:

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 La variable 'clipboardInfo.Text' se asigna a sí misma. Ventana principal.cs 204

En general, el código abunda en asignaciones extrañas:

private void DoParse(string commandLine)
{
  ....
  strLeft = strRight = string.Empty;
  strLeft = strs[0];
  strRight = strs[1];
  ....
}

V3008 A la variable 'strLeft' se le asignan valores dos veces seguidas. Quizás esto sea un error. Comprobar líneas:55, 54. CommandLine.cs 55

V3008 A la variable 'strRight' se le asignan valores dos veces seguidas. Quizás esto sea un error. Comprobar líneas:56, 54. CommandLine.cs 56

strIzquierda y strDerecha – son solo variables locales de tipo string.

El siguiente código es aún más incorrecto. Por alguna razón, el programador hizo muchas evaluaciones y reevaluaciones y luego lo escribió en la misma variable.

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 A la variable 'arg' se le asignan valores dos veces seguidas. Quizás esto sea un error. Comprobar líneas:176, 173. CommandLine.cs 176

Y algunos ejemplos más de asignaciones primarias sin sentido:

private void DrawFormattedText(DpiScale dpiInfo)
{
  ....
  Geometry geometry = new PathGeometry();
  geometry = formattedText.BuildGeometry(
     new System.Windows.Point(0, 0));
  ....
}
  • V3008 A la variable 't' se le asignan valores dos veces seguidas. Quizás esto sea un error. Verificar líneas:141, 115. TrackBall.cs 141
  • V3008 A la variable 't' se le asignan valores dos veces seguidas. Quizás esto sea un error. Verificar líneas:141, 115. TrackBall.cs 141
  • V3008 A la variable 'columnSpan' se le asignan valores dos veces seguidas. Quizás esto sea un error. Verifique las líneas:2115, 2101. HtmlToXamlConverter.cs 2115
  • V3008 A la variable '_timerInterval' se le asignan valores dos veces seguidas. Quizás esto sea un error. Verifique las líneas:52, 47. ClientForm.cs 52
  • V3008 A la variable 'matriz1' se le asignan valores dos veces seguidas. Quizás esto sea un error. Comprobar líneas:126, 125. MainWindow.cs 126
  • V3008 A la variable 'matrixResult' se le asignan valores dos veces seguidas. Quizás esto sea un error. Comprobar líneas:140, 138. MainWindow.cs 140
  • V3008 A la variable 'matrixResult' se le asignan valores dos veces seguidas. Quizás esto sea un error. Verificar líneas:351, 349. MainWindow.cs 351
  • V3008 A la variable 'matrixResult' se le asignan valores dos veces seguidas. Quizás esto sea un error. Comprobar líneas:369, 367. MainWindow.cs 369
  • V3008 A la variable 'pointResult' se le asignan valores dos veces seguidas. Quizás esto sea un error. Comprobar líneas:480, 478. MainWindow.cs 480
  • V3008 A la variable 'columnSpan' se le asignan valores dos veces seguidas. Quizás esto sea un error. Verificar líneas:1930, 1917. htmltoxamlconverter.cs 1930
  • V3008 A la variable 'geometría' se le asignan valores dos veces seguidas. Quizás esto sea un error. Verifique las líneas:56, 55. MainWindow.xaml.cs 56
  • V3008 A la variable 'pathGeometry' se le asignan valores dos veces seguidas. Quizás esto sea un error. Comprobar líneas:66, 65. MainWindow.xaml.cs 66

No tiene sentido escribir cada ejemplo, hay más errores interesantes esperando por delante.

3. Un par de errores varios

Al lanzar la excepción, es importante guardar la llamada de la pila, para que luego podamos entender al mirar los registros, "qué fue exactamente lo que salió mal del lado del usuario", pero no todos saben cómo hacerlo.

public static object InvokePropertyOrMethod(....)
{
  try
  {
     ....
  }
  catch (MissingMethodException e)
  {
    ....
    throw e;
  }
  catch (AmbiguousMatchException e)
  {
     throw e;
  }
  return resultObject;
}

V3052 Se tragó el objeto de excepción original 'e'. La pila de la excepción original podría perderse. ReflectionUtils.cs 797

V3052 Se tragó el objeto de excepción original 'e'. La pila de la excepción original podría perderse. ReflectionUtils.cs 806

De acuerdo con el estándar, si pasamos la excepción anterior en la pila de llamadas de función por medio de throw e; , perderemos la pila de llamadas que estaba antes de la captura de la excepción en el bloque catch. Para mantener toda la llamada de la pila y su continuación, solo necesitamos escribir un throw palabra en la catch bloquear y listo.

A veces, las comprobaciones son innecesarias y, a veces, no son suficientes, como en el siguiente código:

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 La función 'Subcadena' podría recibir el valor '-1' mientras se espera un valor no negativo. Inspeccione el segundo argumento. HtmlCSSParser.cs 645

No se comprueba que fontFamily.Length es mayor que 1, por lo tanto, restando de fontFamily.Length número 2 podemos obtener un valor menor que 0. Y en tales casos, esta función arroja una excepción ArgumentOutOfRangeException .

Si sería más seguro escribir un cheque:

if (fontFamilyList == null && fontFamily.Length > 1)

4. Error de WPF

DependencyProperty es una de las características más notables de WPF. Crear propiedades que puedan notificar al desarrollador directamente desde el cuadro sobre los cambios realizados es increíblemente conveniente. Pero lo principal es evitar confundir la firma para describirlos, es particularmente importante recordar esto al mostrar los ejemplos, porque eso es lo que la gente juzga.

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:los nombres de la propiedad registrada 'RadiusBase' y de la propiedad 'Radius' no se corresponden entre sí. Efecto de fuegos artificiales.cs 196

En este caso particular, el nombre registrado para una propiedad de dependencia no coincide con el nombre de la propiedad contenedora para acceder a DependencyProperty desde el código. Esta opción causa grandes problemas cuando se trabaja desde el marcado XAML. WPF permite desde XAML acceder a una propiedad simple Radius y lea el valor de él, pero los cambios de esta propiedad no se obtendrán de XAML.

En realidad, en PVS-Studio, hay una serie de diagnósticos para detectar errores en la firma al crear DependencyProperty [3044, 3045, 3046, 3047, 3048, 3049]. Pero la mayoría de los errores de este tipo hacen que el programa se cuelgue tan pronto como el programa comience a usar la clase con estas propiedades de dependencia. Es por eso que estos diagnósticos están destinados a evitar que busquemos y analicemos textos largos de firmas, especialmente después de copiar. Por supuesto, lo más eficiente sería verificar el código con PVS-Studio regularmente, no solo hacer el análisis de la versión final del programa.

Veamos otra advertencia interesante. En este caso fue nuestro nuevo diagnóstico V3095. Este diagnóstico muestra los lugares donde primero accedemos a la variable y luego la verificamos contra nulo.

private static XmlElement AddOrphanListItems(....)
{
  Debug.Assert(htmlLiElement.LocalName.ToLower() == "li");
  ....
  XmlNode htmlChildNode = htmlLiElement;
  var htmlChildNodeName = htmlChildNode == null 
      ? null 
      : htmlChildNode.LocalName.ToLower();
  ....
}

V3095 El objeto 'htmlLiElement' se usó antes de que se verificara contra nulo. Verifique las líneas:916, 936. HtmlToXamlConverter.cs 916

En este caso, en la condición del operador ternario comprobamos si la variable htmlChildNode puede ser nulo. Al mismo tiempo, la variable htmlChildNode, no es más que una referencia a la variable htmlLiElement . Pero accedimos a la variable htmlLiElement sin la verificación contra nulo. Como resultado, tenemos un código que nunca se ejecutará o obtendremos una excepción NullReferenceException en la cadena htmlLiElement.LocalName.ToLower() .

Además de los errores que hemos descrito, se llama mucho la atención sobre el diagnóstico V3072, que está diseñado para detectar campos con el tipo que implementa el IDisposable. interfaz, pero la clase donde los campos no están declarados no tiene esta implementación.

internal class Email
{
  private readonly SmtpClient _client;
  ....
}

V3072 La clase 'Correo electrónico' que contiene miembros IDisposable no implementa IDisposable en sí misma. Inspeccionar:_cliente. Correo electrónico.cs 15

IDesechable siempre ha sido problemático. En ocasiones, Finalize puede ser de gran ayuda, al menos en clases estándar, para evitar errores críticos relacionados con su uso incorrecto. Los programadores a menudo olvidan, pierden o simplemente no prestan atención al campo con el tipo, implementando esta interfaz. No es tan fácil justificar dicho código o admitir que se ha producido un error mientras se revisa el código, pero hay patrones a los que vale la pena prestar atención. En esta solución también hubo muchas de estas advertencias:

  • V3072 La clase 'HtmlLexicalAnalyzer' que contiene miembros IDisposable no implementa IDisposable. Inspeccionar:_inputStringReader. HtmlLexicalAnalyzer.cs 16
  • V3072 La clase 'MainWindow' que contiene miembros IDisposable no implementa en sí misma IDisposable. Inspeccionar:_customersTableAdapter, _nwDataSet... MainWindow.cs 15
  • V3072 La clase 'MainWindow' que contiene miembros IDisposable no implementa en sí misma IDisposable. Inspeccionar:_listControl. Ventana principal.cs 14
  • V3072 La clase 'ThumbViewer' que contiene miembros IDisposable no implementa IDisposable en sí misma. Inspeccionar:_annStore, _annotationBuffer. ThumbViewer.xaml.cs 31
  • V3072 La clase 'HtmlLexicalAnalyzer' que contiene miembros IDisposable no implementa IDisposable. Inspeccionar:_inputStringReader. htmllexicalanalyzer.cs 24
  • V3072 La clase 'MainWindow' que contiene miembros IDisposable no implementa en sí misma IDisposable. Inspeccionar:_store. Ventana principal.cs 20
  • V3072 La clase 'MainWindow' que contiene miembros IDisposable no implementa en sí misma IDisposable. Inspeccionar:_customCursor. Ventana principal.cs 14
  • V3072 La clase 'MainWindow' que contiene miembros IDisposable no implementa en sí misma IDisposable. Inspeccionar:_speechSynthesizer. Ventana principal.cs 14

Errores de C++

1. Errores al escribir condiciones de declaración if

Fue toda una revelación para nosotros encontrar proyectos de C++ en esta solución, pero, sin embargo, estos también son errores, así que echemos un vistazo.

Como en C#, comencemos con varias comparaciones. Veamos ese mismo error de C++ que mencionamos en el bloque de C#.

STDMETHOD(CreateInstance)(....)
{
  ....
  T *obj = new T();
  if (NULL != obj)
  {
    ....
}

V668 No tiene sentido probar el puntero 'obj' contra nulo, ya que la memoria se asignó usando el operador 'nuevo'. La excepción se generará en el caso de un error de asignación de memoria. classfactory.h 76

Si el nuevo el operador no pudo asignar la memoria, entonces de acuerdo con el estándar C++, una excepción std::bad_alloc() es aventado. Por lo tanto, la verificación contra null no tiene sentido, ya que el obj puntero nunca será igual a NULL. Si es imposible asignar la memoria, entonces tenemos una excepción que debe manejarse en un nivel superior, y la verificación contra nulo puede simplemente eliminarse. En caso de que no sea deseable tener excepciones en la aplicación, podemos usar el nuevo operador que no genera excepciones (T *obj =new (std::nothrow) T() ), y, por lo tanto, el valor de retorno se puede verificar contra nulo. Había cuatro comprobaciones similares más en la Solución:

  • V668 No tiene sentido probar el puntero de 'colores' contra nulo, ya que la memoria se asignó usando el operador 'nuevo'. La excepción se generará en caso de error de asignación de memoria. aitdecoder.cpp 182
  • V668 No tiene sentido probar el puntero de 'píxeles' contra nulo, ya que la memoria se asignó usando el operador 'nuevo'. La excepción se generará en caso de error de asignación de memoria. aitencoder.cpp 157
  • V668 No tiene sentido probar el puntero de 'colores' contra nulo, ya que la memoria se asignó usando el operador 'nuevo'. La excepción se generará en caso de error de asignación de memoria. aitencoder.cpp 221
  • V668 No tiene sentido probar el puntero de 'bytes' contra nulo, ya que la memoria se asignó usando el operador 'nuevo'. La excepción se generará en caso de error de asignación de memoria. aitencoder.cpp 275

Las condiciones excesivas son comunes para ambos lenguajes de programación:

if (bitmapLock && bitmap)
{
  if(bitmapLock)
  {
    bitmapLock->Release();
    bitmapLock = NULL;
  }
}

V571 Control recurrente. La condición 'bitmapLock' ya se verificó en la línea 104. aitdecoder.cpp 106

Algunos programadores de C# no saben que las siguientes dos operaciones sobre el tipo Nullable son equivalentes:

  • _isInDesignMode !=nulo
  • _isInDesignMode.HasValue

Por eso escriben los siguientes cheques:

if (_isInDesignMode != null && _isInDesignMode.HasValue)

Al mismo tiempo, a la gente de C++ le gusta hacer verificaciones inútiles contra nulo, antes de liberar la memoria asignada por la dirección a la que apunta.

static HRESULT OutputColorContext(....)
{
  ....
  if (pixels)
    delete[] pixels;
  ....
}

V809 No se requiere verificar que un valor de puntero no sea NULL. La marca "si (píxeles)" se puede eliminar. aitencoder.cpp 189

static HRESULT OutputBitmapPalette(....)
{
  ....
  if (colors)
    delete[] colors;
  ....
}

V809 No se requiere verificar que un valor de puntero no sea NULL. El cheque 'si (colores)' se puede eliminar. aitencoder.cpp 241

static HRESULT OutputColorContext(....)
{
  if (bytes)
    delete[] bytes;
}

V809 No se requiere verificar que un valor de puntero no sea NULL. La verificación 'si (bytes)' se puede eliminar. aitencoder.cpp 292

2. Error de lógica

El siguiente código muestra una situación bastante interesante de comparación lógica, aunque no dirías eso.

STDMETHODIMP AitDecoder::QueryCapability(....)
{
  ....
  // If this is our format, we can do everything
  if (strcmp(bh.Name, "AIT") == 0)
  {
     *pCapability = 
       WICBitmapDecoderCapabilityCanDecodeAllImages ||
       WICBitmapDecoderCapabilityCanDecodeThumbnail ||
       WICBitmapDecoderCapabilityCanEnumerateMetadata ||
       WICBitmapDecoderCapabilitySameEncoder;
  }
  ....
}

V560 Una parte de la expresión condicional siempre es verdadera. aitdecoder.cpp 634

El diagnóstico pensó que una parte de la condición siempre es verdadera y es realmente correcta, como las palabras WICBitmapDecoderCapabilityCanDecodeXXX son solo enum valores con el nombre WICBitmapDecoderCapabilities :

enum WICBitmapDecoderCapabilities
{
  WICBitmapDecoderCapabilitySameEncoder = 0x1,
  WICBitmapDecoderCapabilityCanDecodeAllImages = 0x2,
  WICBitmapDecoderCapabilityCanDecodeSomeImages = 0x4,
  WICBitmapDecoderCapabilityCanEnumerateMetadata = 0x8,
  WICBitmapDecoderCapabilityCanDecodeThumbnail = 0x10,
  WICBITMAPDECODERCAPABILITIES_FORCE_DWORD = 0x7fffffff
};

Como resultado, tal vez, alguien confundió los símbolos, y en lugar del bit OR “|” escribió OR lógico “||”. A diferencia del compilador de C#, el de C++ no vio ningún problema con él.

3. Error en inicialización y asignación de variables

Por supuesto, después de la refactorización, es posible que tengamos variables que se inicializaron dos veces seguidas.

STDMETHODIMP BaseFrameEncode::WritePixels(....)
{
   result = S_OK;
   ....
   result = factory->CreateBitmapFromMemory(....);
}

V519 A la variable 'resultado' se le asignan valores dos veces seguidas. Quizás esto sea un error. Verificar líneas:269, 279. baseencoder.cpp 279

Cuando las variables se inicializan aún más después de varias líneas de código, podemos entender fácilmente por qué la persona cometió un error. A veces, tales cadenas se escriben sucesivamente:

STDMETHODIMP AitFrameEncode::Commit()
{
   HRESULT result = E_UNEXPECTED;
   result = BaseFrameEncode::Commit();
   ....
}

V519 A la variable 'resultado' se le asignan valores dos veces seguidas. Quizás esto sea un error. Comprobar líneas:320, 321. aitencoder.cpp 321

Conclusión

Hay un punto de vista, que C# está menos sujeto a errores que C++, y en algunos casos es realmente así. Pero un dato interesante es que la mayoría de los errores no están en construcciones específicas, sino en expresiones simples. Por ejemplo, en la condición de si declaración. El analizador de código estático PVS-Studio para C, C++ y C#, le permitirá controlar la calidad del código y hará todo lo posible para protegerlo de los errores fatales que pueden afectar a sus usuarios.