Question

Je travaille actuellement sur le portage d'une application existante Delphi 5 à Delphi 2010.

Il est une DLL multithread (où les fils sont engendrés par Outlook) qui se charge dans Outlook. Lorsque compilé par Delphi 2010, chaque fois que je ferme une forme que je lance dans une « opération de pointeur non valide » à l'intérieur TMonitor.Destroy ... celui System.pas, ce qui est.

Comme il est une application existante et un peu complexe, j'ai beaucoup de directions pour se pencher sur, et l'aide delphi ne documente même pas à peine les documents Particulière classe TMonitor pour commencer (je traçais à certains postes Allen Bauer avec des informations supplémentaires) ... donc je pensais que je voudrais d'abord demander autour si quelqu'un avait rencontré auparavant ou avait des suggestions sur ce qui pourrait causer ce problème. Pour mémoire: Je ne suis pas en utilisant la fonctionnalité TMonitor explicitement dans mon code, nous parlons d'un port droit de Delphi 5 code ici

.

Modifier Callstack au moment où le problème se produit:

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(???)
Était-ce utile?

La solution

Le pointeur vers l'instance de System.Monitor de chaque objet est stocké après que tous les champs de données. Si vous écrivez trop de données sur le dernier champ d'un objet, il peut arriver que vous écrivez une valeur fausse à l'adresse du moniteur, qui conduirait probablement à un accident lorsque le destructeur de l'objet tente de détruire le moniteur faux. Vous pouvez vérifier cette adresse étant nil dans la méthode BeforeDestruction de vos formes, pour un port droit Delphi 5 il ne devrait pas y avoir de moniteurs affectés. Quelque chose comme

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

Si cela est un problème dans votre code d'origine, vous devriez être en mesure de le détecter dans la version Delphi 5 de votre DLL en utilisant le gestionnaire de mémoire FastMM4 avec tous les contrôles activés. OTOH cela pourrait aussi être causée par l'augmentation de la taille des données de caractères Unicode construit, et dans ce cas dans DLL construit en utilisant Delphi 2009 ou 2010. manifesterait que ce serait encore une bonne idée d'utiliser la dernière FastMM4 avec tous les chèques.

Modifier

A partir de votre trace de la pile, il semble que le moniteur est en effet attribué. Pour savoir pourquoi j'utiliser un point d'arrêt de données. Je ne l'ai pas été en mesure de les faire fonctionner avec Delphi 2009, mais vous pouvez le faire facilement avec WinDbg.

Dans le gestionnaire de OnCreate de votre formulaire mis les éléments suivants:

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

Maintenant charger WinDbg et ouvrez et exécutez le processus qui appelle votre DLL. Lorsque le formulaire est créé une boîte de message vous indiquera l'adresse de l'instance de contrôle. Notez l'adresse, puis cliquez sur OK. Le débogueur viendra, et que vous définissez un point d'arrêt sur l'accès en écriture à ce pointeur, comme suit:

  

ba W4 A32D00

A32D00 remplaçant l'adresse correcte dans la zone de message. Poursuivre l'exécution et le débogueur doit frapper le point d'arrêt lorsque le moniteur est attribué. En utilisant les différentes vues du débogueur (modules, threads, pile) vous pouvez obtenir des informations importantes sur le code qui écrit à cette adresse.

Autres conseils

Une opération de pointeur non valide signifie que votre programme a tenté de libérer un pointeur, mais il y avait une des trois choses mal à cela:

  • Il a été affecté par un autre gestionnaire de mémoire.
  • Il avait déjà été libéré une fois.
  • Il n'a jamais été affecté par quoi que ce soit.

Il est peu probable que vous auriez des gestionnaires de mémoire multiples TMonitor enregistrements , donc je pense que nous pouvons exclure la première possibilité.

En ce qui concerne la deuxième possibilité, s'il y a une classe dans votre programme qui soit ne dispose pas d'un destructor personnalisé ou qui ne libère pas de mémoire dans son destructor, la première désallocation de mémoire réelle pour cet objet pourrait être TObject , où il libère le moniteur de l'objet. Si vous avez une instance de cette classe et que vous essayez de libérer deux fois, ce problème pourrait apparaître sous la forme d'une exception dans TMonitor. Recherchez les erreurs double-free dans votre programme. Les FastMM peut vous aider. De plus, quand vous obtenez cette exception, utilisez le appeler pile pour savoir comment vous avez obtenu au destructor de TMonitor.

Si la troisième possibilité est la cause, alors vous avez la corruption de la mémoire. Si vous avez du code qui fait des hypothèses sur la taille d'un objet, alors cela pourrait être la cause. TObject est quatre octets plus que de Delphi 2009. Toujours utiliser le InstanceSize méthode pour obtenir la taille d'un objet; ne sont pas seulement ajouter la taille de tous ses champs ou utiliser un nombre magique.

Vous dites que les threads sont créés par Outlook. Avez-vous défini la variable globale IsMultithread ? Votre programme définit normalement à vrai quand il crée un thread, mais si vous n'êtes pas une création de threads, il restera à sa valeur par défaut False, ce qui affecte si le gestionnaire de mémoire prend la peine de protège ses structures de données globales lors de l'allocation et désallocation . Réglez-le sur True dans bloc de programme principal de votre fichier RDP.

Après beaucoup de creuser il se trouve que je faisais une belle (lire: horrifiant, mais il a été bien fait son travail dans notre delphi 5 applications pour les âges )

PClass(TForm)^ := TMyOwnClass 

quelque part au fond dans les entrailles de notre cadre d'application. Apparemment, Delphi 2010 a une initialisation de classe pour initialiser le champ « moniteur » maintenant n'a pas eu lieu, ce qui provoque la RTL pour essayer de « libérer le SyncObject » sur la destruction de la forme parce que getFieldAddress a retourné une valeur non nulle. Ugh.

La raison pourquoi nous faisions ce hack en premier lieu était parce que je voulais changer automatiquement les CreateParams sur toutes les instances de formulaire, pour obtenir un formulaire de iconless redimensionnable. Je vais ouvrir une nouvelle question sur la façon de le faire sans hacks rtl révolutionnaire (et pour l'instant me contenterai d'ajouter une belle icône brillante aux formes).

Je marquerai la suggestion de Mghie comme la réponse, car il m'a fourni (et toute personne lisant ce fil) avec une très grande quantité de perspicacité. Merci à tous pour contribuer!

Il y a deux TMonitor à Delphes:

  1. System.TMonitor; qui est une fiche, et est utilisé pour la synchronisation du fil.
  2. Forms.TMonitor; qui est une classe représentant un moniteur connecté (dispositif d'affichage).

System.TMonitor est ajouté à Delphi depuis Delphi 2009; donc si vous portez un code de Delphi 5, ce que votre code utilisait était Forms.TMonitor, pas System.TMonitor.

Je pense que le nom de la classe est référencée sans nom de l'unité dans votre code, et qui fait la confusion.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top