Edité -> ReceiveNetworkData n'abime pas la pile

Archive.
Avatar de l’utilisateur
ZapMan
Messages : 460
Inscription : ven. 13/févr./2004 23:14
Localisation : France
Contact :

Edité -> ReceiveNetworkData n'abime pas la pile

Message par ZapMan »

EDITION DE CE POST : en fin de compte : la fonction ReceiveNetworkData est innocente. Ce post peut rester intéressant pour ceux qui s'intéressent à l'assembleur et aux manipulations de la pile, mais il n'a plus vraiment sa place dans les rapports de bugs.
----------------------------------------------------------------------------------------------


Ca fait un petit moment que j'avais des problèmes avec les programmes utilisant cette fonction. J'ai fait un test sur les données figurant dans la pile et de toute évidence, ReceiveNetworkData ne fait pas les choses proprement.

L'ennui, c'est que c'est alléatoire, ça n'arrive qu'une fois sur dix environ.

Voilà ce que je constate :

Code : Tout sélectionner

Global Temoin
Procedure Utilise_ReceiveNetworkData()

   ReceiveNetworkData(,,)
   Valeur = 10
   Temoin = Valeur
   ProcedureReturn Valeur

EndProcedure

Test = Utilise_ReceiveNetworkData()
If Temoin<>Test
   ; Debug Ouhlala ! Qu'est-ce qui se passe ???
Endif
Comme ce problème est occasionnel et donc pas évident à reproduire, voici un programme qui utilise intensemment ReceiveNetworkData et qui signale le problème de pile quand il survient :
http://www.rankspirit.com/downloads/dropupload_test.zip

La librairie PB_Zip nécessaire à son fonctionnement est jointe dans l'archive.

Un message "Ouch!" signale que le problème a été détecté.
Chez moi, ça arrive quasiment à chaque fois qu'on upload quelquechose avec ce programme.

J'ai bien sur fait les tests avec la 3.93 et la 3.94 (pas de différence). Je tourne sous XP.

Et inutile de dire que je suis trés trés ennuyé par ce problème :cry:

Les bidouilles qui figurent dans le programme joint essayent de gérer le problème, et permettent de faire quand même marcher ce programme. L'ennui c'est que ce problème de pile fait planter le programme de temps en temps. Ce plantage occasionnel se produit précisemment au moment du "ProcedureReturn" de la procedure "Wait" (celle qui utilise justement ReceiveNetworkData). Il n'y a évidemment aucun message d'erreur à ce moment là, c'est un gros plantage pur et dur et j'ai du pas mal galérer pour déterminer exactement à quel endroit ça plantait (comme en plus, c'est occasionnel !!)

Ce plantage est assez rare quand on lance le programme à partir de l'IDE et beaucoup plus fréquent quand on lance la version compilée du programme, ce qui ne m'a pas facilité les choses pour détecter l'endroit où il avait lieu. Aprés moult tests, il semble qu'il soit bel et bien lié au problème de pile.
Dernière modification par ZapMan le sam. 06/août/2005 15:56, modifié 1 fois.
Tout obstacle est un point d'appui potentiel.

Bibliothèques PureBasic et autres codes à télécharger :https://www.editions-humanis.com/downlo ... ads_FR.htm
Avatar de l’utilisateur
ZapMan
Messages : 460
Inscription : ven. 13/févr./2004 23:14
Localisation : France
Contact :

Message par ZapMan »

Je me mets à utiliser la version 3.94 (que je trouve vraiment formidable, surtout pour les breakpoints, comme quoi, l'attente crée vraiment le plaisir parfois, mais bon, je vais finir par fermer cette parenthèse, elle devient trop longue et contient trop de virgules, vraiment trop)

Au bout d'une demi/heure de travail intensif, PAF ! il me met une ligne en rouge en me signalant une erreur d'accés mémoire. La ligne en question était un "ProcedureReturn" (tiens, tiens !!) et l'erreur était causée par l'utilisation d'une variable globale en tant que paramètre de cette procédure. D'où 2 remarques :

- n'y aurait-il pas un rapport avec le problème du post précédent ?

- Ce que je viens de trouver est un bug de la version 3.94 : la 3.93 me signale proprement la nature du problème (l'utilisation d'une variable globale en tant que paramètre) alors que la 3.94 se contente de me signaler un problème d'accés mémoire qui ne me renseigne pas beaucoup ! J'imagine un novice exposé à ça : 8O 8O
Tout obstacle est un point d'appui potentiel.

Bibliothèques PureBasic et autres codes à télécharger :https://www.editions-humanis.com/downlo ... ads_FR.htm
Avatar de l’utilisateur
ZapMan
Messages : 460
Inscription : ven. 13/févr./2004 23:14
Localisation : France
Contact :

Message par ZapMan »

Pardon de relancer ce topic avec si peu de patience, mais je suis vraiment très ennuyé :(
Tout obstacle est un point d'appui potentiel.

Bibliothèques PureBasic et autres codes à télécharger :https://www.editions-humanis.com/downlo ... ads_FR.htm
comtois
Messages : 5186
Inscription : mer. 21/janv./2004 17:48
Contact :

Message par comtois »

pour les urgences il vaut mieux poster sur le forum anglais :)
Avatar de l’utilisateur
ZapMan
Messages : 460
Inscription : ven. 13/févr./2004 23:14
Localisation : France
Contact :

Message par ZapMan »

C'est bien dommage :cry:
Tout obstacle est un point d'appui potentiel.

Bibliothèques PureBasic et autres codes à télécharger :https://www.editions-humanis.com/downlo ... ads_FR.htm
Fred
Site Admin
Messages : 2808
Inscription : mer. 21/janv./2004 11:03

Message par Fred »

Es-tu sur que ce n'est pas ton buffer qui est trop petit ? La fonction ReadNetworkData() est tres petite et je ne vois rien qui puisse faire cela.
Avatar de l’utilisateur
djes
Messages : 4252
Inscription : ven. 11/févr./2005 17:34
Localisation : Arras, France

Message par djes »

Si ça peut t'aider, voici un code que j'avais mis au point au point il y a deux ou trois ans et qui avait l'air de fonctionner (après 10000 essais/erreurs).

Code : Tout sélectionner

      
	SEvent = NetworkServerEvent()

	If SEvent
		ClientID.l = NetworkClientID()

		Select SEvent

		;New client
		Case 1

		;A client is leaving
		Case 4
			CloseNetworkConnection(ClientID)

		;Raw data has been received
		Case 2
			done.l = #FALSE
			Repeat
				;Clear buffer
				For i=0 To 4096-4 step 4
					PokeL(*buffer+i,0)	
				Next i
				requestlength.l = ReceiveNetworkData(ClientID, *buffer, 4096)
				If requestlength>0
					Gosub traite_requete
				ElseIf requestlength = 0
					done = #TRUE
				Else
					error.l = WSAGetLastError_()
					If error=#WSAEWOULDBLOCK
						Delay(100)
						done = #TRUE
					ElseIf error<>0
						done = #TRUE 
					EndIf
				EndIf
			Until done = #TRUE
		
		EndSelect

	EndIf
Je ne suis pas sûr à 100% de ce code, mais comme tu as l'air plus pragmatique que moi, tu sauras sans doute déterminer s'il est valable ou pas (je m'étais inspiré de plusieurs exemples, mais il faut bien dire que j'ai fini par faire le travail à ma façon car j'avais des problèmes).
Dernière modification par djes le jeu. 04/août/2005 17:26, modifié 1 fois.
Avatar de l’utilisateur
ZapMan
Messages : 460
Inscription : ven. 13/févr./2004 23:14
Localisation : France
Contact :

Message par ZapMan »

Fred a écrit :Es-tu sur que ce n'est pas ton buffer qui est trop petit ? La fonction ReadNetworkData() est tres petite et je ne vois rien qui puisse faire cela.
Voici les procédures concernées :

Code : Tout sélectionner

;
Procedure FTPDebug(Line$,Log_Gadget)
  line$ = FormatDate("%hh:%ii:%ss", Date())+ ": " + line$
  Debug line$
  If IsGadget(Log_Gadget)
    SetGadgetText(Log_Gadget,line$)
  EndIf
  CLog + line$ + #LFCR
EndProcedure


Procedure.s Wait(Connection, Timeout,Log_Gadget) 
  Delay(10)
  BufferLenght = 200
  *Buffer = AllocateMemory(BufferLenght+5000)
  If *Buffer > 0
    Text.s = ""
    Repeat
      t = ElapsedMilliseconds()
      Size = -1
      Repeat
        Result = NetworkClientEvent(Connection)
        If Result <> 2 : Delay(5) : EndIf
        EventID = WindowEvent()
        If EventID=#PB_EventGadget
          If EventGadgetID()=#BInter
            Interrupt = 1
          EndIf
        EndIf
        If GetAsyncKeyState_(#VK_ESCAPE)
          Interrupt = 1
        EndIf
      Until Result = 2 Or ElapsedMilliseconds()-t > Timeout Or Interrupt
      If Result = 2
        Size = ReceiveNetworkData(Connection, *Buffer, BufferLenght)
        If Size > 0
          Text.s + PeekS(*Buffer,Size)
        EndIf
      EndIf
      If size > 150
        Timeout = 4000
      Else
        Timeout = 50
      EndIf
    Until Size < 1 Or Interrupt
  
    If Interrupt
      CloseNetworkConnection(Connection)
      Text = "Interrupted"
    EndIf
    If Text
      While Right(Text,1)=Chr(10) Or Right(Text,1)=Chr(13) Or Right(Text,1)=" "
        Text = Left(Text,Len(Text)-1)
      Wend 
      line$ = ">---<" + RemoveString(Text, #LFCR)
      FTPDebug(Line$,Log_Gadget)
    Else
      Text = "TimeOut"
      line$ = ">---<!!!  Time Out  !!!"
      FTPDebug(Line$,Log_Gadget)
    EndIf
    FreeMemory(*Buffer)
    WaitAnswer$ = Text
    Debug "Jusque là, tout va bien."
    ProcedureReturn Text
  Else
    Text = "Memory Error"
    WaitAnswer$ = Text
    ProcedureReturn Text
  EndIf

EndProcedure
Les variables déclarées comme globales sont : CLog, Interrupt et WaitAnswer$

Le plantage survient au moment du ProcedureReturn. Comme je le dis plus haut, c'est un bug occasionnel qui ne survient pas à chaque fois. L'utilisation intensive de cette procédure par DropUpLoad permet de mettre le problème en évidence assez facilement.

Ayant identifié un problème de pile, j'ai essayé pas mal de valeurs différentes pour BufferLenght. Comme on le voie ici, j'ai même essayé d'allouer au buffer une taille largement supérieur au besoin. Ca ne change rien.
Il semble que le nombre de plantage est plus faible quand BufferLength est inférieur à 1000. Cela m'est confirmé par mes utilisateurs qui ont moins de soucis avec la dernière version (BufferLength = 200) qu'avec la version précédente ou BufferLength valait 5000.

Tu dis que ta procédure est courte, Fred. Si tu avais la gentillesse de me dire quelles sont les API utilisées, j'essayerais bien de refaire une équivalence à ReceiveNetworkData histoire de voir si c'est une API qui fiche le bazard (la dernière fois que j'ai eu un problème de pile, c'était du à une erreur d'une procédure API que j'avais mis en exergue par une de mes propres erreurs). J'ai longuement essayé de trouver qu'elles API tu pouvais utiliser sans aucun succés.

Voici la procédure que j'utilise pour révéler le problème :

Code : Tout sélectionner

Procedure.s Wait2(Connection, Timeout,Log_Gadget)
  In.s = Wait(Connection, Timeout,Log_Gadget)
  If WaitAnswer$<>In
    MessageRequester("Ouch !","In = "+IN+Chr(13)+"WaitAnswer$ = "+WaitAnswer$,0)
  EndIf
  Result$ = WaitAnswer$
  ProcedureReturn Result$
EndProcedure
J'ai remplacé dans mon programme tous les appels à Wait() par Wait2()

Encore un indice : ces procédures sont appellées dans un thread. Dans RankSpirit, j'utilise les même procédures sans thread et il ne semble pas y avoir d'erreur (leur utilisation est beaucoup moins intensive, alors c'est difficile de conclure sur ce point).



Merci djes. Ta procedure est intéressante. Je ne connaissais pas WSAGetLastError_()
Tout obstacle est un point d'appui potentiel.

Bibliothèques PureBasic et autres codes à télécharger :https://www.editions-humanis.com/downlo ... ads_FR.htm
Anonyme2
Messages : 3518
Inscription : jeu. 22/janv./2004 14:31
Localisation : Sourans

Message par Anonyme2 »

Il possible que les registes ebx, ecx, ebp, esi, edi préservés sur la pile et aussi l'adresse de retour de la procedure soient écrassés, ça peut arriver (je me souviens d'un code du soldat inconnu qui plantait au retour car il utilisait une variable structurée dont la taille n'atait pas correcte, les structures MS évoluent en fonction des version des OS)

Si c'est vraiment un écrasement de la pile, tu peux essayer d'ajouter des variables locales mais il faudrait les ajouter après celles que tu utilises dans ta procédure donc en déclarant les tiennes d'abord

par exemple comme ceci

Code : Tout sélectionner

  Text.s
  BufferLenght.l
  t.l
  Size.l
  Result.l
  EventID.l
  Interrupt.l
  *Buffer.l
  Line$ =""
  
  ; maintenant on réserve de la place sur la pile pour éviter d'écraser les données
  essai.LVITEM   ; ca réserve 52 octets sur la pile 
j'ai du mettre la ligne Line$ ="" pour déclarer Line$ car PB donne une erreur de compilation sur ce type alors que ça fonctionne sur une variable comme Text.s

J'ai ajouté 52 octets, c'est déjà bien, fait des tests et tiens-nous au courant.

Si c'est Ok, on a bien un écrasement quelque part avant le code du retour
Avatar de l’utilisateur
ZapMan
Messages : 460
Inscription : ven. 13/févr./2004 23:14
Localisation : France
Contact :

Message par ZapMan »

Excellente idée, Denis. Merci, je vais essayer.
Tout obstacle est un point d'appui potentiel.

Bibliothèques PureBasic et autres codes à télécharger :https://www.editions-humanis.com/downlo ... ads_FR.htm
Anonyme2
Messages : 3518
Inscription : jeu. 22/janv./2004 14:31
Localisation : Sourans

Message par Anonyme2 »

Si ça plante toujours, essaye de mettre plusieurs variables structurées pour réserver plus de place

Avec ta procédure d'origine, sans ajouter autre chose, tu peux créer 2 variables globales et ajouter ces quelques lignes asm pour retrouver l'adresse de retour et vérifier en sortie que c'est la même valeur qu'en entrée de ta procédure, tu peux aussi déplacer ces lignes de codes avant et après l'appel de la fonction ReceiveNetworkData() si tu supposes qu'elle est en cause ou une autre

voici le code avec les quelses lignes asm et l'affichage dans une messagerequester avant de quitter

Je pense que c'est la bonne valeur pour récupérer l'adresse de retour

Code : Tout sélectionner


Global adresse, adresse1
Global adresse2, adresse3

Procedure FTPDebug(Line$,Log_Gadget)
  line$ = FormatDate("%hh:%ii:%ss", Date())+ ": " + line$
  Debug line$
  If IsGadget(Log_Gadget)
    SetGadgetText(Log_Gadget,line$)
  EndIf
  CLog + line$ + #LFCR
EndProcedure


Procedure.s Wait(Connection, Timeout,Log_Gadget)
; *-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-  
; j'ai ajouté ceci pour retrouver l'adresse de retour et la mettre dans la variable globale adresse
  !MOV eax, dword[esi+20]
  !MOV dword[v_adresse], eax
  !MOV dword[v_adresse2], esi
; *-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-  

  Delay(10)
  BufferLenght = 200
  *Buffer = AllocateMemory(BufferLenght+5000)
  If *Buffer <> 0
    Text.s = ""
    Repeat
      t = ElapsedMilliseconds()
      Size = -1
      Repeat
        Result = NetworkClientEvent(Connection)
        If Result <> 2 : Delay(5) : EndIf
        EventID = WindowEvent()
        If EventID=#PB_EventGadget
          If EventGadgetID()=#BInter
            Interrupt = 1
          EndIf
        EndIf
        If GetAsyncKeyState_(#VK_ESCAPE)
          Interrupt = 1
        EndIf
      Until Result = 2 Or ElapsedMilliseconds()-t > Timeout Or Interrupt
      If Result = 2
        Size = ReceiveNetworkData(Connection, *Buffer, BufferLenght)
        If Size > 0
          Text.s + PeekS(*Buffer,Size)
        EndIf
      EndIf
      If size > 150
        Timeout = 4000
      Else
        Timeout = 50
      EndIf
    Until Size < 1 Or Interrupt
 
    If Interrupt
      CloseNetworkConnection(Connection)
      Text = "Interrupted"
    EndIf
    If Text
      While Right(Text,1)=Chr(10) Or Right(Text,1)=Chr(13) Or Right(Text,1)=" "
        Text = Left(Text,Len(Text)-1)
      Wend
      line$ = ">---<" + RemoveString(Text, #LFCR)
      FTPDebug(Line$,Log_Gadget)
    Else
      Text = "TimeOut"
      line$ = ">---<!!!  Time Out  !!!"
      FTPDebug(Line$,Log_Gadget)
    EndIf
    FreeMemory(*Buffer)
    WaitAnswer$ = Text
    Debug "Jusque là, tout va bien."

; *-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-  
; j'ai ajouté ceci pour retrouver l'adresse de retour et la mettre dans la variable globale adresse1 puis j'affiche, les valeurs doivent être identiques
; si les valeurs sont différentes, soit esi a été changé soit la valeur a été écrasée
  !MOV eax, dword[esi+20]
  !MOV dword[v_adresse1], eax
  !MOV dword[v_adresse3], esi
     MessageRequester("", "Adresse Retour début : "+ Str(adresse)+Chr(10)+"Adresse Retour fin :      "+Str(adresse1)+Chr(10)+"esi avant : "+Str(adresse2)+Chr(10)+"esi après : "+Str(adresse3), 16) 
; *-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-  
    ProcedureReturn Text
  Else
    Text = "Memory Error"
    WaitAnswer$ = Text

; *-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-  
; j'ai ajouté ceci pour retrouver l'adresse de retour et la mettre dans la variable globale adresse1 puis j'affiche, les valeurs doivent être identiques
; si les valeurs sont différentes, soit esi a été changé soit la valeur a été écrasée
  !MOV eax, dword[esi+20]
  !MOV dword[v_adresse1], eax
  !MOV dword[v_adresse3], esi
     MessageRequester("", "Adresse Retour début : "+ Str(adresse)+Chr(10)+"Adresse Retour fin :      "+Str(adresse1)+Chr(10)+"esi avant : "+Str(adresse2)+Chr(10)+"esi après : "+Str(adresse3), 16) 
; *-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-  

   ProcedureReturn Text
  EndIf
EndProcedure
Avatar de l’utilisateur
ZapMan
Messages : 460
Inscription : ven. 13/févr./2004 23:14
Localisation : France
Contact :

Message par ZapMan »

Tu es trop fort, toi :D

Tu m'apporte le truc sur un plateau !
C'est vraiment sympa, merci, je vais tester.
Tout obstacle est un point d'appui potentiel.

Bibliothèques PureBasic et autres codes à télécharger :https://www.editions-humanis.com/downlo ... ads_FR.htm
Anonyme2
Messages : 3518
Inscription : jeu. 22/janv./2004 14:31
Localisation : Sourans

Message par Anonyme2 »

ZapMan a écrit :Tu es trop fort, toi :D

Tu m'apporte le truc sur un plateau !
C'est vraiment sympa, merci, je vais tester.
Ma bonté me perdra :roll:

:mrgreen: :mrgreen:
Avatar de l’utilisateur
ZapMan
Messages : 460
Inscription : ven. 13/févr./2004 23:14
Localisation : France
Contact :

Message par ZapMan »

Bon, j'ai fait le test et il semble que les adresses que ton code contrôle ne soit pas bricolées, ni à l'intérieur de wait() ni à l'intérieur de wait2(). Pour faire des tests intensifs, j'ai modifié ta formule magique comme suit :

Code : Tout sélectionner

 ; *-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-  
  !MOV eax, dword[esi+20] 
  !MOV dword[v_adresse1], eax 
  !MOV dword[v_adresse3], esi
; -------- ICI ---------
  If adresse<>adresse1 Or adresse2<>adresse3
     MessageRequester("", "Adresse Retour début : "+ Str(adresse)+Chr(10)+"Adresse Retour fin :      "+Str(adresse1)+Chr(10)+"esi avant : "+Str(adresse2)+Chr(10)+"esi après : "+Str(adresse3), 16) 
  EndIf
; *-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-
et le message ne se déclenche jamais.
Pourtant, j'obtiens toujours des valeurs régulièrement différentes entre WaitAnswer$ et IN dans ma procédure Wait2() (voir posts précédents) et j'obtiens toujours un magnifique plantage de temps à autre (même pas précédé de l'alarme ci-dessus).
J'y perds mon latin. :evil:
- Comment expliquer autrement que par un problème de pile le "Ouch !!" de ma procédure Wait2() ?? Note bien que si je fait carrément

Code : Tout sélectionner

ProcedureReturn WaitAnswer$
et que je compare ensuite WaitAnswer$ (c'est une variable globale) avec le résultat retourné, je continue à avoir des différences. C'est quand même fort, Non ?
- L'adresse de retour et l'adresse de la variable retournée sont-elles stockées à deux endroits complètement différents de la pile (ce qui pourrait expliquer que l'une soit abimée et pas l'autre) ?
- Comment comparer l'adresse de la variable de retour de ProcedureReturn (dans Wait()) et juste aprés (dans Wait2()) ?

Je suis piteusement incompétent en matière d'assembleur pentium. :oops: Je n'ai pas fait d'assembleur depuis plus de 20 ans et j'avoue être gravement largué.
Alors, je sais, ça fait beaucoup de questions d'un coup, mais une seule réponse suffira, si elle est bonne !!
Tout obstacle est un point d'appui potentiel.

Bibliothèques PureBasic et autres codes à télécharger :https://www.editions-humanis.com/downlo ... ads_FR.htm
Anonyme2
Messages : 3518
Inscription : jeu. 22/janv./2004 14:31
Localisation : Sourans

Message par Anonyme2 »

ZapMan a écrit :Bon, j'ai fait le test et il semble que les adresses que ton code contrôle ne soit pas bricolées, ni à l'intérieur de wait() ni à l'intérieur de wait2(). Pour faire des tests intensifs, j'ai modifié ta formule magique comme suit :

Code : Tout sélectionner

 ; *-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-  
  !MOV eax, dword[esi+20] 
  !MOV dword[v_adresse1], eax 
  !MOV dword[v_adresse3], esi
; -------- ICI ---------
  If adresse<>adresse1 Or adresse2<>adresse3
     MessageRequester("", "Adresse Retour début : "+ Str(adresse)+Chr(10)+"Adresse Retour fin :      "+Str(adresse1)+Chr(10)+"esi avant : "+Str(adresse2)+Chr(10)+"esi après : "+Str(adresse3), 16) 
  EndIf
; *-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-
et le message ne se déclenche jamais.
Pourtant, j'obtiens toujours des valeurs régulièrement différentes entre WaitAnswer$ et IN dans ma procédure Wait2() (voir posts précédents) et j'obtiens toujours un magnifique plantage de temps à autre (même pas précédé de l'alarme ci-dessus).
J'y perds mon latin. :evil:
Je ne vois pas ce que c'est IN et WAIT2, IN est un paramètre de la procedure WAIT2 ? Tu passe en fait WaitAnswer$ dans la procedure WAIT2 ?
ZapMan a écrit : - Comment expliquer autrement que par un problème de pile le "Ouch !!" de ma procédure Wait2() ?? Note bien que si je fait carrément

Code : Tout sélectionner

ProcedureReturn WaitAnswer$
et que je compare ensuite WaitAnswer$ (c'est une variable globale) avec le résultat retourné, je continue à avoir des différences. C'est quand même fort, Non ?
Tu utilises plusieurs Thread qui sont susceptibles d'accéder en écriture à WaitAnswer$?
Si oui il y a peut-être un problème (voir la doc PB sur les Thread) sur l'utilisation du même buffer avec plusieurs Thread
ZapMan a écrit : - L'adresse de retour et l'adresse de la variable retournée sont-elles stockées à deux endroits complètement différents de la pile (ce qui pourrait expliquer que l'une soit abimée et pas l'autre) ?
- Comment comparer l'adresse de la variable de retour de ProcedureReturn (dans Wait()) et juste aprés (dans Wait2()) ?
L'adresse de retour est stockée sur la pile et PB utilise une buffer pour les variables de type chaîne dont il passe l"adresse sur la pile avant d'empiler les parametres.

Je pense que l'adresse de retour peut être modifiée sans que l'adresse du buffer le soit. Sans tracer le code c'est difficile de dire ce qui cloche

Voilà le fonctionnement de PB pour les Procedure PB (ce n'est pas toujours vrai pour les procedures utilisateur qui peuvent passer les paramètres par eax, mais je ne sais pas si PB utilise le passage par eax pour certaines commandes, mais ça ne change rien pour l'adresse de retour)


Un exemple avec une Procedure à 3 paramètres qui retourne une string

Chaque paramètre quel que soit son type (long, byte, word, etc) sera empilé sous la forme d'un mot de 32 bit soit un long
Pour la valeur retournée dans le cas d'une string, eax n'est pas utilisé et PB travaille directement avec son buffer.

Code : Tout sélectionner

Procedure.s essai(Param1, Param2, Param3)
...
EndProcedure
Les paramètres vont être empilés dans l'ordre inverse en commençant par Param3 pour finir par param1 puis l'adresse de retour de la procédure va se retrouver sur la pile sous la forme d'un mot de 32 bit (appel court)

Ensuite on arrive dans la Procedure PB, et PB va sauvegarder les registres suivants sur la pile et dans l'odre

ebx, ecx, ebp, esi et edi

maintenant, le pointeur de pile esp pointe sur le dernier élément de la pile , donc sur la valeur sauvegardée de edi

PB attribue au registre esi, la valeur de esp car PB utilise esp pour accéder aux variables locales

Esi lui ne change pas et ne doit pas changer.

Voici comment est la pile jusqu'à ce moment là

Code : Tout sélectionner

PB_Strigbase     ; c'est le pointeur du buffer de chaine passé sur la pile
Param3
Param2
Param1
Adresse de retour                          : esi +20
ebx     ; valeur du registre sauvegardée   : esi +16
ecx     ; valeur du registre sauvegardée   : esi +12
ebp     ; valeur du registre sauvegardée   : esi +8
esi     ; valeur du registre sauvegardée   : esi +4   
edi     ; valeur du registre sauvegardée   : esi +0
le registre esi pointe sur le dernier élément de la pile soit sur la valeur sauvegardée de edi

Pour remonter sur la pile on ajoute 4 pour chaque paramètre et on a esi+20 pour l'adresse de retour, mais n'ajoute pas 20 directement à esi car esi ne doit pas changer

d'ou on accède à cette valeur comme ça et on utilise soit eax soit edx car PB ne stocke rien temporairement dans ces 2 registres

Code : Tout sélectionner

!MOV eax, dword[esi+20]
et il faut sauvegarder cette valeur dans une variable globale
On ajoute v_devant le nom de variable pour y accéder en asm (pour PB) et si c'est un pointeur on y met p_

pour la variable globale adresse, on y accède avec v_adresse

pour l'accès en asm, il faut strictement conserver la casse

on stocke comme ceci

Code : Tout sélectionner

  !MOV dword[v_adresse], eax
J'espère que ça t'aidera
Répondre