Plantilla para Salvapantallas
Publicado: 07 Abr 2010, 13:01
He creado una especie de plantilla para hacer salvapantallas, derivado de mi experiencia en la creación de ¡ORB!:
http://www.emesn.com/autoitforum/viewto ... f=4&t=1987
Ofrece la estructura necesaria para hacer un salvapantallas completo, con modos de configuración, previsualización, ejecución normal a toda pantalla...
La plantilla incluye comentarios explicando claramente dónde hay que añadir el código personalizado.
Como ejemplo de cómo rellenar la plantilla os remito al mencionado ¡ORB!, pues también publiqué el código completo.
Edit: Otro ejemplo ULTRASIMPLE en este comentario de más abajo:
http://www.emesn.com/autoitforum/viewto ... 6bfb#p7897
Creo que puede ser muy útil, ya que me he pegado bastante para llegar a esto, os adelantará bastante faena.
Si la usáis agradecería que me nombrarais en los "Créditos".
EDITADO 08/04/2010:
.- Cambio $titulo por $ID para no confundir, pues en realidad no es el título de la ventana de nuestro GUI, sino más bien un identificador de nuestra aplicación de salvapantallas.
.- Pongo un GUISetState(@SW_SHOW) después de la creación del GUI pues lo normal es que siempre está ahí.
.- Retraso al mostrar el previo, para dar tiempo a que se cierre el previo del salvapantallas anterior y asegurarnos de que somos el único proceso hijo antes de entrar en el bucle.
http://www.emesn.com/autoitforum/viewto ... f=4&t=1987
Ofrece la estructura necesaria para hacer un salvapantallas completo, con modos de configuración, previsualización, ejecución normal a toda pantalla...
La plantilla incluye comentarios explicando claramente dónde hay que añadir el código personalizado.
Como ejemplo de cómo rellenar la plantilla os remito al mencionado ¡ORB!, pues también publiqué el código completo.
Edit: Otro ejemplo ULTRASIMPLE en este comentario de más abajo:
http://www.emesn.com/autoitforum/viewto ... 6bfb#p7897
Creo que puede ser muy útil, ya que me he pegado bastante para llegar a esto, os adelantará bastante faena.
Si la usáis agradecería que me nombrarais en los "Créditos".
EDITADO 08/04/2010:
.- Cambio $titulo por $ID para no confundir, pues en realidad no es el título de la ventana de nuestro GUI, sino más bien un identificador de nuestra aplicación de salvapantallas.
.- Pongo un GUISetState(@SW_SHOW) después de la creación del GUI pues lo normal es que siempre está ahí.
.- Retraso al mostrar el previo, para dar tiempo a que se cierre el previo del salvapantallas anterior y asegurarnos de que somos el único proceso hijo antes de entrar en el bucle.
Código: Seleccionar todo
#cs
;
; Título .........: SalvapantallasPlantilla.au3
; Descripción ....: Estructura necesaria para crear un salvapantallas
; Autor ..........: Ximo Izquierdo (Ximorro)
; Versión ........: 1.0.0.3
; Notas ..........: Un salvapantallas es como un ejecutable normal, pero se cambia la extensión
; EXE por SCR. Aunque no es obligatorio es recomendable guardarlo en la carpeta
; System32 de Windows, para seleccionarlo cómodamente desde el diálogo de Windows.
;
; Las secciones marcadas con comentarios tipo ";**********" son las que hay que
; rellenar con código propio, o se da el esquema del código listo para completar
; con muy pocos añadidos (como en creación de GUI y bucle principal).
;
#ce
;
; Título .........: MiSalvapantallas.au3
; Descripción ....: Qué hace el salvapantallas
; Autor ..........: Tu nombre
; Versión ........: x.x.x.x
; Créditos .......: Creado con la plantilla de Ximorro (Ximo Izquierdo) en
; http://www.emesn.com/autoitforum/viewtopic.php?f=4&t=2074&start=0&sid=945967164a7b1524acedc4a804481175
;
#include <WindowsConstants.au3> ;$WS_POPUP, $WS_EX_TOPMOST, $WS_EX_TOOLWINDOW
#NoTrayIcon
; Si ya está en ejecución cerramos la anterior y seguimos con esta como la buena.
; Lo normal es que se esté cambiando de modo, p.e. de previo a configuración.
Global $ID = "Mi identificador" ;************** Ponemos un nombre identificativo a nuestro salvapantallas
If WinExists($ID) Then WinKill($ID)
AutoItWinSetTitle($ID)
; Parámetros para un SCR:
; /s -> modo normal de ejecución
; /p hWnd -> modo de previsualización, dentro del diálogo de Windows para seleccionar salvapantallas,
; hWnd es un handle a la ventana de la que tenemos que hacernos ventana hija
; /c -> modo de configuración, para mostrar un diálogo con las opciones del salvapantallas
Global $modo
If $CmdLine[0] > 0 Then
$modo = StringLeft($CmdLine[1], 2)
Else
$modo = "/s" ; Pongo modo normal si no hay parámetros, por ejemplo ejecutado a mano desde el explorador
EndIf
;*********************************************************************************************************
; Aquí se leen las variables de configuración, valores por defecto y/o leídos de un INI, registro, etc...
;*********************************************************************************************************
If $modo = "/c" Then
;*********************************************************************************************************
; Aquí se crea el GUI de configuración, si no hay valores configurables al menos es buena idea poner un
; MsgBox con un "Acerca de..." para que lo muestre Windows al darle al botón "Configurar".
; Este GUI guardará la configuración en un archivo INI, en el registro o como idee el programador, un
; salvapantallas no tiene un sistema especial para guardar información, es como un ejecutable normal.
;
; Cuando se cierre este GUI (o MsgBox) hay que salir con Exit para que Windows vuelva a la ventana de
; selector de salvapantallas.
;*********************************************************************************************************
ElseIf $modo = "/s" OR $modo = "/p" Then
;*********************************************************************************************************
; Aquí creamos el GUI del salvapantallas, es prácticamente igual para los modos normal (/s) y previo (/p).
; La diferencia es que en normal es habitual ejecutarlo a pantalla completa, ocultar ratón, etc... pero
; en modo previo se ejecuta necesariamente a una resolución de 152x112 y debemos hacer nuestra ventana
; hija del panel donde la inserta Windows (el handler de dicho panel se nos pasa como segundo parámetro).
; A continuación pongo código de ejemplo de cómo se hace esto.
;*********************************************************************************************************
; Ejemplo de creación de GUI
Global $GUIscr, $WIDTH, $HEIGHT
If $modo = "/p" Then ; Modo previo: pantalla tamaño fijo, ventana hija
$WIDTH = 152
$HEIGHT = 112
$GUIscr = GUICreate("", $WIDTH, $HEIGHT, 0, 0, $WS_POPUP)
DllCall("user32.dll", "int", "SetParent", "hwnd", $GUIscr, "hwnd", HWnd($CmdLine[2]))
Else
; Modo normal: a toda pantalla, sin barra de título ni bordes
$WIDTH = @DesktopWidth
$HEIGHT = @DesktopHeight
$GUIscr = GUICreate("", $WIDTH, $HEIGHT, 0, 0, $WS_POPUP, BitOR($WS_EX_TOPMOST, $WS_EX_TOOLWINDOW))
GUISetCursor(16, 1) ; Ocultar ratón
EndIf
;**********************************************************************************************************
; Aquí crearemos los controles del GUI, si lo preparamos todo para que funcione a diferentes resoluciones,
; algo prácticamente obligado en un salvapantallas, el código será exactamente el mismo para modo normal
; y previo.
;**********************************************************************************************************
GUISetState(@SW_SHOW)
;**********************************************************************************************************
; A continuación entramos en el bucle principal del salvapantallas. Normalmente será exactamente igual
; para los dos modos, así que esos bloques que marco como "Dibujamos en nuestro GUI" lo normal es que sean
; iguales, así que es conveniente meter ese proceso en una función y aquí simplemente hacer la llamada.
;
; Lo que es diferente en ambos modos de visualización es la manera de finalizar:
; a) En modo normal lo lógico es finalizar con cualquier interacción del usuario, lo hago comprobando
; GetLastInputInfo() en USER32.DLL, que me dice la última vez que el usuario hizo algo.
; b) En modo previo hay que finalizar cuando nos cierren la ventanita (cerrado el diálogo de selección de
; salvapantallas) o cuando se pase a otro salvapantallas seleccionándolo en el combo, esto no es fácil
; de detectar porque en ese caso no nos cierran el proceso, sino que queda colgado del proceso
; principal, pero esto se puede detectar ayudado con las funciones de SmOke_N que se incluyen.
;**********************************************************************************************************
If $modo = "/s" Then
; Modo normal, ejecutándose hasta que haya interacción de usuario
Global $USER32 = DllOpen("user32.dll")
Global $previo = _GetLastInputInfo()
While 1
If _GetLastInputInfo() <> $previo Then ExitLoop
;**********************************************
; Dibujamos en nuestro GUI
;**********************************************
WEnd
DllClose($USER32)
Exit
Else
; Modo previo, ejecutándose hasta que se cierre el padre, se seleccione otro salvapantallas, o
; se pase a modo configuración
Global $KERNEL32 = DllOpen("kernel32.dll")
Global $child_PID, $parent_PID = _ProcessGetParent(@AutoItPID)
Sleep(500) ;damos tiempo a que se cierre el previo del salvapantallas anterior
While 1
If Not WinExists($GUIscr) Then ExitLoop ; Nos han cerrado la ventana
$child_PID = _ProcessGetChildren($parent_PID)
If $child_PID[0] > 1 Then ExitLoop ; han seleccionado otro salvapantallas en el ComboBox
;**********************************************
; Dibujamos en nuestro GUI
;**********************************************
WEnd
DllClose($KERNEL32)
Exit
EndIf
EndIf
;;;;;;;;;;;;;;;;;;;;;;
; FUNCIONES DE APOYO ;
;;;;;;;;;;;;;;;;;;;;;;
Func _GetLastInputInfo()
Local $struct = DllStructCreate("uint;dword")
DllStructSetData($struct, 1, DllStructGetSize($struct))
DllCall($USER32, "int", "GetLastInputInfo", "ptr", DllStructGetPtr($struct))
Local $ticksSinceIdle = DllStructGetData($struct, 2)
Return $ticksSinceIdle
EndFunc
; Funciones para encontrar procesos padre e hijos de otro proceso dado, originales de SmOke_N
; http://www.autoitscript.com/forum/index.php?showtopic=78445&st=0&p=566572
; Las he modificado para acceder a las dll a través de DLLOpen() y hacerlas más eficientes
Func _ProcessGetParent($i_pid) ;get PID from parent process, done by SmOke_N
Local $TH32CS_SNAPPROCESS = 0x00000002
Local $a_tool_help = DllCall($KERNEL32, "long", "CreateToolhelp32Snapshot", "int", $TH32CS_SNAPPROCESS, "int", 0)
If IsArray($a_tool_help) = 0 Or $a_tool_help[0] = -1 Then Return SetError(1, 0, $i_pid)
Local $tagPROCESSENTRY32 = _
DllStructCreate( _
"dword dwsize;" & _
"dword cntUsage;" & _
"dword th32ProcessID;" & _
"uint th32DefaultHeapID;" & _
"dword th32ModuleID;" & _
"dword cntThreads;" & _
"dword th32ParentProcessID;" & _
"long pcPriClassBase;" & _
"dword dwFlags;" & _
"char szExeFile[260]" _
)
DllStructSetData($tagPROCESSENTRY32, 1, DllStructGetSize($tagPROCESSENTRY32))
Local $p_PROCESSENTRY32 = DllStructGetPtr($tagPROCESSENTRY32)
Local $a_pfirst = DllCall($KERNEL32, "int", "Process32First", "long", $a_tool_help[0], "ptr", $p_PROCESSENTRY32)
If IsArray($a_pfirst) = 0 Then Return SetError(2, 0, $i_pid)
Local $a_pnext, $i_return = 0
If DllStructGetData($tagPROCESSENTRY32, "th32ProcessID") = $i_pid Then
$i_return = DllStructGetData($tagPROCESSENTRY32, "th32ParentProcessID")
DllCall($KERNEL32, "int", "CloseHandle", "long", $a_tool_help[0])
If $i_return Then Return $i_return
Return $i_pid
EndIf
While @error = 0
$a_pnext = DllCall($KERNEL32, "int", "Process32Next", "long", $a_tool_help[0], "ptr", $p_PROCESSENTRY32)
If DllStructGetData($tagPROCESSENTRY32, "th32ProcessID") = $i_pid Then
$i_return = DllStructGetData($tagPROCESSENTRY32, "th32ParentProcessID")
If $i_return Then ExitLoop
$i_return = $i_pid
ExitLoop
EndIf
WEnd
DllCall($KERNEL32, "int", "CloseHandle", "long", $a_tool_help[0])
Return $i_return
EndFunc ;==>_ProcessGetParent
Func _ProcessGetChildren($i_pid) ; First level children processes only, done by SmOke_N
Local Const $TH32CS_SNAPPROCESS = 0x00000002
Local $a_tool_help = DllCall($KERNEL32, "long", "CreateToolhelp32Snapshot", "int", $TH32CS_SNAPPROCESS, "int", 0)
If IsArray($a_tool_help) = 0 Or $a_tool_help[0] = -1 Then Return SetError(1, 0, $i_pid)
Local $tagPROCESSENTRY32 = _
DllStructCreate _
( _
"dword dwsize;" & _
"dword cntUsage;" & _
"dword th32ProcessID;" & _
"uint th32DefaultHeapID;" & _
"dword th32ModuleID;" & _
"dword cntThreads;" & _
"dword th32ParentProcessID;" & _
"long pcPriClassBase;" & _
"dword dwFlags;" & _
"char szExeFile[260]" _
)
DllStructSetData($tagPROCESSENTRY32, 1, DllStructGetSize($tagPROCESSENTRY32))
Local $p_PROCESSENTRY32 = DllStructGetPtr($tagPROCESSENTRY32)
Local $a_pfirst = DllCall($KERNEL32, "int", "Process32First", "long", $a_tool_help[0], "ptr", $p_PROCESSENTRY32)
If IsArray($a_pfirst) = 0 Then Return SetError(2, 0, $i_pid)
Local $a_pnext, $a_children[11] = [10], $i_child_pid, $i_parent_pid, $i_add = 0
$i_child_pid = DllStructGetData($tagPROCESSENTRY32, "th32ProcessID")
If $i_child_pid <> $i_pid Then
$i_parent_pid = DllStructGetData($tagPROCESSENTRY32, "th32ParentProcessID")
If $i_parent_pid = $i_pid Then
$i_add += 1
$a_children[$i_add] = $i_child_pid
EndIf
EndIf
While 1
$a_pnext = DllCall($KERNEL32, "int", "Process32Next", "long", $a_tool_help[0], "ptr", $p_PROCESSENTRY32)
If IsArray($a_pnext) And $a_pnext[0] = 0 Then ExitLoop
$i_child_pid = DllStructGetData($tagPROCESSENTRY32, "th32ProcessID")
If $i_child_pid <> $i_pid Then
$i_parent_pid = DllStructGetData($tagPROCESSENTRY32, "th32ParentProcessID")
If $i_parent_pid = $i_pid Then
If $i_add = $a_children[0] Then
ReDim $a_children[$a_children[0] + 10]
$a_children[0] = $a_children[0] + 10
EndIf
$i_add += 1
$a_children[$i_add] = $i_child_pid
EndIf
EndIf
WEnd
If $i_add <> 0 Then
ReDim $a_children[$i_add + 1]
$a_children[0] = $i_add
EndIf
DllCall($KERNEL32, "int", "CloseHandle", "long", $a_tool_help[0])
If $i_add Then Return $a_children
Return SetError(3, 0, 0)
EndFunc ;==>_ProcessGetChildren