Problema con el retorno de una función

Tus preguntas. Algoritmos o Grupos de Comandos formando Programas Escripts.
Responder
Jonny
Profesional del Autoit
Mensajes: 1042
Registrado: 30 Jun 2008, 20:08

Problema con el retorno de una función

Mensaje por Jonny »

Hola

He reescrito la función _AdslGetIpLocation() que hice (con ayuda de Chefito) en la librería Adsl.au3 hace algún tiempo, porque Creo que podía mejorarse su rendimiento y esque, tal y como estaba cuando la escribí por primera vez al ejecutarla la aplicación que la contubiera, aún simulando el multihilo (que tanto me gusta) se bloqueaba mientras la función recogía la información que le enviaba el servidor donde se realiza la consulta, (que por cierto, tarda unos segundos) es decir, toda la página que devuelve el servidor.
Eso, en mi opinión no queda bien del todo, pues no es posible ni siquiera cerrar el programa si quisiéramos.

por eso, la he hecho de forma, que también simule el multihilo, en la parte que debe recoger la página resultante de la consulta con un timer. De esta forma si mientras se realiza la consulta queremos interactuar con la aplicación podemos hacerlo.

Además, he incluido en la función un parámetro que permite indicar si se quiere, el tiempo máximo que se esperará para que el servidor entregue la respuesta. Así podemos evitar errores o lags innecesários.

Está todo listo, pero a la hora de probar la función encontré un pequeño problema que no se como arreglar.
Aquí va el código por si quereis hechar un vistazo (será más fácil ver como arreglarlo):

;AdslGetIpLocation():

Código: Seleccionar todo


#include-once

#include <Constants.au3>

#Include <Array.au3>

#Include <File.au3>

#Include <String.au3>

#Include <Ie.au3>

#Include <Inet.au3>

#Include <Timers.au3>

Global $_DataLocation="", $BuffXHandleWindow

Global $HostLocation="www.ipaddresslocation.org", $ActualMSecond=0

Global $MethodLocation="post", $PathLocation="/ip-address-locator.php"

Global $DataLocation, $UserAgentLocation=True

Global $SockLocation, $PortLocation=80

Global $Timer_DataReceibeLocation, $BuffXAutoPause=0

Func _AdslGetIpLocation($XIp, $XHandleWindow, $XAutoPause=0, $_Ex=0)

Local $LegalCharsAutoPause="0123456789", $Location[4]

Switch($_Ex)

Case 0

$BuffXHandleWindow=$XHandleWindow

$DataLocation="ip="&$XIp

$AutoPauseDiv=StringSplit($XAutoPause, "")

For $I=1 To $AutoPauseDiv[0] Step +1

If StringInStr($LegalCharsAutoPause, $AutoPauseDiv[$I])==0 Then

SetError(1)

Return "Valor de pausa incorrecto"

ExitLoop

EndIf

Next

$XAutoPause=Number($XAutoPause)

If $XAutoPause>0 Then

$BuffXAutoPause=$XAutoPause

$ActualMSecond=0

EndIf

If $XHandleWindow=="" Then

SetError(1)

Return "Debe especificarse un identificador de ventana válido"

EndIf

TCPStartUp()

If $XIp=="" Then

setError(1)

Return "Deve especificarse una dirección ip válida"

EndIf

$IpSplit=StringSplit($XIp, ".")

If $IpSplit[0]<>4 Then

SetError(1)

Return "Debe especificarse una dirección ip válida"

EndIf

   For $n=1 To 4   

      If IsNumber($IpSplit[$n]) Then 

         SetError(1)

         Return $XIp&" no es una dirección ip válida"

      EndIf

      If (Number($IpSplit[$n])<0 Or Number($IpSplit[$n])>255) Then 

         SetError(1)

         Return $XIp&" no es válido"

      EndIf

   Next

If $MethodLocation=="" Then

$MethodLocation="GET"

EndIf

$MethodLocation=StringUpper($MethodLocation)

$SVPing=Ping($HostLocation, 2000)

If @Error<>0 Then

SetError(1)

Return "La base de datos no está disponible en este momento"

EndIf

$SockLocation=TCPConnect(TcpNameToIp($HostLocation), $PortLocation)

If $SockLocation==-1 Then

SetError(1)

Return "Error al conectar a la base de datos"

EndIf

If $MethodLocation=="GET" Then

$PathLocation&="?"&$DataLocation

EndIf

TcpSend($SockLocation, $MethodLocation&" "&$PathLocation&" HTTP/1.1"&@CRLF)

TcpSend($SockLocation, "Host: "&$HostLocation&@CRLF)

TcpSend($SockLocation, "Content-Type: application/x-www-form-urlencoded"&@CRLF)

If $MethodLocation=="POST" Then

TcpSend($SockLocation, "Content-length: "&StringLen($DataLocation)&@CRLF)

EndIf

If $UserAgentLocation==True Then

TcpSend($SockLocation, "User-Agent: MSIE"&@CRLF)

EndIf

TcpSend($SockLocation, "Connection: close"&@CRLF&@CRLF)

If $MethodLocation=="POST" Then

TcpSend($SockLocation, $DataLocation)

EndIf

$Timer_DataReceibeLocation=_Timer_SetTimer($XHandleWindow, 0, "_DataReceibeLocationTimer")

Case 1

$posIniCountry=StringInStr($_DataLocation,"IP Country:")+15

$posFinCountry=StringInStr($_DataLocation,"</b>",0,1,$posIniCountry)

$Country=StringMid($_DataLocation,$posIniCountry,$posFinCountry-$posIniCountry)

$posIniContinent=StringInStr($_DataLocation,"IP Continent:",0,1,$posFinCountry)+17

$posFinContinent=StringInStr($_DataLocation,"</b>",0,1,$posIniContinent)

$Continent=StringMid($_DataLocation,$posIniContinent,$posFinContinent-$posIniContinent)

$posIniRegion=StringInStr($_DataLocation,"IP Region:",0,1,$posFinContinent)+14

$posFinRegion=StringInStr($_DataLocation,"</b>",0,1,$posIniRegion)

$Region=StringMid($_DataLocation,$posIniRegion,$posFinRegion-$posIniRegion)

$posIniCity=StringInStr($_DataLocation,"Guessed City:",0,1,$posFinRegion)+17

$posFinCity=StringInStr($_DataLocation,"</b>",0,1,$posIniCity)

$City=StringMid($_DataLocation,$posIniCity,$posFinCity-$posIniCity)

If $Country="" or $Continent="" or $Region="" or $City="" Then

SetError(1)

Return "Error al localizar "&$XIp

EndIf

$Location[0]=$Continent

$Location[1]=$Country

$Location[2]=$Region

$Location[3]=$City

$_DataLocation=""

SetError(0)

Return $Location

Case 2

$_DataLocation=""

SetError(1)

Return "Tiempo de espera para el servidor agotado"

EndSwitch

EndFunc

Func _DataReceibeLocationTimer($hWnd, $Msg, $iIDTimer, $dwTime)

$_DataLocation&=TCPRecv($SockLocation, 128)

If @Error Then

_Timer_KillTimer($BuffXHandleWindow, $Timer_DataReceibeLocation)

TcpCloseSocket($SockLocation)

_AdslGetIpLocation("", "", "", 1)

Return

EndIf

If $BuffXAutoPause>0 Then

If $ActualMSecond>=$BuffXAutoPause Then

_Timer_KillTimer($BuffXHandleWindow, $Timer_DataReceibeLocation)

TcpCloseSocket($SockLocation)

_AdslGetIpLocation("", "", "", 2)

Return

Else

$ActualMSecond+=1

EndIf

EndIf

EndFunc

 
;Código de ejemplo para ejecutar la función:

Código: Seleccionar todo


#Include <Constants.au3>

#Include <EditConstants.au3>

#Include <GUIConstants.au3>

#Include <GUIConstantsEx.au3>

#Include <GuiComboBox.au3>

#Include <ButtonConstants.au3>

#Include <Sound.au3>

#Include <StaticConstants.au3>

#Include <TreeViewConstants.au3>

#Include <WindowsConstants.au3>

#Include <Array.au3>

#Include <File.au3>

#Include <String.au3>

#Include <Misc.au3>

#Include <Timers.au3>

#Include <Ie.au3>

#Include <Inet.au3>

#Include <Word.au3>

#Include <WinAPI.au3>

#Include <F:\herramientas\lib\Adsl.au3>

#Include <F:\herramientas\lib\Sys_Info.au3>

#Include <F:\herramientas\lib\Zip.au3>

Opt("MustDeclareVars", 0)

Opt("TrayAutoPause", 1)

Opt("TrayOnEventMode", 1)

Opt("TrayIconHide", 1)

Opt("TrayMenuMode", 1)

Opt("GUIOnEventMode", 1)

Opt("OnExitFunc", "ExitFile")

Opt("GUIResizeMode", $GUI_DOCKWIDTH+$GUI_DOCKHEIGHT+$GUI_DOCKTOP+$GUI_DOCKLEFT)

Global $CtrlWindowThread, $WindowHandler, $Location

$CtrlWindowThread=_Timer_SetTimer($WindowHandler, 0, "CtrlWindowThread")

GUIDelete()

$WindowHandler=GUICreate("Prueba", 900, 700, -1, -1, BitOR($GUI_SS_DEFAULT_GUI, $WS_MAXIMIZEBOX))

$Location=_AdslGetIpLocation("87.216.218.141", $WindowHandler, 12)



GuiSetState()

While 1

If (Not IsArray($Location) And $Location<>"") Then

Msgbox(0, "", $Location)

ExitLoop

ElseIf IsArray($Location) Then

For $I=1 To 4 Step +1

Msgbox(0, "", $Location[$I])

Next

ExitLoop

EndIf



_ReduceMemory()

Wend

Func CtrlWindowThread($hWnd, $Msg, $iIDTimer, $dwTime)

GuiSetOnEvent($GUI_EVENT_CLOSE, "ExitFile")

GuiSetOnEvent($GUI_EVENT_MAXIMIZE, "GuiMaximize")

GuiSetOnEvent($GUI_EVENT_MINIMIZE, "GuiMinimize")

GuiSetOnEvent($GUI_EVENT_RESTORE, "GuiRestore")

EndFunc

Func _ReduceMemory()

DllCall("psapi.dll", "int", "EmptyWorkingSet", "long", -1)

EndFunc

Func ExitFile()

Exit

EndFunc

Func GuiMaximize()

$WinMaximize=@SW_MAXIMIZE

EndFunc

Func GuiMinimize()

$WinMinimize=@SW_MINIMIZE

EndFunc

Func GuiRestore()

$WinRestore=@SW_RESTORE

EndFunc

 
El problema que surge con el código de _AdslGetIpLocation() es que En el momento en que se empieza ejecutar el timer que recoge la respuesta del servidor Sigue ejecutándose la función y al no cumplirse ninguno de los case siguientes, llega al final y devuelve 0, mientras que no debería ser así, sinó que una vez ejecutada la línea:

Código: Seleccionar todo


$Timer_DataReceibeLocation=_Timer_SetTimer($XHandleWindow, 0, "_DataReceibeLocationTimer")

 
La función debería terminar o pausarse, pero sin retornar nada, pues solo debe retornarse algo en el case 1 o 2.

¿Como puedo arreglar esto?...
No encontré la manera.
En definitiva, lo que quiero es, que la función únicamente retorne algo cuando encuentre la instrucción return.

He modificado el switch por sentencias if, pero el resultado como era de esperar, es el mismo...
Gracias de antemano,

Salu2!
Avatar de Usuario
Chefito
Profesional del Autoit
Mensajes: 2035
Registrado: 21 Feb 2008, 18:42
Ubicación: Albacete/Cuenca (España)

Re: Problema con el retorno de una función

Mensaje por Chefito »

Pufff....como siempre para hacer que funcione este código habría que hacer filigranas :smt024 , así que esta vez paso :smt009 . Con lo facil que sería poner un código completo con su ejemplo ya incluido y todo........ .

He visto por encima algo que no me ha gustado mucho: _Timer_SetTimer($XHandleWindow, 0, "_DataReceibeLocationTimer")
Te das cuenta que le has dado al tiempo de repetición de la función 0 milisegundos???. :smt017
No creo que se comporte correctamente la funcion timer pero bueno.
Ten mucho cuidado con los timer, no sea que se monten (se vayan ejecutando muchisimos sin parar) y vuelvas loco al script.
Tienes que definir muy bien las condiciones de entrada en la función que llama el timer, para que no se vuelva a ejecutar antes de que pueda acabar de ejecutar el anterior. Cosas así.

Saludos.
Cita vista en algún lugar de la red: En este mundo hay 10 tipos de personas, los que saben binario y los que no ;).
Jonny
Profesional del Autoit
Mensajes: 1042
Registrado: 30 Jun 2008, 20:08

Re: Problema con el retorno de una función

Mensaje por Jonny »

Hola

Vaya, hoy que Hice bien los deberes y puse la función _AdslGetIpLocation() con todos sus includes, variables globales etc (Antes del código puse lo que era: ";AdslGetIpLocation():").

También puse un código de ejemplo que ejecutaba la función, con includes, opt()'s, Variables globales, Creación de una ventana, llamada a _AdslGetIpLocation(), bucle while para que se mantenga el programa, y un timer que comprueba si se sale del programa, maximiza la ventana, minimiza o restaura con sus correspondientes eventos escritos... (Antes del código puse: ";Código de ejemplo para ejecutar la función:").
Yo he copiado los dos códigos que puse y no he tenido que tocar nada... funciona bien, excepto el código de ejemplo, que no muestra el resultado de _AdslGetIpLocation() que es por lo que abrí el post, claro.

sí, se que le puse 0 milisegundos al timer, porque quizá si le pongo tiempo no recoja bien la respuesta del servidor donde la función hace la consulta. el timer funciona bien. de hecho, si se cambian los returns de la función por Msgbox(0, "", $Location) por ejemplo funciona todo bien.
Si ocurrió un error la función msgbox() lo muestra y si nó con algo como:
Msgbox(0, "", $Location[0]) etc se obtienen los resultados.
El problema está en que la función hace un return antes de lo que debería.
Gracias,


Salu2!
Avatar de Usuario
Chefito
Profesional del Autoit
Mensajes: 2035
Registrado: 21 Feb 2008, 18:42
Ubicación: Albacete/Cuenca (España)

Re: Problema con el retorno de una función

Mensaje por Chefito »

Jejejeje....bueno, tienes razón. Cambiando tres includes y nombrando el script de la función con el nombre adsl funciona sin muchas movidas. Me equivoqué.
Es que yo intenté juntar todo en un script y algo pasó, porque en un principio no me funcionó.

Bueno, he visto que no devuelve nada. Y tu te preguntas que por que no te devuelve el array con el resultado??? Muy sencillo, porque cuando llama el usuario la función se corta, ya que solo entra en el case 0. Y entonces acaba....sin hacer nada. Lo único que puede devolver son los errores en la ip.
Y tu me dirás que si la completas. Pues no, el que la completa es el timer, ya que la llama dos veces mas (estas 2 llamadas a la función son totalmente distintas de la primera.....como ya te he dicho la primera terminó).
Con esto te digo que si has definido las variables de la función con ámbito global (creo que sí), supongo que el resultado te lo devuelva la primera llamada a la función desde el timer ( _AdslGetIpLocation("", "", "", 1) ), ya que dentro del case 1 está el código que devuelve el resultado.

Y recuerda, cada vez que llames a una función, esta llamada será única con un resultado único. No puedes llamarlas y pensar que se van a acumular las llamadas hasta recuperar la última. Cada llamada a una función da resultados distintos.

Fuera de esto, yo creo que la función que se hizo en su día está bien, ya que apenas tarda nada en dar la información.

Saludos.
Cita vista en algún lugar de la red: En este mundo hay 10 tipos de personas, los que saben binario y los que no ;).
Jonny
Profesional del Autoit
Mensajes: 1042
Registrado: 30 Jun 2008, 20:08

Re: Problema con el retorno de una función

Mensaje por Jonny »

Hola

Perdona, es cierto que se me escapó algún include, no me acordé de cambiar los includes de "Adsl.au3" ni decir que debían ser dos aplicaciones diferentes jeje.

sí, todo lo que dices, lo ví tras mirar el código y pensar en como funcionaba...

Es lo que tiene picar código a veces a la lijera...
Como me dí cuenta que la función estaba bien, que no estaba correcto el planteamiento que yo hice, me bastó retocar alguna cosita para que entonces sí, funcionara como yo quería.
en éste post:
http://www.emesn.com/autoitforum/viewto ... 5209#p5209
está la librería con la función actualizada.
Como lo de los returns no tiene solución tal y como escribí el código que por otro lado no encontré otra forma de hacerlo, el resultado se ajusta en una variable global de la librería, que es la que hay que chequear para ver el resultado de la función...

Quizá suene raro (puede serlo) pero para mí es la única forma jaja.

De todas maneras, en la función está todo bien explicado en su documentación.

A mí sí me tardaba unos segundos (y me tarda) en devolverme los resultados (siempre que no ocurra ningún error) por eso el modificarla... porque relentizaba cualquier aplicación que la ejecutara.

Ahora, la verdad es que me gusta más todabía!

Se admiten críticas xd.
Gracias,

Salu2!
Responder