Domanda

Al momento sto lavorando sul porting di un'applicazione Delphi 5 esistente per Delphi 2010.

E 'una DLL multithread (dove i fili sono generati da Outlook) che carica in Outlook. Quando viene compilato attraverso Delphi 2010, ogni volta che chiudo una forma mi imbatto in una "operazione di puntatore non valido" dentro TMonitor.Destroy ... quello in system.pas, che è.

Poiché si tratta di un'applicazione esistente e un pò complesso, ho un molto di direzioni di esaminare, e l'aiuto di Delphi non ha nemmeno documento appena documenti questo particolare classe TMonitor per cominciare (ho tracciato ad alcuni post Allen Bauer con informazioni aggiuntive) ... così ho pensato di chiedere prima in giro se qualcuno avesse incontrato prima o ha avuto qualche suggerimento su che cosa potrebbe causare questo problema. Per la cronaca: non sto usando la funzionalità TMonitor esplicitamente nel mio codice, stiamo parlando di una porta dritto di Delfi 5 codice qui

.

Modifica Callstack in questo momento il problema si verifica:

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(???)
È stato utile?

Soluzione

Il puntatore all'istanza System.Monitor di ciascun oggetto viene memorizzato dopo che tutti i campi di dati. Se si scrive troppi dati per l'ultimo campo di un oggetto potrebbe accadere che si scrive un valore fasullo all'indirizzo del monitor, che molto probabilmente portare ad un crash quando il distruttore dell'oggetto tenta di distruggere il monitor fasullo. Si potrebbe verificare la presenza di questo indirizzo essendo nil nel metodo BeforeDestruction delle vostre forme, per una porta dritto Delphi 5 Non ci dovrebbe essere qualsiasi monitor assegnati. Qualcosa di simile

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

Se questo è un problema nel codice originale si dovrebbe essere in grado di rilevare che nella versione di Delphi 5 della DLL utilizzando il gestore di memoria FastMM4 con tutti i controlli attivati. OTOH questo potrebbe anche essere causato dalla crescita dimensione dei dati di carattere in Unicode costruisce, e in questo caso sarebbe solo manifesta nella DLL costruisce utilizzando Delphi 2009 o 2010. Sarebbe comunque una buona idea di utilizzare l'ultima FastMM4 con tutti i controlli.

Modifica

Dal tuo stack trace sembra che il monitor è infatti assegnato. Per scoprire perché vorrei usare un punto di interruzione di dati. Non sono stato in grado di farli funzionare con Delphi 2009, ma lo si può fare facilmente con WinDbg.

Nel gestore OnCreate del modulo inserire il seguente:

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

Ora caricare WinDbg e aprire ed eseguire il processo che chiama la DLL. Quando si crea la forma di una finestra di messaggio vi mostrerà l'indirizzo dell'istanza monitor. Annotare l'indirizzo, e fare clic su OK. Il debugger verrà in su, e impostare un punto di interruzione accesso in scrittura a tale puntatore, in questo modo:

  

ba W4 A32D00

sostituendo A32D00 con l'indirizzo corretto dalla finestra di messaggio. Continuare l'esecuzione, e il debugger dovrebbe colpire il punto di interruzione quando il monitor viene assegnato. Utilizzando le varie viste debugger (moduli, discussioni, stack) si possono ottenere informazioni importanti sul codice che scrive a questo indirizzo.

Altri suggerimenti

Un'operazione puntatore non valido significa che il programma ha tentato di liberare un puntatore, ma non v'è stata una delle tre cose sbagliate con esso:

  • E 'stato assegnato da qualche altro gestore della memoria.
  • Si era già stato liberato una volta prima.
  • Non era mai stato assegnato da nulla.

E 'improbabile che ci si dispone di più gestori di memoria assegnazione TMonitor record , quindi penso che possiamo escludere la prima possibilità.

Per quanto riguarda la seconda possibilità, se c'è una classe nel vostro programma che o non ha un distruttore personalizzata o che non libera alcuna memoria nel suo distruttore, poi la prima deallocazione memoria effettiva per tale oggetto potrebbe essere in TObject , dove si libera del monitor dell'oggetto. Se si dispone di un'istanza di quella classe e si tenta di liberare due volte, il problema potrebbe apparire sotto forma di un'eccezione in TMonitor. Cercare gli errori doppio liberi nel vostro programma. I href="http://docwiki.embarcadero.com/RADStudio/en/Configuring_the_Memory_Manager" in FastMM può aiutare con questo. Inoltre, quando si ottiene tale eccezione, utilizzare la chiamare pila per scoprire come è arrivata a distruttore di TMonitor.

Se la terza possibilità è la causa, allora si ha il danneggiamento della memoria. Se si dispone di codice che rende le ipotesi circa le dimensioni di un oggetto, quindi che potrebbe essere la causa. TObject è quattro byte più grande come di Delphi 2009. Usare sempre la InstanceSize metodo per ottenere le dimensioni di un oggetto; non basta sommare le dimensioni di tutti i suoi campi o utilizzare un numero magico.

Dici i fili sono creati da Outlook. Avete impostato il href="http://docwiki.embarcadero.com/VCL/en/System.IsMultiThread" rel="nofollow noreferrer"> IsMultithread variabile globale

Dopo un sacco di scavare si scopre che stavo facendo una bella (leggi: orribile, ma è stato correttamente facendo il suo lavoro nel nostro Delphi 5 applicazioni per le età )

PClass(TForm)^ := TMyOwnClass 

da qualche parte in fondo nelle viscere del nostro framework di applicazioni. A quanto pare Delphi 2010 ha qualche inizializzazione di classe per inizializzare il campo "Monitor", che ora non è accaduto, causando la RTL per cercare di "liberare la SyncObject" sulla distruzione forma perché getFieldAddress ha restituito un valore non-zero. Ugh.

Il motivo per perché che stavamo facendo questo hack in primo luogo era perché volevo cambiare automaticamente i CreateParams su tutte le istanze di forma, per ottenere una forma ridimensionabile iconless. Aprirò una nuova domanda su come fare questo senza hack RTL-rottura (e per ora sarà sufficiente aggiungere un bel lucido icona alle forme).

I segnerà il suggerimento di Mghie come la risposta, perché mi (e qualcuno leggendo questa discussione) ha fornito una grande quantità di insight. Grazie a tutti per aver contribuito!

Ci sono due TMonitor a Delfi:

  1. System.TMonitor; che è un disco, ed è usato per la sincronizzazione thread.
  2. Forms.TMonitor; che è una classe che rappresenta un monitor collegato (dispositivo di visualizzazione).

System.TMonitor viene aggiunto Delphi dal Delphi 2009; quindi se siete porting un codice da Delphi 5, ciò che il codice è stato utilizzato è stato Forms.TMonitor, non System.TMonitor.

Credo che il nome della classe viene fatto riferimento, senza nome dell'unità nel codice, e che sta facendo la confusione.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top