Pregunta

Actualmente estoy trabajando en portar una aplicación existente Delphi 5 a Delphi 2010.

Es una DLL multiproceso (donde los hilos son generados por Outlook) que se carga en Outlook. Cuando se compila a través de Delphi 2010, siempre que cierre una forma me encuentro con una "operación puntero no válido" dentro de TMonitor.Destroy ... el que está en system.pas, es decir.

Como se trata de una aplicación existente y un poco compleja, tengo un mucho de direcciones para buscar en, y la ayuda de Delphi ni siquiera documentar apenas documentos Este particular TMonitor clase para empezar (he seguido a algunos puestos Allen Bauer con información adicional) ... así que pensé que primero pediría alrededor si alguien había encontrado antes o tenía alguna sugerencia sobre lo que podría causar este problema. Para el registro: No estoy usando la funcionalidad TMonitor explícitamente en mi código, estamos hablando de un puerto recta de Delphi 5 código aquí

.

Editar pila de llamadas en el momento en que el problema se produce:

System.TMonitor.Destroy
System.TObject.Free
Forms.TCustomForm.CMRelease(???)
Controls.TControl.WndProc(???)
Controls.TWinControl.WndProc((45089, 0, 0, 0, 0, 0, 0, 0, 0, 0))
Forms.TCustomForm.WndProc(???)
Controls.TWinControl.MainWndProc(???)
Classes.StdWndProc(15992630,45089,0,0)
Forms.TApplication.ProcessMessage(???)
¿Fue útil?

Solución

El puntero a la instancia System.Monitor de cada objeto se almacena después de que todos los campos de datos. Si se escribe demasiados datos para el último campo de un objeto que podría suceder que se escribe un valor falso a la dirección del monitor, lo cual muy probablemente conduciría a un accidente cuando el destructor del objeto intenta destruir el monitor falso. Podrías comprobar de esto es la dirección nil en el método BeforeDestruction de sus formas, para un puerto recta Delphi 5 no debe haber ningún monitores asignados. Algo así como

procedure TForm1.BeforeDestruction;
var
  MonitorPtr: PPMonitor;
begin
  MonitorPtr := PPMonitor(Integer(Self) + InstanceSize - hfFieldSize + hfMonitorOffset);
  Assert(MonitorPtr^ = nil);
  inherited;
end;

Si esto es un problema en su código original debe ser capaz de detectar que en la versión de Delphi 5 de su DLL utilizando el administrador de memoria FastMM4 con todos los controles activados. Otoh esto también podría ser causado por el aumento de tamaño de los datos de caracteres Unicode se basa en, y en ese caso sería sólo se manifiesta en la DLL construye utilizando Delphi 2009 o 2010. Todavía sería una buena idea utilizar la última FastMM4 con todos los cheques.

Editar

Desde el seguimiento de la pila parece que el monitor está en efecto asignado. Para saber por qué me gustaría utilizar un punto de interrupción de datos. No he sido capaz de hacer que funcionen con Delphi 2009, pero se puede hacer fácilmente con WinDbg.

En el controlador OnCreate de su formulario de poner lo siguiente:

var
  MonitorPtr: PPMonitor;
begin
  MonitorPtr := PPMonitor(Integer(Self) + InstanceSize - hfFieldSize + hfMonitorOffset);
  MessageDlg(Format('MonitorPtr: %p', [pointer(MonitorPtr)]), mtInformation,
    [mbOK], 0);
  DebugBreak;
  // ...

Ahora carga WinDbg y abrir y ejecutar el proceso que llama a la DLL. Cuando se crea la forma de un cuadro de mensaje le mostrará la dirección de la instancia del monitor. Anote la dirección y haga clic en OK. El depurador se van a plantear, y establecer un punto de interrupción en el acceso de escritura a ese puntero, así:

  

ba w4 A32D00

reemplazando A32D00 con la dirección correcta desde el cuadro de mensaje. Continuar la ejecución y el depurador debe golpear el punto de interrupción cuando el monitor se le asigna. El uso de las distintas vistas del depurador (módulos, temas, pila) se puede obtener información importante sobre el código que escribe en esa dirección.

Otros consejos

Una operación puntero no válido significa que su programa ha intentado liberar un puntero, pero no era una de las tres cosas mal con él:

  • Se asigna por algún otro administrador de memoria.
  • Ya se había liberado una vez antes.
  • Nunca había sido asignado por nada.

Es poco probable que usted tendría múltiples administradores de memoria asignación TMonitor registros , así que creo que podemos descartar la primera posibilidad.

En cuanto a la segunda posibilidad, si hay una clase en su programa que, o bien no tiene un destructor personalizado o que no libera la memoria en su destructor, entonces la primera cancelación de asignación de memoria real de ese objeto podría estar en TObject , donde se libera el monitor del objeto. Si usted tiene una instancia de esa clase y se intenta liberar dos veces, este problema podría aparecer en la forma de una excepción en TMonitor. Busque errores doble libres en su programa. Los href="http://docwiki.embarcadero.com/RADStudio/en/Configuring_the_Memory_Manager" en FastMM le puede ayudar con eso. Además, cuando llegue a esa excepción, utilice el pila de llamadas para averiguar cómo ha llegado a destructor de TMonitor.

Si la tercera posibilidad es la causa, entonces usted tiene una corrupción de memoria. Si tiene código que hace suposiciones sobre el tamaño de un objeto, entonces eso podría ser la causa. TObject es de cuatro bytes más grande a medida de Delphi 2009. Siempre utilice el InstanceSize método para obtener el tamaño de un objeto; no sólo tiene que añadir el tamaño de todos sus campos o utilizar un número mágico.

Usted dice que los hilos son creados por Outlook. Se ha marcado el href="http://docwiki.embarcadero.com/VCL/en/System.IsMultiThread" rel="nofollow noreferrer"> IsMultithread variable global

Después de mucho excavar resulta que yo estaba haciendo un buen (léase: horrible, pero ha estado haciendo su trabajo apropiadamente en nuestra delphi 5 aplicaciones para las edades )

PClass(TForm)^ := TMyOwnClass 

algún lugar en lo profundo de las entrañas de nuestra estructura de aplicaciones. Al parecer Delphi 2010 tiene alguna clase de inicialización para inicializar el "campo del monitor" que ahora no sucedió, haciendo que el RTL para tratar de "liberar el SyncObject" sobre la destrucción debido a la forma getFieldAddress devuelve un valor no nulo. Uf.

La razón ¿Por qué que estábamos haciendo este truco en el primer lugar era porque quería cambiar automáticamente los CreateParams en todos los casos de forma, para lograr una forma de tamaño variable iconless. Voy a abrir una nueva pregunta sobre cómo hacer esto sin cortes RTL-ruptura (y por ahora simplemente añadirá un bonito icono brillante a las formas).

Voy a marcar la sugerencia de Mghie como la respuesta, porque me (y cualquiera que lea este hilo) ha proporcionado una gran cantidad de conocimiento. Gracias a todos por contribuir!

Hay dos TMonitor en Delphi:

  1. System.TMonitor; que es un registro, y se usa para la sincronización de hilo.
  2. Forms.TMonitor; que es una clase que representa un monitor conectado (dispositivo de pantalla).

System.TMonitor se añade a Delphi Delphi desde 2009; por lo que si usted está transfiriendo un código de Delphi 5, lo que estaba usando era su código Forms.TMonitor, no System.TMonitor.

Creo que se hace referencia al nombre de la clase y sin nombre de la unidad en su código, y que está haciendo la confusión.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top