Novembre 2024 | Lun | Mar | Mer | Jeu | Ven | Sam | Dim |
---|
| | | | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | | Calendrier |
|
|
| @ Klaus : DLL_OFF provoque un plantage ! [Résolu] | |
| | Auteur | Message |
---|
papydall
Nombre de messages : 7017 Age : 74 Localisation : Moknine (Tunisie) Entre la chaise et le clavier Date d'inscription : 03/03/2012
| Sujet: @ Klaus : DLL_OFF provoque un plantage ! [Résolu] Dim 25 Fév 2018 - 0:24 | |
| @Klaus dll_off ( après dll_on "kgf") provoque un plantage ! Le code suivant fonctionne parfaitement, mais si on enlève le REM de la dernière ligne, l’erreur est systématiquement au rendez-vous ! - Code:
-
dim WB%,res%,url$ full_space 0 dll_on "kgf" url$ = "http://papydall-panoramic.forumarabia.com/" WB% = dll_call1("WB_Create",handle(0)) res% = dll_call5("WB_Locate",WB%,20,20,width_client(0)-50,height_client(0)-50) res% = dll_call2("WB_Url",WB%,adr(url$)) ' dll_off : ' <--- PANORAMIC_TEMP xxx.exe a cessé de fonctionner
dll_off ne provoque l'erreur qu’avec kgf.dllPour une autre dll, No Problem ! La preuve : le code suivant ne provoque pas d’erreur - Code:
-
dim ret% ,hwnd% hwnd% = handle_canvas(0) dll_on "gdi32" ret% = dll_call5("Ellipse",hwnd%,50,50,150,200) dll_off : ' <--- Pas d'erreur
Dernière édition par papydall le Dim 25 Fév 2018 - 2:30, édité 1 fois | |
| | | Klaus
Nombre de messages : 12331 Age : 75 Localisation : Ile de France Date d'inscription : 29/12/2009
| Sujet: Re: @ Klaus : DLL_OFF provoque un plantage ! [Résolu] Dim 25 Fév 2018 - 0:31 | |
| Normal, Papydall, enfin voyons ! Tu crées un Webrowser qui réside dans KGF.dll. Tu l'attaches à la form 0. Tu le fais naviguer vers une URL. Puis tu veux supprimer la DLL ? Qu'est-ce ue tu fais du WebBrowser ? Si tu fermes la DLL, la référence au WebBrowser qui se trouve dans la form 0, pointe vers une adresse memoire qui a été libérée. et bouuuum ! Violation d'accès mémoire, que ce soit en lecture ou en écriture.
Faudra revoir ta copie...
EDIT
Un peu plus de "fond": 1. si tu crées un objet Panoramic, le fait de fermer le programme provoque toute une réaction en chaîne commandée par Windows: on parcourt la totalité des "enfants" de la fenêtre pour les supprimer, en cascade, et ainsi, ton objet Panoramic sera supprimé (et donc enlevé de la form 0) avant la suppression effective de la form. Et donc, pas de pointeur invalide. 2. ton second code ne crée pas d'objet Windows et ne laisse dont rien en mémoire. Il écrit simplement, via une fonction DLL, dans un canvas qui est la propriété de la form 0 et n'a acun lien avec la DLL. Et donc, on peut décharger la DLL sans problème.
A la lumière de cela, tu vois déjà om est le problème. Le fait de décharger la DLL n'avertit pas la form 0 que l'objet WebBrowser n'existe plus. C'est comme si tu lui retirais une chaise sous le derrière - la chute est inévitable.
Seule solution: supprimer le WenBrowser avec la fonction approprirée avant de décharger la DLL.
Mais si tu as besoin de l'affichage de ce que le WebBrowser montre, il faut le laisser jusqu'au bout et le supprimer, par exemple dans l'évènement ON_CLOSE de la form 0. Ou alors, tu fais une capture d'écran, tu découpes l'image du WebBrowser et tu la charges dans un PICTURE... | |
| | | papydall
Nombre de messages : 7017 Age : 74 Localisation : Moknine (Tunisie) Entre la chaise et le clavier Date d'inscription : 03/03/2012
| Sujet: Re: @ Klaus : DLL_OFF provoque un plantage ! [Résolu] Dim 25 Fév 2018 - 1:13 | |
| DLL_OFF est innocente ! J’ai revu ma copie. Si on tient à DLL_OFF, on doit d’abord supprimer le browser ! - Code:
-
dim WB%,res%,url$ full_space 0 dll_on "kgf" url$ = "http://papydall-panoramic.forumarabia.com/" WB% = dll_call1("WB_Create",handle(0)) res% = dll_call5("WB_Locate",WB%,20,20,width_client(0)-50,height_client(0)-50) res% = dll_call2("WB_Url",WB%,adr(url$)) message "ok" res% = dll_call1("WB_Delete",WB%) dll_off font_bold 0 : font_size 0,24 print_locate 100,100 : print "Fermeture dans 5 s ..." pause 5000 terminate
Et merci pour les explications : Tu es Le maître ! | |
| | | Klaus
Nombre de messages : 12331 Age : 75 Localisation : Ile de France Date d'inscription : 29/12/2009
| Sujet: Re: @ Klaus : DLL_OFF provoque un plantage ! [Résolu] Dim 25 Fév 2018 - 1:56 | |
| De rien, Papydall ! Je me bats tous les jours avec ce genre de problèmes, et je commence à avoir l'habitude ! Tiens, je te présente un autre problème inextricable en apparence. Ca n'a rien à voir, quoique... encore une histoire de violation de mémoire à cause d'un pointeur devenu invalide: Dans la Dll, je mémorise certaines chaînes de caractères en provenance de Panoramic. Noms de fichiers, textes simples, codes, .. enfin, de simples chaînes de caractères. Sauf qu'en Delphi, une chaîne de caractères est tout sauf simple ! En fait, c'est une zone de mémoire dynamique allouée par le Memory Manager intégré dans Delphi (et donc utilisé par Panoramic !) et qui garde évidemment la trace de chaque petite parcelle de mémoire alouée. Et Delphi optimise assez fortement ses traitements. C'est pourquoi une chaîne de caractères est assimilée à un pointeur, associé à un compteur de révérence. Ce compteur indique combien d'instances différentes font référence à la chaîne de caractères en question. Et tant qu'on l'utilise uniquement en lecture, pas de problème. Mais si une partie du programme modifie la chaîne, le Memory Manager crée immédiatement une copie de la chaîne initiale, donne la copie au module qui veut modifier en mémorisant un compteur de référence de 1 pour cette nouvelle chaîne, puis décrémente le compteur de référence de la chaîne d'origine. Et ce n'est que lorsque ce compteur de référence tombe à zéro que la mémoire allouée à cette chaîne est libérée. Ouf ! C'est dit. C'est clair jusque là ? Alors, on continue, si tu veux bien. Panoramic ne sait pas vraiment passer une chaîne de caractères en paramètre à une fonction DLL? C'est dommage, mais c'est comme ça. Et donc, on passe adr(s$). On en a tellement l'habitude qu'on ne se rend même plus compte de ce que ça veut dire. Reprenons: adr$(s$) donne l'adresse, non pas du premier caractère de la chaîne (pourquoi faire simple quand on peut faire compliqué !), mais donne l'adresse d'un mot de 32 bits qui, lui, contient l'adresse du premier caractère de la chaîne ! A partir de cette adresse indirecte, on a tous les octets de la chaîne, et un octet supplémentaire: un octet zéro binaire marquant la fin de la chaîne. C'est ce que d'autres langages appellent le format ASCIZ. Mais ce n'est pas tout ! Les deux mots de 32 bits qui précèdent le premier octet de la chaîne contiennent la longueur de la chaîne sur 32 bits, ainsi que le fameux compteur de référence ! On a donc le format suivant: adr(s$) ==> adresse A 32 bits compteur de référence <=== offset -8 sur l'adresse A 32 bits longueur de la chaîne N <=== offset -4 sur l'adresse A N caractères <=== ici, adresse A ! 1 octet zéro binaire Maintenant, je prends cette chaîne dans ma DLL. Panoramic (et donc le Memory Manager de Delphi étant linké aec Panoramic) n'en sait rien du tout ! Par contre, le Memory Manager linké avec la DLL, lui, en a connaissance et gère donc ""son" compteur de référence. Et tant que j'utilise cette chaîne sans la modifier, et tant que je ne supprime pas l'objet (un équivalent de DLIST en Delphi, par exemple), tout se passe bien. Ce sont simplement des pointeurs qui sont copiés, et comme la mémoire ne change pas, les pointeurs restent valides. Tu commences à sentir d'où vient le vent ? Maintenant, je supprime un des objets (genre DLIST interne, ou un des objets créés dans KGF.dll comme un RichEdit etc), c'est immédiatement la pagaille entre les pointeurs et les compteurs de référence. Il y a forcément, à un moment ou un autre, un des deux Memory Managers qu veut accéder à la mémoire occupée par la chaîne, et c'est le crash. Cela peut se produire beaucoup plus tard que le fait déclencheur, par exemple simplement à l'arrêt du programme. J'ai vraiment galéré longtemps pour trouver la solution. J'ai même demandé de l'aide à des forms spécialisés, et j'ai reçu une solution, complexe mais qui marche. Seulement, c'est un boulot énorme de mettre cela en oeuvre partout dans KGF.dll où ce serait nécessaire. Mais j'ai fini par trouver un moyen simple de "tromper" les deux Memory Managers qui se croient les maîtres du jeu. Je fais maintenant dans la DLL, en Delphi, systématiquement ce qu'en Panoramic, on coderait comme suit: - Code:
-
MaChaineInterne$ = trim$(ChaineTransmiseDePanoramic$+" ") Ca paraît dingue, mais ça marche. L'optimisation du code par le compilateur Delphi n'y voit que du feu, et l'effet net est que mon expression créer une chaîne de caractères interne, temporaire, incrémentant le compteur de référence de la chaîne d'origine puisqu'il y a une référence, et cette chaîne de caractères interne aura le compteur de référence 1 attribué par l'affectation dans la variable MaChaineInterne$. Jusque là, tout va bien. Maintenant, je termine ma fonction DLL qui a fini son boulot. Qu'est-ce qui se passe ? MaChaineInterne$ est, soit une variable globale dont la durée de vie est la durée de chargement de la DLL, soit un stockage interne dans un objet Delphi comme un RichEdit, par exemple. Donc, permanent aussi, tant que l'objet n'est pas supprimé. Tu commences à voir le lien avec ton problème ? Maintenant, je sors de ma fonction (équivalent de RETURN). Qu'est-ce qui se passe ? L'objet Delphi ou la variable globale persistent. Bien. Mais ma chaîne temporaire (le résultat de l'expression, avant affectation dans ma variable) sera libérée par le Memory Manager de la DLL. Et automatiquement, juste avant, cette chaîne temporaire sera cette fois copiée dans la fameuse variable ou dans l'objet Delphi, et le fait de supprimer ensuite cette variable temporaire décrémente également le compteur de référence de la chaîne passée en paramètre. Et Voilà. Le tour est joué: avec uen simple expression "idiote", les deux Memory Managers n'ont pas vu que je garde en réalité une copie de la chaîne d'origine qui a échappé à leur contrôle. Et il n'y a plus de violation de mémoire ! Bon, j'espère que je ne t'ai pas trop saoûlé avec mes élucubrations techniques. Tout ça pour montrer que c'est un problème oh combien fréquent qui me hante tous les jours? Je te fais grâce des multiples variantes mais qui conduisent toutes au même résultat: libérer une partie de mémoire qui est encore référencée ailleurs. | |
| | | papydall
Nombre de messages : 7017 Age : 74 Localisation : Moknine (Tunisie) Entre la chaise et le clavier Date d'inscription : 03/03/2012
| Sujet: Re: @ Klaus : DLL_OFF provoque un plantage ! [Résolu] Dim 25 Fév 2018 - 2:33 | |
| Klaus, même si je ne comprends pas toujours ce que tu écris, j’aime bien te lire. Tu sais comment expliquer d’une manière simple les choses complexes. Merci, merci pour tout ce que tu fais. Celui qui donne des explications aux autres, finit bien par consolider ses connaissances. Alors, tu as intérêt à continuer d’être « le maitre qui-sait-presque-tout-et-qui-n’ignore-presque-rien » J'ai modifié le titre de mon post en y ajoutant [Résolu] | |
| | | silverman
Nombre de messages : 970 Age : 52 Localisation : Picardie Date d'inscription : 18/03/2015
| Sujet: Re: @ Klaus : DLL_OFF provoque un plantage ! [Résolu] Dim 25 Fév 2018 - 10:36 | |
| Klaus, cette explication technique est vraiment très interessante! On n'imagine pas tout ce qui se passe derrière une simple affectation de chaine. Si j'ai bien compris, créer 'MaChaineInterne$' revient à faire comprendre au memorymanager de la dll que 'ChaineTransmiseDePanoramic$' existe! Il travaille ensuite sur une instance de 'ChaineTransmiseDePanoramic$' qui est 'MaChaineInterne$' c'est bien ça? Dans mes dlls, je cré systématiquement une copie de 'ChaineTransmiseDePanoramic$' et je travaille sur la copie, et ensuite je met à jour la chaine originale (copy memory). Sans le savoir, je faisait bien! Que se passerait-il si tu trompais le memorymanager linké à panoramic en incrémentant manuellement de 1 le compteur de référence de 'ChaineTransmiseDePanoramic$'? Tu devrait pouvoir te passer de 'MaChaineInterne$' ensuite, non? | |
| | | Klaus
Nombre de messages : 12331 Age : 75 Localisation : Ile de France Date d'inscription : 29/12/2009
| Sujet: Re: @ Klaus : DLL_OFF provoque un plantage ! [Résolu] Dim 25 Fév 2018 - 11:34 | |
| - Citation :
- Que se passerait-il si tu trompais le memorymanager linké à panoramic en incrémentant manuellement de 1 le compteur de référence de 'ChaineTransmiseDePanoramic$'? Tu devrait pouvoir te passer de 'MaChaineInterne$' ensuite, non?
Oui et non. J'ai essayé. Effectivement, cela permet de tromper le Memory Manager de Panoramic. Pas celui de la DLL, malheureusement. Normalement, je faisais comme ceci: - Code:
-
Panoramic: dim s$ s$ = "Ma chaîne Panoramic" res% = dll_call1("test",adr(s$))
DLL en Delphi: function test(s: pstring):integer; stdcall; export; begin MyExternalRichEdit.Lines.Add(s^); result := 0; end; exports test;
Cela provoque une violation de mémoire, soit en sortant de la fonction, soit en arrêtant le programme Panoramic, selon les circonstances. Normal: la méthode Add transfère un pointeur vers une variable temporaire (construite par l'opération de déréferencement s^), et en sortant de la fonction, cette variable temporaire n'existe plus. Tant qu'on n'essaie pas d'accéder à cet élément du RichEdit, tout va bien. Mais dès qu'un accès est fait (affichage,, lecture de la ligne, suppression du RichEdit, ...) le pointeur est invalide. Je fais maintenant comme ceci: - Code:
-
Panoramic: dim s$ s$ = "Ma chaîne Panoramic" res% = dll_call1("test",adr(s$))
DLL en Delphi: function test(s: pstring):integer; stdcall; export; var temp: string begin temp := trim(s^+' '); MyExternalRichEdit.Lines.Add(temp); result := 0; end; exports test;
et je n'ai plus de problème. En fait, cela revient à faire ce que tu fais: copier la chaîne d'abord par une méthode quelconque de copie d'octet par octet, via des poiteurs (surtout pas par une affectation de variable !), puis travailler sur la copie. | |
| | | Contenu sponsorisé
| Sujet: Re: @ Klaus : DLL_OFF provoque un plantage ! [Résolu] | |
| |
| | | | @ Klaus : DLL_OFF provoque un plantage ! [Résolu] | |
|
Sujets similaires | |
|
| Permission de ce forum: | Vous ne pouvez pas répondre aux sujets dans ce forum
| |
| |
| |