Plantilla para Salvapantallas

y programas personales para otros como tu, puede que te corrijan ;)
Responder
Avatar de Usuario
Ximorro
Profesional del Autoit
Mensajes: 1500
Registrado: 10 Jul 2009, 12:35
Ubicación: Castellón, España

Plantilla para Salvapantallas

Mensaje por Ximorro »

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.

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
Última edición por Ximorro el 12 Abr 2010, 11:10, editado 9 veces en total.
"¿Y no será que en este mundo hay cada vez más gente y menos personas?". Mafalda (Quino)
Avatar de Usuario
Chefito
Profesional del Autoit
Mensajes: 2035
Registrado: 21 Feb 2008, 18:42
Ubicación: Albacete/Cuenca (España)

Re: Plantilla para Salvapantallas

Mensaje por Chefito »

Muy interesante :smt023 :smt023 :smt023 .
Gracias por compartir :smt024 .

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 ;).
Avatar de Usuario
Ximorro
Profesional del Autoit
Mensajes: 1500
Registrado: 10 Jul 2009, 12:35
Ubicación: Castellón, España

Re: Plantilla para Salvapantallas

Mensaje por Ximorro »

¡De nada!

Por si ¡ORB! es un poco complicado de entender, os pongo ahora un ejemplo ultra-mega-fácil para newbies, ¡vais a flipar de lo fácil que es! Haced estas modificaciones en la plantilla:
Los dos bloques "Dibujamos en nuestro GUI" los cambiamos por:

Código: Seleccionar todo

GUISetBkColor(Random(0, 16777215, 1), $GUIscr)
Sleep(500)
¡¡YA ESTÁ!! :smt003

En este caso es tan simple que podemos repetir el código, no hace falta hacer una función.
Se compila, se cambia el EXE por SCR, copiamos a windows\system32 y a seleccionarlo.

Bueno, para afinar mejor se cambia el identificador al principio de la plantilla de "Mi identificador" a por ejemplo "ColoritosID", para que no se pegue con otros.
Y donde se indica que va el GUI de la configuración podemos poner:

Código: Seleccionar todo

MsgBox(64+8192, "Coloritos", "Salvapantallas creado con la plantilla de Ximorro.")
Exit
Así saldrá algo aunque no haya parámetros que configurar. ;-)
"¿Y no será que en este mundo hay cada vez más gente y menos personas?". Mafalda (Quino)
joaquin
Aprendiz de Mago
Mensajes: 48
Registrado: 04 Jun 2008, 22:31
Ubicación: Barcelona, España

Re: Plantilla para Salvapantallas

Mensaje por joaquin »

Molt xulo l'ORB!!
Gràcies per la plantilla i les explicacions!!
:smt002
Salutacions
Responder