violations address delphi access-violation

access violations address delphi



¿Depurando errores de violación de acceso? (4)

¿Qué consejos puede compartir para ayudar a localizar y corregir violaciones de acceso al escribir aplicaciones en Delphi?

Creo que las violaciones de acceso generalmente se deben al intentar acceder a algo en la memoria que aún no se ha creado, como un objeto, etc.

Me resulta difícil identificar qué desencadena las violaciones de acceso y luego dónde realizar los cambios necesarios para intentar detenerlos o corregirlos.

Un ejemplo es un proyecto personal en el que estoy trabajando ahora. Estoy almacenando en la propiedad TTreeView Node.Data algunos datos para cada nodo. Los nodos se pueden seleccionar y exportar múltiples (la exportación se repite a través de cada nodo seleccionado y guarda datos específicos en un archivo de texto; la información guardada en el archivo de texto es la que se almacena en nodes.data). Los archivos también se pueden importar a la vista de árbol (guardando el contenido de los archivos de texto en el nodo.data).

El problema en ese ejemplo es que si importo archivos a Treeview y luego los exporto, funciona perfectamente. Sin embargo, si agrego un nodo en tiempo de ejecución y los exporto, obtengo:

"Infracción de acceso en la dirección 00405772 en el módulo ''Project1.exe''. Lea la dirección 00000388."

Mi opinión sobre eso debe ser la forma en que asigno los datos a los nodos creados, tal vez de manera diferente a la forma en que los asigno cuando se importan, pero todo me parece bien. La violación de acceso solo se muestra al exportar, y esto nunca sucede con los archivos importados.

NO estoy buscando una solución al ejemplo anterior, sino principalmente consejos / sugerencias sobre cómo encontrar y corregir este tipo de errores. A menudo no obtengo violaciones de acceso, pero cuando lo hago son muy difíciles de localizar y corregir.

Así que los consejos y sugerencias serían muy útiles.


Encuentro que las violaciones de acceso realmente difíciles de encontrar no siempre ocurren mientras estoy ejecutando un depurador. Peor aún, les pasan a los clientes y no a mí. La respuesta aceptada menciona esto, pero realmente creo que debería dársele más detalles: MadExcept proporciona un rastreo de pila que me brinda información valiosa del contexto y me ayuda a ver dónde falla el código, o tiene excepciones no manejadas (no es solo por violaciones de acceso). Incluso proporciona una forma para que los clientes le envíen por correo electrónico los informes de errores desde su programa. Eso conduce a más violaciones de acceso encontradas y solucionadas, informadas por sus evaluadores beta o sus usuarios.

En segundo lugar, me he dado cuenta de que las sugerencias y advertencias del compilador en realidad detectan algunos de los problemas comunes. Elimine las sugerencias y advertencias y puede encontrar muchas violaciones de acceso y otros problemas sutiles. Olvidar declarar sus destructores correctamente, por ejemplo, puede llevar a una advertencia del compilador, pero a problemas serios en el tiempo de ejecución.

En tercer lugar, he encontrado herramientas como Pascal Analyzer de Peganza, y la función de auditorías y métricas en algunas ediciones de Delphi, puede ayudarlo a encontrar áreas de su código que tienen problemas. Como un ejemplo concreto, Pascal Analyzer ha encontrado lugares donde olvidé hacer algo importante, lo que provocó un bloqueo o una infracción de acceso.

Cuarto, difícilmente puedes vencer la técnica de hacer que otro desarrollador critique tu código. Puede que te sientas un poco avergonzado después, pero aprenderás algo, con suerte, y mejorarás haciendo lo que estás haciendo. Lo más probable es que haya una manera más que una de usar una vista de árbol, y más de una forma de hacer el trabajo que está haciendo, y una mejor arquitectura, y una forma limpia de hacer las cosas dará como resultado un código más confiable que no se rompe cada vez que lo tocas. No existe una lista finita de reglas para producir código limpio, es más bien un esfuerzo de por vida y una cuestión de grados. Te sorprendería que el código de apariencia inocente pueda ser un foco de posibles bloqueos, violaciones de acceso, condiciones de carrera, bloqueos y puntos muertos.


Me gustaría mencionar una herramienta más, que uso cuando otras herramientas no detectan AV. Es SafeMM ( versión más reciente ). Una vez me apuntó al pequeño procedimiento de 5 líneas. Y tuve que mirar más de 10 minutos para ver el AV que ocurrió allí. Probablemente ese día mis habilidades de programación no estaban al máximo, pero ya sabes, las cosas malas tienden a suceder exactamente en esos días.


Significa que su código está accediendo a alguna parte de la memoria a la que no se le permite. Eso generalmente significa que tienes un puntero u referencia de objeto que apunta a la memoria incorrecta. Tal vez porque no está inicializado o ya está liberado.

Utilice un depurador, como Delphi. Le indicará en qué línea de código ocurrió el AV. A partir de ahí, resuelva su problema mirando la pila de llamadas y las variables locales, etc. A veces también ayuda si compila con las DCU de depuración.

Si no tiene un depurador porque solo sucede en el lado del cliente, es posible que desee usar MadExcept o JclDebug para registrar la excepción con la pila de llamadas y que se la envíen. Le da menos detalles, pero puede apuntarle en la dirección correcta.

Existen algunas herramientas que podrían ser capaces de encontrar este tipo de problemas antes al realizar una comprobación más agresiva. El administrador de memoria FastMM tiene tales opciones.

EDITAR

"Infracción de acceso en la dirección 00405772 en el módulo ''Project1.exe''. Lea la dirección 00000388."

Por lo tanto, su problema resulta en un AV en las direcciones 00405772 en el módulo ''Project1.exe''. El depurador de Delphi lo llevará a la línea de código correcta (o usará Buscar error).

Está intentando leer la memoria en la dirección 00000388. Eso es bastante cercano a 00000000 (nil), por lo que probablemente significaría acceder a algún puntero / referencia a una matriz o matriz dinámica que es nula. Si fuera una matriz de bytes, sería el elemento 388. O podría ser un campo de un objeto o registro bastante grande con muchos campos. El objeto o registro puntero / referencia sería nulo.


Solo quiero mencionar otras técnicas de depuración o "protección de código" que no se mencionaron en las respuestas anteriores:

Herramientas "locales":
* Use FastMM en DebugMode: haga que escriba ceros cada vez que asigne memoria. Esto hará que su programa sea LENTAMENTE lento, pero tiene una GRAN oportunidad de encontrar errores, como intentar acceder a un objeto liberado.
* Use FreeAndNil (Obj) en lugar de Obj.Free. Algunos, la gente se quejaba de que esto creaba problemas, pero sin proporcionar un ejemplo claro de dónde podría ocurrir esto. Además, Emarcadero recientemente agregó la recomendación de usar FreeAndNil en su manual (¡por fin!).
* SIEMPRE compile la aplicación en modo Liberar y Liberar. Asegúrese de que las Opciones de proyecto estén configuradas correctamente para el modo de depuración. La configuración de PREDETERMINADA para el modo de depuración NO es correcta / completa, al final no en Delphi XE7 y Tokio. Tal vez algún día establecerán las opciones correctas para el modo de depuración. Entonces, habilita cosas like :

  • " Apilar marcos "
  • "Generación de archivo de mapa (detallado)"
  • "Control de rango",
  • "Información de referencia del símbolo"
  • "Información de depuración"
  • "Comprobación de desbordamiento"
  • "Aserciones"
  • "Depurar DCUs"
  • Desactivar las "optimizaciones del compilador"!

Herramientas de terceros:

  • Use MadShi o EurekaLog (recomendaría MadShi sobre EurekaLog)
  • Utilice ApplicationVerfier de Microsoft