Hoy tenía la necesidad de configurar una máquina virtual de Azure. Podía haber optado por la manera simple, que es conectarme al portal y configurarla, pero ya que tenía el SDK de Azure instalado, he decidido probar a ver cuanto puedo hacer desde línea de comandos.
Creando un grupo de afinidad y una storage account
El primer paso es crear un grupo de afinidad que es, básicamente, una sub-región donde podemos tener varios servicios. En mi caso lo configuraremos en la región del oeste de Europa:
El primer paso es conseguir la máquina virtual que queremos agregar, para lo cual podemos filtrar y seleccionar solamente las máquinas que cumplen ciertos requisitos:
Get-AzureVMImage | where {$_.Label -match "SQL Server 2014"} | Select Label
Una vez que hemos seleccionado una imagen base, la podemos asignar a una variable para su posterior uso:
$vm = (Get-AzureVMImage | where {$_.Label -match "SQL Server 2014"})[6]
Finalmente podemos crear nuestra máquina virtual, con los comandos New-AzureVMConfig, Add-AzureProvisioningConfig y New-AzureVM:
Si todo ha ido bien, podremos ver que, tras ejecutar el siguiente comando:
Get-AzureVM
Se muestra nuestra máquina virtual en estado de «Provisioning».
Conclusiones
Ha sido cuanto menos curioso (y más difícil de lo que imaginaba en un principio) no solo por la sintaxis de los comandos, sino porque a veces no somos conscientes de todos los procesos que se ejecutan cuando creamos una máquina virtual.
El hecho de que esto sea un script, es que lo podemos meter en un bucle for y crear 20-50-100 máquinas de la misma manera, según nuestras necesidades.
En el siguiente artículo veremos qué podemos hacer una vez que el provisionamiento está completo. Nos podremos conectar por Powershell como si de SSH se tratara? Podremos descargar el perfil de RDP desde la línea de comandos? La respuesta a esta y más preguntas en el próximo artículo de la serie.
When we test VS Anywhere in a production environment, one of the things that we tend to do is to open 2 sessions of Visual Studio on either side of the screen, so we can make quick tests of specific features.
This requires opening 2 different instances of visual studio by hand, and attach each side of the screen, something tedious, boring, and, above all, automatable. So I set out to find the way of solving this small issue.
The path
If we have Windows 7 or higher we can make use of Aero Snap, using Win+Left or Win+Right, it sets a window to the left or to the right of the screen. It seemed simple, I would only need to emulate this key combination, something that it seemed easy to do:
Theorically, we can solve this by using Powershell. In fact, in the following entry of TechNet Provide Input to Applications with PowerShellexplains how to send commands to an application using SendKeys, resulting in the following code:
Surprise: is not there. On the other hand, looking a bit online, I found that the Windows keystroke could be simulated by using the Ctrl + Esc.
Second surprise: ITSIMULATES the WINDOWS key, and nothing else. When you press that combination, It jumps right to the desktop or to the start menu, it is not possible to use it to simulate a key combination involving Win + whatever, so that by this way little more we can do.
On the other hand, if what we are doing is a keyboard shortcut, surely matches some kind of command available on the Windows API that makes the action of Aero Snap, but no, there is no API, no documentation and there is no reference except the brand name, so this is another dead end.
There was still another option, which was to move the window directly using a custom program, after some search I found this piece of code, allowing you to do what I wanted using Powershell:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
It was not too complex, but I chose to do a small application with C# so that it could solve the problem, using native calls to the Windows API through P/Invoke:
Importing functions and moving windows
To use the Windows API functions we have to copy their header in our function, define it as external (.NET framework will already know what to do) and define the DLL from which we import, in this case the function MoveWindow is located in user32.dll:
[DllImport ("user32.dll")]
public static extern bool MoveWindow(IntPtr hWnd, int X, int Y, int nWidth, int nHeight, bool bRepaint);
We will have to repeat this process for each function that we want to import, a we will also need to add the following using:
using System.Runtime.InteropServices;
To be able to move the window, we need its hWnd, which is a unique window identifier. There are many ways to locate it, and one of them is from the list of processes, selecting those whose main window matched with what was looking for, in this case “Visual Studio”.
For moving the window, we need to set an origin and a size, and for this we could either fix them manually, or rely on the resolution of the screen, using the following functions:
These are found within the namespace System.Windows.Forms, which requires in addition a reference as System.Drawing from our project.
The really interesting thing about this, is that it allows us to move the window to the position we would like with any size, so we can take advantage of it in order to set any number of windows, being these equally distributed.
Finally we need a couple of additional calls to Windows API functions to maximize the window before moving it, and to set the focus, which are imported in a similar way:
[DllImport ("user32.dll")]
public static extern bool ShowWindow(IntPtr hWnd, int X);
[DllImport ("user32.dll")]
public static extern bool SetFocus(IntPtr hWnd);
Launching applications and input parameters
In addition to the functionality of moving windows, it could be interesting to launch the apps we want to move, so a bit of extra code to generate a process, to set the name of the file, and, after launching it, a small sleep while loading:
Process p = new Process();
p.StartInfo.FileName = "notepad.exe";
p.Start ();
System.Threading.Thread.Sleep (2000);
To make it simpler, the last step is to convert all the parameters used to input arguments of the program, being finally the project a console application, which gets, in this order;
Path of the executable
Title of the window to search
Number of processes to launch
Waiting for each process
In the case of a 3 windows launch for notepad.exe, this is the output showing the console application:
On the other hand we have 3 windows, equally distributed:
As the last detail, the window title is set using the following code block:
Console.Title = "WinResize";
Wrapping up
This is a small app that solves a very specific scenario can be widely improved and I’ll probably work for some more time on it, here are some ideas for future improvements:
Make all the script in powershell, it’s possible, and we can make P/Invoke without problems
Emulate the keypress (must be a code associated with the windows key, and a way to press it!) simulating the keyboard.
Rather than take all processes available, use the ID of those we’ve just created.
The code is available for free and licensed under open source. If you want to contribute please send your pull request :)
Para hacer pruebas de VS Anywhere en un entorno de producción, una de las cosas que solemos hacer es abrir 2 sesiones de Visual Studio a cada lado de la pantalla, de esta manera podemos hacer pruebas rápidas de características específicas.
El problema es que esto requiere abrir 2 instancias de visual studio de manera manual, y fijarlas cada una a un lado de la pantalla, algo tedioso, aburrido, y, sobre todo, automatizable. Así que me lancé a la búsqueda de la verdad, qué me puede solucionar?
El camino recorrido
Si tenemos Windows 7 o superior podemos hacer uso de Aero Snap, que usando Win+Izquierda o Win+Derecha, fija una ventana a la izquierda o a la derecha de la pantalla. Parecía sencillo, solamente tendríamos que emular esta combinación de teclas, lo cual, en un principio era algo fácil de hacer:
En teoría, usando Powershell, podríamos hacerlo, de hecho, en la siguiente entrada de TechNet Provide Input to Applications with PowerShell se explica cómo enviar comandos a una aplicación empleando SendKeys, siendo este código el resultado:
Sorpresa: no está. Por otra parte, buscando un poco por internet, encontré que se podría simular la pulsación de la tecla Windows usando la Ctrl + Esc.
Segunda sorpresa: SIMULA LA TECLA WINDOWS, y nada más. En el momento que se pulsa eso, se salta al escritorio, o al menú inicio, no siendo posible usarlo para simular una combinación de teclas que involucre Win + cualquier cosa, así que por este camino poco más podemos hacer.
Por otro lado, si lo que estamos haciendo es un atajo de teclado, seguramente corresponda con alguna API de windows que haga la acción de Aero Snap, pero no, no hay API, no hay documentación y no hay ninguna referencia salvo el nombre comercial. con lo cual este es otro callejón sin salida,
Aún quedaba una opción, que era mover directamente la ventana usando código, tras algo de búsqueda encontré este código, que permitía hacer lo que quería usando powershell:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
No era demasiado complejo, pero opté por hacer una pequeña aplicación C# para que me resolviera el problema, usando las llamadas nativas a la API de Windows a través de P/Invoke:
Importando las funciones y moviendo la ventana
Para usar funciones de la API de Windows hemos de copiar su cabecera en nuestra función, definirla como externa (el framework ya sabrá que hacer) y definir la DLL de la que realizaremos la importación, en este caso la función MoveWindow situada en user32.dll:
[DllImport("user32.dll")]
public static extern bool MoveWindow(IntPtr hWnd, int X, int Y, int nWidth, int nHeight, bool bRepaint);
Deberemos repetir este proceso para cada función que queramos importar, y además, para todo esto, necesitaremos agregar el siguiente using:
using System.Runtime.InteropServices;
Para poder mover la ventana, necesitamos su hWnd, que es un identificador de ventana único. Hay muchas maneras de localizarlo, y una de ellas es a partir de la lista de procesos, seleccionando aquellos cuya ventana principal coincidiera con lo que estaba buscando, en este caso Visual Studio.
foreach (Process proc in Process.GetProcesses())
{
if (proc.MainWindowTitle.Contains("Visual Studio"))
{
IntPtr handle = proc.MainWindowHandle;
...
MoveWindow(handle ...);
...
}
}
Para poder mover la pantalla necesitamos establecer un punto origen y un tamaño, y para ello podríamos o bien fijarlos manualmente, o basarnos en la resolución de la pantalla, usando para ello las siguientes funciones:
que se encuentran dentro del namespace System.Windows.Forms, que requiere además una referencia as System.Drawing desde nuestro proyecto.
Lo realmente interesante de esto, es que me permitía mover la ventana a la posición que quisiera con cualquier tamaño, así que lo aproveché para poder establecer cualquier número de ventanas, estando estas igualmente distribuidas.
Finalmente necesitamos un par de llamadas más a funciones de la API de Windows para maximizar la ventana antes de moverla, y para establecer el foco, que se importan de manera similar:
[DllImport("user32.dll")]
public static extern bool ShowWindow(IntPtr hWnd, int X);
[DllImport("user32.dll")]
public static extern bool SetFocus(IntPtr hWnd);
Lanzando las aplicaciones y parámetros de entrada
Además de la funcionalidad de mover ventanas, podría ser interesante lanzar las aplicaciones, así que un poco de código para generar un proceso, establecer el nombre del fichero, y, tras lanzarlo, una leve espera mientras carga:
Process p = new Process();
p.StartInfo.FileName = "notepad.exe";
p.Start();
System.Threading.Thread.Sleep(2000);
Para hacerlo más sencillo, el último paso es convertir todos los parámetros usados a parámetros de entrada, siendo finalmente el proyecto una aplicación de consola, que recibe, en este orden;
Ruta del ejecutable
Título de la ventana a buscar
Número de procesos a lanzar
Espera para cada proceso
La salida que muestra la aplicación de consola es ésta, para el caso de 3 ventanas de notepad.exe:
Por otra parte tenemos las 3 ventanas, igualmente distribuidas.
Como último detalle, el título de la ventana está fijado usando el siguiente bloque de código:
Console.Title = "WinResize";
Conclusiones
Esta es una pequeña app que en mi caso soluciona un escenario muy concreto, se puede expandir hasta límites insospechados, y posiblemente lo haga, aunque hay cosas que se me han quedado en el tintero seguro, y que dejo como idea para el futuro:
Hacer todo el script en powershell, es posible, y podemos hacer P/Invoke sin problemas
Emular la pulsación de la tecla (tiene que haber un código asociado a la tecla windows, y una manera de pulsarlo!) simulando el teclado.
En vez de recorrer todos los procesos, usar el identificador de los que acabo de crear.
El código está disponible de manera gratuita y bajo licencia open-source. Si quieres contribuir envía tu pull request :)
Powershell es una herramienta muy versátil para desarrolladores y administradores de sistema, ya que, al igual que la shell de unix, permite automatizar tareas repetitivas con facilidad. Una de estas tareas puede ser controlar un proceso, aplicación o programa, cuanto tiempo lleva activo, y el uso de recursos que está haciendo.
En este artículo se verá cómo obtener el tiempo de un proceso en Powershell, el uso de CPU y RAM, así como enviar un e-mail con esa información de manera periódica.