Es uno de estos programas de control remoto que corren por ahí, que te permiten controlar un ordenador desde otro, pudiendo ver el escritorio del ordenador remoto y hacer clicks por ahí, o enviando teclas.
Este lo optimizé para que me pueda conectar desde cualquier dispositivo (en concreto, un smartphone Android, que aun no lo tengo [mañana me llega :P]).
La parte del cliente, el que se conecta, en el ordenador que queremos controlar, la queria hacer en el lenguaje de Android, pero como que todavia no he podido hacer ningun programa para esta plataforma ni siquiera lo puedo probar [pues el emulador es muy lento], lo hize con el Flex SDK 4, con FlashDevelop [para hacer aplicaciones Flash completamente gratis :D], ya que los .swf con ActionScript 3 tambien funcionan en Android.
La aplicacion principal, el servidor, esta hecho en AutoIt. este es el script:
Código: Seleccionar todo
#include <GDIPlus.au3>
#include <ScreenCapture.au3>
Opt("MouseClickDragDelay", 50)
Dim $deviceScreenWidth = 480
Dim $deviceScreenHeight = 770
Dim $deviceRatio = $deviceScreenWidth / $deviceScreenHeight
Const $DEBUG = False ; If true, waits 1 second before executing any command that depends of the state of the remote machine [window active]
Const $RESIZE_TO_BIGGER = False
#cs If true, if the area to capture is smaller than the device screen, the image will be resized to fit the
device screen and then sent. If false, it won't resize it, send it, and the client will resize to fit the image. True requires more
bandwidth, but the quality of the resized image is a lot better than False.
#ce
Const $CLICK = "1"
Const $DOWN = "2"
Const $UP = "3"
Const $RIGHT = "4"
Const $LEFT = "5"
Const $SCREEN = "6"
Const $WINDOW = "7"
Const $LOGIN = "a"
Const $RESOLUTION = "b"
Const $PASSWORD = "c"
Const $AUTOIT = "d"
Const $RAW = "e"
Const $storedPassword = "myprivatepassword"
Global $regionStartX
Global $regionStartY
Global $regionEndX
Global $regionEndY
Func getRegionWidth()
Return $regionEndX - $regionStartX
EndFunc
Func getRegionHeight()
Return $regionEndY - $regionStartY
EndFunc
_ScreenCapture_SetBMPFormat(0)
TCPStartup()
Dim $srvSocket, $cliSocket
$srvSocket = TCPListen("127.0.0.1", 1045) ; Localhost connection
;$srvSocket = TCPListen("192.168.1.10", 1045) ; LAN connection
;$srvSocket = TCPListen("192.168.0.2", 1045) ; LAN connection
If $srvSocket == -1 Then
Exit
EndIf
Dim $error = True
Dim $ratio = 0
Dim $allowed
While 1
If $error Then
Do
$cliSocket = TCPAccept($srvSocket)
$allowed = False
$regionStartX = 0
$regionStartY = 0
$regionEndX = $regionStartX + @DesktopWidth ; @DesktopWidth
$regionEndY = $regionStartY + @DesktopHeight ; @DesktopHeight
Until $cliSocket <> -1
EndIf
;size(2)code(a-zA-Z0-9)data
$s = TCPRecv($cliSocket, 2)
$size = Int($s)
If $size > 0 Then
$str = TCPRecv($cliSocket, $size)
While StringLen($str) < $size
$str = $str & TCPRecv($cliSocket, $size - StringLen($str))
WEnd
$codi = StringLeft($str, 1)
$dades = StringTrimLeft($str, 1)
If $codi == $LOGIN Then
If $dades == "thisismypassword" Then
If $DEBUG Then sleep(1000)
$allowed = True
EndIf
ElseIf $codi == $RESOLUTION Then
$str = StringSplit($dades, ",", 2)
$deviceScreenWidth = Int($str[0])
$deviceScreenHeight = Int($str[1])
$deviceRatio = $deviceScreenWidth / $deviceScreenHeight
;TrayTip("", $deviceScreenWidth & ", " & $deviceScreenHeight, 5)
ElseIf $codi == $PASSWORD And $allowed Then
Send($storedPassword)
ElseIf $codi == $AUTOIT And $allowed Then
If $DEBUG Then sleep(1000)
Send($dades, 0)
ElseIf $codi == $RAW And $allowed Then
If $DEBUG Then sleep(1000)
Send($dades, 1)
ElseIf $codi == $CLICK And $allowed Then
$str = StringSplit($dades, ",", 2)
$x0 = $str[1] / $ratio + $regionStartX
$y0 = $str[2] / $ratio + $regionStartY
$xf = $str[3] / $ratio + $regionStartX
$yf = $str[4] / $ratio + $regionStartY
If $str[0] == "r" Then ; right
MouseClickDrag("secondary", $x0, $y0, $xf, $yf, 3)
ElseIf $str[0] == "l" Then ; left
MouseClickDrag("primary", $x0, $y0, $xf, $yf, 3)
EndIf
ElseIf $codi == $DOWN Then
$val = Int($dades)
$regionEndY = $regionEndY + $val / $ratio
ElseIf $codi == $UP Then
$val = Int($dades)
$regionStartY = $regionStartY + $val / $ratio
ElseIf $codi == $RIGHT Then
$val = Int($dades)
$regionEndX = $regionEndX + $val / $ratio
ElseIf $codi == $LEFT Then
$val = Int($dades)
$regionStartX = $regionStartX + $val / $ratio
ElseIf $codi == $SCREEN Then
$regionStartX = 0
$regionStartY = 0
$regionEndX = $regionStartX + @DesktopWidth
$regionEndY = $regionStartY + @DesktopHeight
ElseIf $codi == $WINDOW Then
If $DEBUG Then sleep(1000)
$arr = WinGetPos("[ACTIVE]")
$regionStartX = $arr[0]
$regionStartY = $arr[1]
$regionEndX = $regionStartX + $arr[2]
$regionEndY = $regionStartY + $arr[3]
EndIf
EndIf
$w = getRegionWidth()
$h = getRegionHeight()
$regionRatio = $w / $h
If ($deviceRatio > 1 And $regionRatio > 1) Or ($deviceRatio < 1 And $regionRatio < 1) Then ; The device won't flip the image
$ratio = $deviceScreenWidth / $w
if $ratio * $h > $deviceScreenHeight Then $ratio = $deviceScreenHeight / $h
Else ; The device will flip the image
$ratio = $deviceScreenWidth / $h
if $ratio * $w > $deviceScreenHeight Then $ratio = $deviceScreenHeight / $w
EndIf
If $ratio < 1 Or $RESIZE_TO_BIGGER Then
$deviceW = $w * $ratio
$deviceH = $h * $ratio
Else
$deviceW = $w
$deviceH = $h
EndIf
If $allowed Then
$img = _ScreenCapture_Capture("", $regionStartX, $regionStartY, $regionEndX, $regionEndY)
_ImageResize($img, @ScriptDir & "\tmp.jpg", $deviceW, $deviceH)
_WinAPI_DeleteObject($img)
Else
$img = _ScreenCapture_Capture("", 0, 0, 0, 0)
_ImageResize($img, @ScriptDir & "\tmp.jpg", $deviceScreenWidth, $deviceScreenHeight)
_WinAPI_DeleteObject($img)
EndIf
$size = FileGetSize(@ScriptDir & "\tmp.jpg")
$oFile = FileOpen(@ScriptDir & "\tmp.jpg", 0)
$bin = FileRead($oFile)
FileClose($oFile)
SetError(0)
$str = to10chars(String($size))
TCPSend($cliSocket, $str)
TCPSend($cliSocket, $bin)
If @error Then
$error = True
Else
$error = False
EndIf
WEnd
Func to10chars($str)
$num = 9 - StringLen($str)
For $i = 1 To $num
$str = "0" & $str
Next
$str = "s" & $str
Return $str
EndFunc
Func _ImageResize($sInImage, $sOutImage, $iW, $iH)
Local $hWnd, $hDC, $hBMP, $hImage1, $hImage2, $hGraphic, $CLSID, $i = 0
;OutFile path, to use later on.
Local $sOP = StringLeft($sOutImage, StringInStr($sOutImage, "\", 0, -1))
;OutFile name, to use later on.
Local $sOF = StringMid($sOutImage, StringInStr($sOutImage, "\", 0, -1) + 1)
;OutFile extension , to use for the encoder later on.
Local $Ext = StringUpper(StringMid($sOutImage, StringInStr($sOutImage, ".", 0, -1) + 1))
; Win api to create blank bitmap at the width and height to put your resized image on.
$hWnd = _WinAPI_GetDesktopWindow()
$hDC = _WinAPI_GetDC($hWnd)
$hBMP = _WinAPI_CreateCompatibleBitmap($hDC, $iW, $iH)
_WinAPI_ReleaseDC($hWnd, $hDC)
;Start GDIPlus
_GDIPlus_Startup()
;Get the handle of blank bitmap you created above as an image
$hImage1 = _GDIPlus_BitmapCreateFromHBITMAP ($hBMP)
;Load the image you want to resize.
If IsString($sInImage) Then
$hImage2 = _GDIPlus_ImageLoadFromFile($sInImage)
Else
$hImage2 = _GDIPlus_BitmapCreateFromHBITMAP($sInImage) ; This function is modified here, so we don't have to write the bmp to the hard disk
EndIf
;Get the graphic context of the blank bitmap
$hGraphic = _GDIPlus_ImageGetGraphicsContext ($hImage1)
;Draw the loaded image onto the blank bitmap at the size you want
_GDIPLus_GraphicsDrawImageRect($hGraphic, $hImage2, 0, 0, $iW, $iH)
;Get the encoder of to save the resized image in the format you want.
$CLSID = _GDIPlus_EncodersGetCLSID($Ext)
;Generate a number for out file that doesn't already exist, so you don't overwrite an existing image.
Do
$i += 1
Until (Not FileExists($sOP & $i & "_" & $sOF))
;Prefix the number to the begining of the output filename
;$sOutImage = $sOP & $i & "_" & $sOF
$sOutImage = $sOP & $sOF
;Save the new resized image.
_GDIPlus_ImageSaveToFileEx($hImage1, $sOutImage, $CLSID)
;Clean up and shutdown GDIPlus.
_GDIPlus_ImageDispose($hImage1)
_GDIPlus_ImageDispose($hImage2)
_GDIPlus_GraphicsDispose ($hGraphic)
_WinAPI_DeleteObject($hBMP)
_GDIPlus_Shutdown()
EndFunc
Lo que hace es:
1 -> Monta el servidor en la ip que se le ponga [ahora esta puesta para localhost, en el puerto 1045]
2 -> Acepta un cliente
3 -> Mira si el cliente le envia algun comando [estos son hacer clic en algun lugar, cambiar la area de captura, etc]
4 -> Luego procede a la captura de la zona de la pantalla que se necesite
5 -> Redimensiona la imagen [para que ocupe menos] y la comprime en jpg, guardandola en el disco duro
6 -> Abre la imagen en formato binario y se la manda al cliente
7 -> Si el cliente se desconectó, vuelve al paso 2.
8 -> Sino, vuelve al paso 3.
Alguna optimizacion que no se hacer, a ver si alguien me puede ayudar, es que una vez redimensionada y comprimida la imagen, es como hacerlo para no tener que guardarla y abrirla en el disco duro para obtener los datos en binario.
Otras cosas... por el principio os encontrareis estas dos constantes de opcion:
$DEBUG -> esta hace que se espere 1 segundo antes de enviar algun comando. Esto es para debugar el programa si haces una conexion a localhost [porque estas controlando tu propia maquina, que no tiene mucho sentido] y que te de tiempo a canviar de ventana, etc
$RESIZE_TO_BIGGER -> esta hace que si la area de captura es más pequeña que la pantalla del dispositivo, la redimensiona para hacerla mas grande. Si esta a True, la redimensiona, y queda una imagen mucho mas bonita, con mucha mas resolucion, pero necesita mas ancho de banda. Si esta a False, no la redimensiona, y el cliente ya se encargará de hacerla mas grande. Esta opcion consume menos ancho de banda, pero la resolucion es bastante menor.
Yo recomiendo que lo pongan a True. Tienen unas capturas en el .zip de las dos opciones, para que comparen :P
Luego, flash, cuando se conecta en algun sitio, necesita una cosa que se llama policy file, para saber si se puede conectar o no. Asi que tambien he montado un pequeño servidor, llamado policyServer, que lo que hace es escuchar en el puerto 843 por peticiones de policy file, y le envia el archivo policy.xml, tambien incluido en el .zip
Tienen que configurarlo para que escuche en su IP, sino no se les va a poder abrir.
Y para acabar, tengo que advertir que esta aplicación no es segura, si haces que el servidor acepte conexiones de internet. En un principio, cualquier podia entrar en vuestra maquina con este servidor, y controlarla [pudiendo acceder a datos personales, borrar archivos, etc]. Para evitar esto he hecho una medida de seguridad muy debil, que es hechar un password. Hasta que el cliente no se loguee el servidor no hace caso de lo que le diga el cliente. Asi nos evitamos que cualquiera pueda entrar, pero ahora cualquiera que ponga un packet sniffer entre tu dispositivo y tu ordenador [para poder cojer la contraseña que mandas] podra entrar.
Esto solo se podria solucionar con:
-> Criptografia de clave publica.... muy complicada de implementar
-> enviar la informacion encriptada con un AES con una clave fijada, que solo tu sepas. Pero tampoco tengo el tiempo para implementar AES para autoit.
Es possible que yo lo use, porque no creo que haya alguien tan paranoico como para ponerme un packet sniffer por ahí y acceder a mi ordenador... que ya ves, lo uso solo para programar y poco mas xD
O sino tienen otras alternativas, como usando este servidor con este cliente para android, pero bueno, esto ya va a gusto personal, ya no es AutoIt.
Almenos me pareció interesante desenvolupar esto. No lo he podido provar en un Android [todavia!, queda poco :P], pero lo he provado de PC a PC, uno conectado con mi internet, y otro conectado por wifi con una red de un vecino que la tiene abierta, y la veolcidad era mas que aceptable. Algo mas de una imagen por segundo.
Espero que les guste :D
olivarra1