Erwin.Ried.cl / Desarrollos / Windows /
Servicios, servicios y más servicios

Hay ciertas compañías que nunca se cansan con sus prácticas primitivas: pedir reiniciar el equipo, alterar la configuración del usuario y por sobre todo llenar tu equipo de inútiles servicios en segundo plano, sí, es una indirecta para ti, VMWare. En este artículo veremos como subsanar un poco el desperdicio de recursos que producen servicios de aplicaciones como VMWare Workstation o Player, aunque es aplicable en cualquier otra (17/11/2010 04:58 PM)


Hace mucho tiempo, solucioné otro problema que también se originó con los chicos de VMWare. El problema anterior era sobre la incansable consulta de registros (problema que claramente siguen dejando activo en sus nuevos productos).

Muchos productores de software se han dado cuenta de la importancia de alivianar sus soluciones. Inclusive Microsoft muestra esta tendencia con Windows 7, haciendo cosas tan simples como por ejemplo si no tenemos bluetooth, el servicio relacionado simplemente no es cargado, si no conectamos un teléfono Windows Phone nunca aparece precargado el centro de sincronización para dispositivos móviles, entre otras.


1. El problema

"Ufff, Windows, cada día más lento. ¿Por qué no pueden hacer algo que funcione bien como <xyz>?"

La pregunta de arriba suele derivar en un posible formateo del sistema completo, algo que por experiencia personal es totalmente innecesario y una pérdida de tiempo. Usar la cabeza, un poco de intuición e investigar es lo único que necesitan.

Colocamos y removemos tantas aplicaciones que perderíamos fácilmente la cuenta, agravando el problema con el actual modelo de instalaciones de Windows, el cual es demasiado permisivo.

Por ahora, en el presente artículo quiero enfocarme solamente en el problema con ciertos programas que se toman la libertad de instalar servicios para su beneficio, los cuales atentan contra nuestros recursos libres, tiempos de inicio de la máquina y estabilidad general. Es cosa de ejecutar "services.msc" (o Administrar equipo / Servicios) para encontrar una lista de servicios de terceros, que están siendo automáticamente puestos en marcha por el sistema operativo. Aunque hay muchas empresas que publican productos que dejan servicios "inútiles", para mí la más emblemática es VMWare, los cuales parecen ser extremadamente testarudos a recibir opiniones y hacer mejoras.


2. Una sencilla solución

Cuando busqué por posibles soluciones, encontré una muy simple pero efectiva llamada VMware Launcher. Se trata de un código en VBS que lanza los servicios en demanda y también los finaliza.

Esta solución tiene algunas falencias.

La más importante, la única que me hizo buscar otra solución, es que necesita que alteremos los permisos de los servicios para poder lanzarlos. Aparte de ser un problema ante cada actualización de VMWare, se pierde funcionalidad, por ejemplo con dispositivos USB, pues los servicios hacen operaciones que requieren privilegios de administrador.


3. Una mejor solución, mucho más general

Entonces, que pasaría si tenemos una nueva solución, configurable, que permita seleccionar lo que queremos hacer con cada servicio de forma rápida y además que no nos solicite alterar cosas. Pasaría que tendríamos una mejor solución, probablemente la mejor posible dado el escenario provisto por ciertos fabricantes.

Además al ser más general, no sólo podría aplicarse a productos de VMWare, sino a cualquier producto que se nos ocurra.


4. Desarrollo

Para el desarrollo de la solución se usará Visual Studio Express C# 2010. Es importante tener conceptos previos sobre uso de las "Settings" en C# pues el panel de opciones trabajará exclusivamente con esta característica.

El objeto principal utilizado será uno denominado RelatedService. Este objeto será la representación de un servicio real del equipo, el cual tiene sólo una particularidad, un modo de inicio personalizado adicional al del equipo:

Código:
/// <summary>
/// Modo de inicio personalizado
/// </summary>
public enum CustomStartMode
{
    StartStop,
    StartOnly,
    SystemAutomatic,
    SystemAutomaticDelayed,
    SystemManual,
    SystemDisabled,
    Unknown
}


Existen tres modos personalizados, StartStop (inicia y finaliza en demanda el servicio), StartOnly (sólo lo inicia y lo deja activo), Unknown (existe un error en el servicio y no se puede determinar el modo).

Los inicios denominados con el prefijo System (como SystemAutomatic) representan un modo de inicio de sistema. Uno de los que puede ser configurado en las opciones del sistema operativo.

El flujo principal de la solución una vez configurada es el siguiente:



Los pasos del flujo son detallados a continuación:

4.1) Búsqueda inicial de servicios (BackgrounderWorker)

Código:
/// <summary>
/// Realiza las acciones de inicio de servicios según la configuración
/// </summary>
/// <param name="backgroundWorkerStart">Para registrar el progreso</param>
internal void SystemStart(BackgroundWorker backgroundWorkerStart)
{
    if (_services.Count > 0)
    {
        int processes = 0;
        double step = 100 / _services.Count;

        foreach (RelatedService r in _services)
        {
            ServiceController s = new ServiceController(r.Id);

            if (isServiceValid(s))
                if (r.Mode == CustomStartMode.StartStop || r.Mode == CustomStartMode.StartOnly)
                    if (s.Status != ServiceControllerStatus.Running)
                    {
                        backgroundWorkerStart.ReportProgress((int)Math.Ceiling((processes++ *
step)));

                        s.Start();
                        s.WaitForStatus(ServiceControllerStatus.Running);
                    }
        }
    }
}


4.2) Inicio de la aplicación

Vea el siguiente punto de este artículo.

4.3) Esperar final de la aplicación (Sistema operativo)

Como aparece en el punto 5 de este artículo, sea cual sea la forma en que se lance la aplicación, se utiliza WaitForExit para esperar su finalización. A diferencia de otros métodos, WaitForExit le pide al sistema operativo que le avise cuando un proceso sea finalizado, consumiendo absolutamente cero tiempo de CPU mientras espera.

4.4) Búsqueda final de servicios (BackgrounderWorker)

Código:
/// <summary>
/// Realiza las acciones de detención de servicios según la configuración
/// </summary>
/// <param name="backgroundWorkerStop">Para registrar el progreso</param>
internal void SystemStop(BackgroundWorker backgroundWorkerStop)
{
    if (_services.Count > 0)
    {
        int processes = 0;
        double step = 100 / _services.Count;

        foreach (RelatedService r in _services)
        {
            ServiceController s = new ServiceController(r.Id);

            if (isServiceValid(s))
                if (r.Mode == CustomStartMode.StartStop)
                    if (s.Status != ServiceControllerStatus.Stopped)
                    {
                        backgroundWorkerStop.ReportProgress((int)Math.Ceiling((processes++ *
step)));

                        s.Stop();
                        s.WaitForStatus(ServiceControllerStatus.Stopped);
                    }
        }
    }
}


5. La herencia entre proceso padre e hijo, los privilegios

Como necesitamos privilegios elevados para iniciar servicios (aunque los servicios usen otra cuenta de usuario) es natural que imaginemos que la aplicación que lanzaremos luego de iniciar sus servicios tendrá los mismos privilegios elevados.

Como a mí no me gusta ni me parece correcto ejecutar aplicaciones con mayores privilegios de los que necesita, había que buscar una solución para "bajar" los privilegios.

A considerar
Cuando ejecutamos cualquier instalador en Windows Vista o superior, exceptuando los basados en MSI Installer los cuales solicitan elevación de requisitos en cierto instante del proceso, el sistema operativo nos muestra una ventana de UAC al abrir el instalador.

Esto es una necesidad evidente, pero es importante considerar que luego de instalar el programa algunos instaladores ofrecen ejecutar inmediatamente el programa recién instalado. Ahora, la primera ejecución de ese programa se haría heredando los privilegios elevados con que cuenta el instalador algo poco considerado, pero bastante peligroso.


La solución ya había sido abordada en el siguiente sitio:
http://odetocode.com/Blogs/scott/archive/2004/10/28/602.aspx

Para integrarla, simplemente se realiza una bifurcación dependiendo si el usuario desea o no "bajar" los privilegios del proceso hijo el cual será lanzado:

Código:
Process p;

if (Settings.Default.launch_limited)
{
    int processId = SaferProcess.CreateSaferProcess(Settings.Default.application_path,
        "", SaferLevel.NormalUser);

    p = Process.GetProcessById(processId);
}
else
{
    // Lanzar la aplicación y esperar
    p = new Process();
    p.StartInfo.FileName = Settings.Default.application_path;

    p.Start();
}

p.WaitForExit();



6. Altamente personalizable

Un usuario final, sin modificar el código de fuente puede personalizar de gran forma esta solución. Las principales modificaciones son accesibles desde el ícono del programa mientras está en ejecución:



Para lograrlo, se debe abrir el archivo de configuraciones adjunto en un editor de textos. Algunas de las configuraciones posibles se muestran y detallan a continuación:

Código:
<setting name="launcher_main_title" serializeAs="String">
    <value>Launcher</value>
</setting>
<setting name="launcher_config_title" serializeAs="String">
    <value>Launcher Configuration</value>
</setting>
<setting name="launcher_tray_title" serializeAs="String">
    <value>Launcher</value>
</setting>
<setting name="application_path_label" serializeAs="String">
    <value>Path to application:</value>
</setting>
<setting name="application_path_title" serializeAs="String">
    <value>Choose a program</value>
</setting>
<setting name="services_add_title" serializeAs="String">
    <value>Add services</value>
</setting>


Todas las configuraciones de arriba, permiten estéticamente cambiar los textos de las etiquetas del programa.

Código:
<setting name="launcher_related_keyword" serializeAs="String">
    <value />
</setting>


La palabra clave configurada por launcher_related_keyword permite realizar una búsqueda automática de servicios relacionados. Por ejemplo si colocamos "vmware" se listarán todos los servicios que tengan nombre para mostrar o interno coincidente con esos caracteres.

Si esta configuración se deja en blanco, no aparece la opción para buscar servicios relacionados nuevamente.



Código:
<setting name="application_path_filename" serializeAs="String">
    <value />
</setting>
<setting name="application_path_filter" serializeAs="String">
    <value>Executable|*.exe</value>
</setting>


Las dos configuraciones de arriba permiten personalizar el archivo de la aplicación que debe ser seleccionado por el usuario, el que lanzará el programa. La configuración application_path_filename permite establecer un nombre predeterminado de ejecutable, por ejemplo "vmware.exe" (escrito en la caja de texto de "Abrir archivo").

La otra configuración establece el filtro. La nomenclatura es la típica para Windows. Por ejemplo, para obligar al usuario a elegir "vmware.exe" o "vmplayer.exe" se usaría:

Código:
<setting name="application_path_filter" serializeAs="String">
    <value>VMware Workstation or Player|vmware.exe;vmplayer.exe</value>
</setting>code]

Así mismo podríamos dar más flexibilidad al usuario:

[code]<setting name="application_path_filter" serializeAs="String">
    <value>VMware Workstation or Player|vmware.exe;vmplayer.exe|Other program
(*.exe)|*.exe</value>
</setting>


Las siguientes configuraciones personalizan aún más la parte gráfica de la interfaz de opciones y el icono al lado del reloj:

Código:
<setting name="services_add_allow" serializeAs="String">
    <value>True</value>
</setting>
<setting name="services_delete_allow" serializeAs="String">
    <value>True</value>
</setting>
<setting name="launcher_tray_icon" serializeAs="String">
    <value />
</setting>
<setting name="trayicon_enable" serializeAs="String">
    <value>True</value>
</setting>


Código:
<setting name="launcher_wizard" serializeAs="String">
    <value>False</value>
</setting>



7. Pensado para agradar al usuario

ServiceLauncher, la solución propuesta por este artículo, está diseñado para el usuario. La interfaz tiene características sutiles como la existencia del cuadro de selección de "modo de arranque del servicio" de sistema siempre habilitado para permitir una personalización rápida.



Además, la barra de progreso de inicio y finalización de servicios aparece suavemente (cuando Aero está activado) y sólo cuando el proceso tarde más de lo esperado, por ejemplo si los servicios necesitan menos de un segundo para iniciar, el usuario no visualizará una barra de progreso.



Hay otras sutilezas, la existencia de un modo "asistido" de configuración, en donde sólo se pregunta por el ejecutable relacionado o la posibilidad, como se vió en el punto anterior, de bloquear funcionalidades. También realiza un traspaso de los argumentos recibidos (siendo completamente transparente).


8. Descarga y código fuente

Ejecutable sin personalización de Process Launcher (38 KB)

Ejecutable personalizado (VMWare Launcher) para Workstation y VMPlayer (79 KB)

Código fuente de la aplicación (134 KB)

El ejecutable sin personalización puede ser alterado como se explica en el siguiente punto del artículo. De esta forma podemos tener una solución única para cada problema con servicios inescrupulosos.

Para instalar, por ejemplo la versión personalizada para VMWare Workstation (cualquier versión), sólo copiamos el ejecutable y el archivo adjunto en el paquete a cualquier parte (de preferencia en el directorio de Workstation) y creamos un acceso directo al mismo (idealmente reemplazando el original de VMWare Workstation en el menú inicio).

Lo ejecutamos con doble clic y en la primera ejecución pedirá que le digamos donde está el producto de VMWare que deseamos usar.

El código fuente es para Visual Studio C# Express 2010.


9. Conclusiones

La solución encontrada, elimina la existencia de consumo de recursos y tiempo de arranque, es personalizable y aplicable en un alto rango de problemas.

En el caso de que la aplicación (digamos VMPlayer) sea utilizada constantemente por el usuario, no tiene sentido utilizar la solución provista por este artículo. El tiempo perdido (overhead) en inicializar y finalizar los servicios podría presentar inconvenientes mayores que tenerlos constantemente cargados, usando memoria y tiempo de CPU. Así, no sería recomendable usar esta solución sobre software que sea iniciado y finalizado con el equipo (como por ejemplo IIS, SQL Server, Apache).

Haga clic sobre una de las estrellas para calificar este artículo.

Opiniones y comentarios (Escribir un nuevo comentario)
Una aportacion cojonuda. Gracias.
Escrito por ADM (08/03/2012 08:57 AM)
¡Gracias!
Escrito por Erwin Ried (14/03/2012 06:57 PM)
Un trabajo fabuloso. Mi mas sincera enhorabuena y agradecimiento. He dedicado mucho tiempo (ensayo/error) a adivinar que procesos disparaba XP, cuando, que servicios, para que servian, quien era el fabricante, que procesos los necesitaban, etc. ¡Una jungla!, a base de msConfig, de cambiar inicio de los servicios, algunos cambios mínimos en el registro.. Desesperante, no compensa tanto tiempo, para que a la vuelta de la esquina te cambien todo. No uso aplicaciones que limpian el registro y similares. Soy muy reació a instalar nada si no sé a cienca cierta lo que es y que lo voy a necesitar. Luego encontré las SysInternals (AutoRuns, ProcessMonitor,.. no encuentro RegMon) ayudan mucho. Pero nunca cambié ni una coma del código, aunque sé programar. Humildemente pido disculpas por no llegar a entenderlo del todo. Solo lo he mirado por encima, le dedicaré mas tiempo. Prometido. Queria escribirte ahora porque sino igual no lo hago. Solo darte las gracias y animarte a seguir con ello. Buen trabajo. Un saludo. Carlos.
Escrito por Carlos (05/03/2012 09:04 AM)
Gracias!
Escrito por Erwin Ried (12/03/2012 12:04 AM)
Wonder does it Work with VMware 8 Workstation ?
Escrito por Bob (25/11/2011 09:42 AM)
Yes, it does work. I am actually using this with version 8
Escrito por Erwin Ried (26/11/2011 04:21 PM)

Copyright © 2013 por Erwin Ried.