Frage

Im Moment arbeite ich auf eine vorhandene Delphi 5-Anwendung Delphi portiert 2010.

Es ist ein Multi-Thread-DLL (wobei die Themen von Outlook hervorgebracht werden), dass Lasten in Outlook. Wenn durch Delphi 2010 kompiliert, wenn ich ein Formular schließe ich in einen „ungültigen Zeiger Betrieb“ laufe innerhalb TMonitor.Destroy ... der in system.pas, das ist.

Da dies eine bestehende und irgendwie komplexe Anwendung ist, ich habe ein Los von Richtungen zu schauen, und der delphi Hilfe nicht einmal Dokument kaum Dokumente dieser besondere TMonitor Klasse beginnen (ich es zu einigen alle Bauer Beiträge mit zusätzlichen Informationen verfolgt) ... so dass ich dachte ich zum ersten Mal umhören würde, wenn jemand vorher begegnet war oder hatte irgendwelche Vorschläge, was dieses Problem verursachen könnte. Für das Protokoll: Ich bin nicht die TMonitor Funktionalität explizit in meinem Code verwenden, sprechen wir einen gerade Hafen von Delphi 5-Code hier

.

Bearbeiten Aufrufhierarchie im Moment das Problem auftritt:

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(???)
War es hilfreich?

Lösung

Der Zeiger auf die System.Monitor Instanz jedes Objektes wird nach allen Datenfeldern gespeichert. Wenn Sie zu viele Daten auf das letzte Feld eines Objekts schreiben könnte es passieren, dass Sie einen falschen Wert an die Adresse des Monitors schreiben, die höchstwahrscheinlich zu einem Absturz führen würde, wenn der Destruktor des Objekts versucht, den falschen Monitor zu zerstören. Sie könnten für diese Adresse Wesen nil im BeforeDestruction Methode Ihrer Formulare überprüfen, für eine gerade Delphi 5-Port sollte nicht zugewiesen alle Monitore. So etwas wie

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

Wenn dies ein Problem in Ihrem ursprünglichen Code ist, sollten Sie in der Lage sein, es mit allen Kontrollen unter Verwendung des FastMM4 Speichermanagers in der Delphi-5-Version der DLL zu erkennen aktiviert. OTOH könnte dies auch durch die Zunahme der Größe Zeichendaten verursacht werden, in Unicode-Versionen, und in diesem Fall wäre es nur manifest in DLL-Builds mit Delphi 2009 oder 2010. Es wäre immer noch eine gute Idee, die neueste FastMM4 mit allen Kontrollen zu verwenden.

Edit:

Von Ihrem Stack-Trace sieht es aus wie der Monitor tatsächlich zugeordnet ist. Um herauszufinden, warum würde ich einen Datenhaltepunkt verwenden. Ich habe sie nicht die Arbeit mit Delphi 2009 konnte machen, aber man kann es leicht mit WinDbg tun.

In der OnCreate Handler des Formulars setzen Sie die folgenden:

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

Jetzt WinDbg und offen laden und den Prozess ausführen, die Ihre DLL aufruft. Wenn das Formular ein Meldungsfeld erstellt wird, zeigen Ihnen die Adresse der Monitor-Instanz. Schreiben Sie die Adresse nach unten, und klicken Sie auf OK. Der Debugger wird kommen, und Sie einen Haltepunkt auf dem Schreibzugriff auf diesen Zeiger, etwa so:

  

ba w4 A32D00

A32D00 mit der richtigen Adresse aus dem Nachrichtenfeld zu ersetzen. Weiter die Ausführung und der Debugger sollte den Haltepunkt erreicht, wenn der Monitor zugewiesen wird. Mit den verschiedenen Debugger-Ansichten (Module, Threads, Stack) Sie wichtige Informationen über den Code erhalten, dass Schreiben an diese Adresse.

Andere Tipps

Ein ungültiger Zeiger Betrieb bedeutet, dass Ihr Programm versucht, einen Zeiger zu befreien, aber es war eines der drei Dinge falsch mit ihm:

  • Es wurde von einem anderen Speichermanager zugewiesen.
  • Es war bereits befreit worden einmal vor.
  • Es war nie etwas zugewiesen.

Es ist unwahrscheinlich, dass Sie die Zuweisung mehrerer Speichermanager haben würde TMonitor Aufzeichnungen , so dass ich denke, wir können die erste Möglichkeit auszuschließen.

Wie für die zweite Möglichkeit, wenn es eine Klasse in Ihrem Programm ist, dass entweder kein eigenes destructor hat oder dass frei keinen Speicher in seinem destructor, dann ist die erste tatsächliche Speicherfreigabe für das Objekt konnte in TObject sein , wo es befreit den Monitor-Objekt. Wenn Sie eine Instanz dieser Klasse haben und Sie versuchen, es zweimal zu befreien, könnte das Problem in Form einer Ausnahme in TMonitor erscheinen. Geben Sie für Doppelfreie Fehler in Ihrem Programm. Die Debugging-Optionen in FastMM können Ihnen dabei helfen. Auch wenn Sie diese Ausnahme erhalten, verwenden Sie die Call-Stack um herauszufinden, wie Sie TMonitor Destruktor bekommen.

Wenn die dritte Möglichkeit, die Ursache ist, dann haben Sie eine Speicherbeschädigung. Wenn Sie Code haben, Annahmen über die Größe eines Objekts macht, dann könnte das die Ursache sein. TObject ist vier Bytes größer als von Delphi 2009 Verwenden Sie immer die InstanceSize Methode die Größe eines Objekts zu erhalten; nicht nur die Größe aller seiner Felder hinzufügen oder eine magische Zahl verwenden.

Sie sagen, dass die Themen von Outlook erstellt werden. Haben Sie den IsMultithread globalen Variable? Ihr Programm normalerweise setzt es auf True, wenn es einen Thread erstellt, aber wenn du nicht derjenige bist Fäden zu schaffen, wird es bei den Standard False-Wert bleiben, das anzeigt, ob der Speichermanager bothers wirkt sich auf seine globalen Daten schützt Strukturen bei Zuweisung und Freigabe . Legen Sie es auf True in Ihrem DPR-Datei des Hauptprogrammblock.

Nach viel Graben es stellt sich heraus, dass ich eine schöne tat (sprich: erschreckend, , aber es ist ihre Aufgabe in unserer delphi 5 Apps für Kinder ab richtig tun )

PClass(TForm)^ := TMyOwnClass 

irgendwo tief in den Eingeweiden unseres Anwendungs-Framework. Offenbar Delphi 2010 hat einige Klasseninitialisierung den „Monitor Feld“, die nun nicht geschehen, so dass das RTL zu versuchen und „befreit die SyncObject“ auf Form Zerstörung initialisiert werden, da getFieldAddress einen Nicht-Null-Wert zurückgegeben. Ugh.

Der Grund Warum wir in erster Linie diesen Hack taten, weil ich automatisch wollte die CreateParams Instanzen auf allen Form ändern, eine iconless veränderbare Form zu erreichen. Ich werde eine neue Frage auf eröffnen, wie dies ohne rtl brech Hacks zu tun (und jetzt wird einfach ein schönes glänzendes Symbol zu den Formen hinzufügen).

werde ich mghie Vorschlag als Antwort markieren, weil es mir zur Verfügung gestellt hat (und jemand diesen Thread lesen) mit einer sehr großen Menge an Einsicht. Vielen Dank an alle für einen Beitrag!

Es gibt zwei TMonitor in Delphi:

  1. System.TMonitor; das ist ein Datensatz, und ist für die Thread-Synchronisation verwendet.
  2. Forms.TMonitor; das ist eine Klasse eines angeschlossenen Monitor (Anzeigeeinrichtung) darstellt.

System.TMonitor ist Delphi seit Delphi 2009 hinzugefügt; also, wenn Sie einen Code von Delphi 5 portieren, was Ihr Code Forms.TMonitor war verwendet, nicht System.TMonitor.

Ich denke, der Klassenname ohne Gerätenamen in Ihrem Code verwiesen wird, und dass die Verwirrung macht.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top