Cómo entender el aislamiento de sesiones de Windows — Session 0, RDP y la ejecución simultánea de varios usuarios

· Actualizado el: · · Session 0, Aislamiento de sesiones, RDP, Escritorio remoto, Servicios de Windows, Multiusuario, Windows, .NET, C#, Arquitectura, Consultoría técnica

Historial de revisiones (1 actualizaciones, última el 22 Aug 2026)

Registro de los cambios realizados en este artículo. Cuando se archivó una versión previa, sigue siendo legible mediante un enlace permanente con DOI.

Se ha sustituido la traducción, que estaba abreviada, por una traducción completa del artículo japonés en su versión actual: el texto crece un 10 %. Se han incorporado 9 filas de tabla, 2 bloques de código y 1 diagrama que no estaban en la edición anterior. El contenido no cambia respecto al original japonés; esta edición simplemente ya no lo resume. Además, los enlaces a otros artículos que ya tienen edición en español apuntan ahora a esa edición en lugar de a la japonesa. Leer la versión anterior a esta actualización (DOI: 10.5281/zenodo.21638243)
Primera publicación
Citar este artículo(DOI: 10.5281/zenodo.21638242)

Este artículo está archivado en Zenodo. A continuación se muestran tanto el DOI que siempre resuelve a la última versión como el DOI fijado a la versión que está leyendo.

Go Komura (2026). Cómo entender el aislamiento de sesiones de Windows — Session 0, RDP y la ejecución simultánea de varios usuarios. KomuraSoft LLC. https://doi.org/10.5281/zenodo.21638242 https://comcomponent.com/es/blog/windows-session-rdp-multiuser-guide/

DOI (última versión)
10.5281/zenodo.21638242
DOI (esta versión)
10.5281/zenodo.22053429

«El cuadro de diálogo que muestra el servicio no lo ve nadie», «en cuanto me conecto por RDP, la pantalla local pasa a la pantalla de inicio de sesión», «se suponía que el mutex impedía iniciar varias instancias, pero al iniciar sesión con otro usuario se abrieron dos con toda normalidad». En las consultas sobre aplicaciones de negocio, detrás de estos síntomas suele haber una comprensión insuficiente del concepto de «sesión» de Windows. La sesión es un tema transversal que afecta tanto al diseño de servicios como a la operación del escritorio remoto y a la prevención de instancias múltiples de objetos con nombre, pero rara vez se explica de forma sistemática.

Este artículo ordena, desde una perspectiva práctica, por qué se introdujo el aislamiento Session 0, cómo se comportan las sesiones en una conexión RDP, cómo se aíslan los objetos con nombre entre sesiones, y los errores de diseño habituales en entornos de PC compartido y Servicios de Escritorio remoto (RDS, Remote Desktop Services).

1. Conclusión

La estructura básica se resume en tres puntos.

  • Los servicios se ejecutan en Session 0, una sesión aislada, y no pueden mostrar interfaz de usuario directamente.1
  • RDS está diseñado para admitir varias sesiones simultáneas, mientras que un sistema operativo cliente habitual se basa fundamentalmente en una sesión única.2
  • Los objetos con nombre del kernel tienen un espacio de nombres independiente por sesión, y hay que decidir si se necesita Global\ según la intención.3

A continuación se resumen las conclusiones por punto, junto con el capítulo donde se tratan en detalle.

Punto Conclusión Detalle
Mostrar interfaz desde un servicio Debido al aislamiento Session 0, aunque el servicio muestre un cuadro de diálogo, este no llega al usuario que ha iniciado sesión. El proceso se cuelga esperando un botón que nadie puede pulsar1 Cap. 2 y 3
Su solución Separar la aplicación con interfaz de usuario en un proceso distinto en la sesión del usuario interactivo y comunicarse mediante IPC (comunicación entre procesos). Iniciarla como usuario interactivo con el Programador de tareas es otra opción4 Cap. 3
Número de sesiones simultáneas Windows Server + RDS admite varias sesiones a la vez. Un sistema operativo cliente como Windows 10/11 Pro es, básicamente, de sesión única. La edición «Enterprise multisession», exclusiva de AVD (Azure Virtual Desktop), es la excepción2 Cap. 4
Aislamiento de objetos con nombre Mutex, eventos, semáforos, etc. tienen un espacio de nombres por sesión. Si se desea limitarlos a uno solo en toda la máquina, hay que declarar Global\ explícitamente3 Cap. 6 y 9
Excepción de las canalizaciones con nombre Quedan fuera de este mecanismo. Por defecto usan un espacio de nombres distinto, accesible desde toda la máquina (e incluso de forma remota si el servicio de servidor está activo), así que para diferenciarlas por sesión o usuario hay que incluir, por ejemplo, el ID de sesión en el propio nombre de la canalización5 Cap. 3 y 6
Detectar la propia sesión Se puede determinar con la variable de entorno SESSIONNAME o con GetSystemMetrics(SM_REMOTESESSION), aunque hay casos, como el uso de RemoteFX vGPU, en los que la detección se desvía de lo previsto; no conviene confiar en ella ciegamente67 Cap. 5
Si se necesita compatibilidad con PC compartido/RDS Es algo que debe acordarse con el cliente en una fase temprana del diseño. Los conflictos de rutas de archivos temporales, la confusión de AppData y el acceso simultáneo a dispositivos o dongles son incidentes típicos que estallan en cuanto una aplicación pensada para un PC individual se despliega en RDS Cap. 7 y 8

2. Qué es el aislamiento Session 0 — por qué se separó de la sesión de usuario

Antes de Windows Vista / Windows Server 2008, los servicios y el primer usuario que iniciaba sesión compartían la misma Session 0. Los procesos de una misma sesión pueden intercambiar mensajes de ventana entre sí sin importar sus privilegios. Un proceso con privilegios estándar podía enviar un mensaje manipulado a la ventana de un servicio que se ejecutaba con privilegios SYSTEM, secuestrar su procesamiento y escalar privilegios: esta familia de ataques de escalada de privilegios, que explota fallos en el procesamiento de mensajes de ventana, se denunció repetidamente durante la década de 2000, y de hecho se corrigieron en varias ocasiones vulnerabilidades de escalada de privilegios que explotaban fallos del procesamiento de mensajes de ventana del núcleo de Windows (win32k.sys).8

Desde Windows Vista este esquema cambió. Session 0 quedó reservada en exclusiva a los servicios y a las aplicaciones que no están vinculadas a una sesión de usuario interactiva; el primer usuario que inicia sesión se asigna a la sesión 1, el siguiente a la sesión 2, y así sucesivamente, siempre en una sesión distinta de la de los servicios. Session 0 no admite operación interactiva: ni el servicio puede enviar mensajes a la aplicación, ni la aplicación puede enviarlos al servicio. Tampoco puede el servicio mostrar directamente elementos de interfaz de usuario como cuadros de diálogo. Con este cambio se cerró estructuralmente la vía por la que un proceso con privilegios estándar enviaba mensajes directamente a la ventana de un servicio.1

Es decir, el aislamiento Session 0 no es una simple restricción operativa del tipo «el servicio trabaja entre bastidores y por eso no tiene pantalla», sino un diseño de seguridad que bloquea estructuralmente el intercambio de mensajes de ventana a través de un límite de privilegios. El único recurso que le queda a un servicio para mostrar interfaz es la función WTSSendMessage, una API que hace aparecer un cuadro de mensaje sencillo en otra sesión, pero que no sirve para diálogos complejos ni para una interacción bidireccional.1

3. Los servicios no pueden mostrar interfaz de usuario — restricciones prácticas y patrones de solución

La consecuencia práctica del aislamiento Session 0 es sencilla. Aunque dentro de un servicio se llame a MessageBox.Show o se muestre un formulario, no aparece nada en la pantalla del usuario que ha iniciado sesión. Peor aún, es la causa clásica de que todo el proceso se cuelgue esperando indefinidamente un botón Aceptar que nadie puede pulsar. Al migrar código antiguo (por ejemplo, algo que funcionaba como «servicio interactivo» en la época de Windows XP) a un servicio, compruebe siempre que no queden llamadas que muestren interfaz de usuario.

Existen dos soluciones recomendadas oficialmente.4

  • Separar la aplicación con interfaz de usuario en un proceso distinto (en la sesión del usuario interactivo) y comunicarse mediante IPC. El servicio inicia el procesamiento y pasa los resultados o las solicitudes de entrada al proceso de interfaz. Este muestra al usuario un icono en el área de notificación o un cuadro de diálogo, y devuelve el resultado de la operación al servicio. Si se usa un mecanismo de IPC (Inter-Process Communication, comunicación entre procesos) como las canalizaciones con nombre, es necesario diseñar correctamente la ACL (Access Control List, lista de control de acceso; enumera quién puede realizar qué operación sobre ese objeto) para que no quede expuesta a través de la red. En entornos donde conviven varios usuarios, se recomienda distinguir cada sesión incluyendo, por ejemplo, el ID de sesión en el nombre de la canalización.4 Para elegir el mecanismo de IPC más adecuado, consulte el artículo hermano publicado el mismo día «Tabla de decisión para la comunicación entre procesos». La forma de construir el propio servicio, y aspectos operativos como el tipo de inicio, la cuenta de ejecución o las opciones de recuperación, se resumen en «Cómo crear un servicio de Windows».
  • Iniciarla como usuario interactivo mediante el Programador de tareas. En lugar de mantenerla residente como servicio, se lanza el programa con interfaz con los privilegios y en la sesión del usuario que ha iniciado sesión. Aquí hay que tener cuidado: la propia «tarea registrada como SYSTEM» no debe ser la que muestre la interfaz. La cuenta SYSTEM no tiene derecho de inicio de sesión interactivo, por lo que el usuario no puede ver ni manipular programas o tareas que se ejecutan con privilegios SYSTEM.9 El modelo de tarea elevada (Elevated Task Model) presupone un reparto de funciones: la tarea SYSTEM, con un descriptor de seguridad que permite iniciarla a un usuario estándar, se limita al procesamiento que requiere privilegios de administrador, mientras que la interfaz en sí corre a cargo de otro programa que se ejecuta con los privilegios y en la sesión del usuario que ha iniciado sesión (o del mismo procesamiento registrado como tarea de la cuenta de usuario).10 Haga que la tarea SYSTEM actúe únicamente como un «intermediario sin interfaz visible» y confíe siempre la interfaz visible a un proceso que se ejecute en la sesión del propio usuario.

Sea cual sea el método elegido, lo importante es incorporar desde el principio del diseño la premisa de que «el servicio trabaja entre bastidores y la interfaz va en un proceso aparte». Intentar añadirlo después, cuando el código dependiente de la interfaz ya está impregnado en el servicio, es un desarrollo habitual en la práctica que dispara el coste de separación.

4. Comportamiento de las sesiones en una conexión RDP — diferencias entre RDS y el sistema operativo cliente

La confusión de «es el mismo RDP en el servidor y en el cliente, pero se comporta de forma distinta» se debe a la presencia o ausencia del rol Servicios de Escritorio remoto (RDS).

Windows Server con el rol RDS está pensado para admitir varias sesiones simultáneas: varios usuarios inician sesión a la vez en un mismo servidor y cada uno usa su escritorio o sus aplicaciones en una sesión independiente.11 En RDS, la conexión administrativa RDP habitual (para administradores) permite hasta dos sesiones sin necesidad de CAL, pero superar ese número o permitir el uso simultáneo de usuarios normales exige instalar el rol RD Session Host y disponer de las RDS CAL (licencias de acceso de cliente) adecuadas.12 Existen dos modalidades de CAL, por dispositivo y por usuario, y conviene confirmar los detalles del modelo de licencias según cada entorno.13

En cambio, un sistema operativo cliente habitual, como Windows 10/11 Pro o Enterprise, no tiene el rol RDS y se basa fundamentalmente en una sesión única. Existe una edición especial, «Windows Enterprise multisession», pensada para sistemas cliente que pueden tener varias sesiones interactivas a la vez, pero se ofrece exclusivamente para Azure Virtual Desktop; en la distribución local habitual del sistema operativo cliente no se contemplan sesiones simultáneas de varios usuarios.2 En la práctica, tal como se percibe cuando «al conectarse por RDP al PC de un compañero, la pantalla local pasa a la pantalla de bloqueo», conviene asumir con seguridad que en un sistema operativo cliente una configuración en la que la consola (el uso físico local) y una conexión remota coexisten al mismo tiempo no es, en principio, posible.

A continuación se resumen las diferencias que hay que tener presentes al diseñar una aplicación de negocio.

Aspecto Windows Server + RDS Sistema operativo cliente (Windows 10/11 Pro, etc.)
Sesiones simultáneas Varios usuarios pueden iniciar sesión a la vez11 Básicamente sesión única. Salvo la edición multisession exclusiva de AVD (Azure Virtual Desktop), no admite varias sesiones interactivas2
Licencias Requiere RD Session Host + RDS CAL (excepto las dos conexiones administrativas)12 La función de escritorio remoto viene incluida en el sistema operativo, pero hay que confirmar aparte los requisitos de licencia y las ediciones permitidas
Uso previsto Aplicaciones de negocio en terminales compartidos, entornos de cliente ligero Mantenimiento remoto de un PC personal, acceso en teletrabajo

Si se ignora esta diferencia y se concluye que «como funcionó en el sistema operativo cliente de la máquina de desarrollo, debería funcionar igual en el entorno RDS del servidor compartido», se pasan por alto los fallos propios de la ejecución simultánea de varios usuarios que se describen en el capítulo 7. A la inversa, si solo se prevé un uso de sesión única en un sistema operativo cliente, no hace falta asumir desde el principio el coste de diseño de la compatibilidad con PC compartido. Esta decisión debe tomarse en una fase temprana de la definición de requisitos (capítulo 8).

5. Cómo determinar la sesión actual

Hay muchas situaciones en las que interesa saber en qué sesión se está ejecutando el propio proceso: por ejemplo, para suprimir efectos visuales pesados en un procesamiento en segundo plano, o para desactivar funciones que consumen ancho de banda cuando se trata de una sesión remota.

Antes de escribir código, conviene ejecutar qwinsta en el propio equipo para ver cuántas sesiones hay activas y con qué número. La documentación oficial recoge un ejemplo de salida como el siguiente.14

C:\>qwinsta
    SESSIONNAME     USERNAME        ID STATE    TYPE    DEVICE
    console         Administrator1  0 active    wdcon
    >rdp-tcp#1      User1           1 active    wdtshare
    rdp-tcp                         2 listen    wdtshare
                                    4 idle
                                    5 idle

Así se leen las columnas: SESSIONNAME es el nombre asignado a la sesión, USERNAME es el usuario que ha iniciado sesión en ella, ID es el identificador de sesión, STATE es el estado y TYPE es el tipo de sesión. DEVICE no se muestra en las sesiones de consola ni en las conexiones de red. El > al inicio de la línea indica la sesión en la que se encuentra en ese momento.14

Tenga en cuenta que este ejemplo oficial procede de una versión antigua de Windows, y que en él el ID de sesión de console es 0. Desde Windows Vista, la sesión 0 queda reservada en exclusiva para los servicios, así que en un equipo real la consola física corresponde a partir de la sesión 1.1 Si al ejecutarlo en su propio equipo console aparece como 1, es señal de que el aislamiento Session 0 está funcionando. La línea rdp-tcp en estado listen es el receptor que espera conexiones RDP, no la sesión de un usuario que ha iniciado sesión.

Existen varios medios para hacer esta determinación.

  • La variable de entorno SESSIONNAME: en una sesión de consola toma el valor Console, y en una conexión RDP, un nombre que empieza por RDP-Tcp#. Es la misma información que aparece en la columna SESSIONNAME que muestra el comando qwinsta.14 Sirve para una determinación sencilla, pero al no ser una API oficial es más seguro no depender de ella en exceso.
  • GetSystemMetrics(SM_REMOTESESSION): API de Win32 que determina si se trata de una sesión remota. Devuelve un valor distinto de cero si el proceso que la llama está asociado a una sesión de cliente de Terminal Services. Sin embargo, tiene la limitación de que, desde Windows 8/Server 2012, en una sesión remota que usa RemoteFX vGPU, esta función confunde erróneamente la sesión remota con una sesión local. Para ese caso se recomienda un medio alternativo: comparar el valor GlassSessionId del registro con el ID de la sesión actual.67
  • La propiedad SystemParameters.IsRemoteSession de .NET (WPF): permite obtener desde una aplicación WPF, como una propiedad, la misma determinación que ofrece GetSystemMetrics(SM_REMOTESESSION).15 En aplicaciones WinForms o de consola hay que llamarla directamente mediante P/Invoke.
  • WTSGetActiveConsoleSessionId: API que obtiene el ID de la sesión conectada a la consola física. Comparándolo con el ID de la propia sesión se puede determinar si el proceso se ejecuta en la consola física. Tenga en cuenta que, cuando no hay nadie conectado a la consola física (en transición), devuelve 0xFFFFFFFF.16

No use WTSGetActiveConsoleSessionId para identificar la sesión en un entorno RDS. Tal como indica su nombre, solo devuelve «la sesión conectada a la consola física»; no incluye la sesión de un usuario que ha iniciado sesión por RDP.16 Si el servicio necesita saber «en qué sesión debe mostrar la interfaz auxiliar del usuario interactivo», esta API basta cuando solo se prevén usuarios conectados por consola, pero en un entorno RDS donde varios usuarios de RDP inician sesión a la vez conviene enumerar las sesiones activas con WTSEnumerateSessions17, o usar directamente el ID de sesión que entrega la notificación de cambio de sesión (WM_WTSSESSION_CHANGE). Confundir esto es una causa típica de un fallo difícil de detectar: que la interfaz de notificación no aparezca, o aparezca en la sesión equivocada, únicamente para los usuarios que se conectan por RDP. Tenga siempre presente esta distinción al decidir en qué sesión iniciar el proceso de interfaz dentro de la configuración de IPC del capítulo 3.

6. Aislamiento de sesiones de los objetos con nombre

Del capítulo 2 al 5, la palabra «sesión» ha aparecido muchas veces. Antes de continuar, resumamos en un solo diagrama la relación entre la disposición de las sesiones y los espacios de nombres. Todo lo que sigue se apoya en esta estructura.

Sesión 2 — el siguiente usuario que inicia sesión. En un entorno RDS estas se multiplicanSesión 1 — el primer usuario que inicia sesiónSesión 0 — exclusiva para servicios. El usuario interactivo no entra aquíAplicación del usuario BEspacio de nombres Local — exclusivo de la sesión 2Aplicación del usuario AEspacio de nombres Local — exclusivo de la sesión 1Servicio de Windowsproceso residente que se ejecuta, p. ej., como SYSTEMEspacio de nombres Local — exclusivo de la sesión 0Espacio de nombres Global — uno solo por máquina. Es el mismo para todas las sesiones

Figura 1: cada sesión tiene su propio espacio de nombres independiente, y fuera de ellos existe un único espacio de nombres global para toda la máquina

Al crear un objeto con nombre sin prefijo, este se coloca en el espacio de nombres de esa sesión (el Local del diagrama). Solo cuando se antepone Global\ se coloca en el espacio de nombres común de la máquina que aparece en la parte inferior del diagrama. El síntoma de «al iniciar sesión con otro usuario, el Mutex que impedía instancias múltiples dejó de funcionar» se explica simplemente porque se creó en Local y se usaba con la intención de que fuera Global.

Los objetos con nombre del kernel como Mutex, eventos, semáforos, temporizadores en espera y objetos de asignación de archivos tienen un espacio de nombres independiente por sesión. Aunque se cree un Mutex llamado MyAppMutex en una sesión, si otra sesión intenta abrirlo con el mismo nombre, se crea uno nuevo y distinto. Para procesos que se ejecutan principalmente en la sesión 0, como los servicios, o para configuraciones cliente/servidor que quieren compartir un objeto entre varias sesiones, se puede anteponer Global\ al nombre para colocarlo explícitamente en el espacio de nombres global. A la inversa, anteponer Local\ lo coloca explícitamente en el espacio de nombres de la propia sesión.3

Esto es especialmente relevante en la práctica en el caso del Mutex usado para impedir instancias múltiples.

  • Si el objetivo es únicamente «impedir que el mismo usuario inicie dos veces la aplicación dentro de la misma sesión», basta con un nombre de Mutex por defecto (sin prefijo). El espacio de nombres por sesión actúa automáticamente, de modo que en la sesión de otro usuario se trata como un Mutex distinto.
  • Si el objetivo es «limitar la aplicación a una sola instancia en toda la máquina, aunque varios usuarios hayan iniciado sesión a la vez en un entorno RDS», no se consigue sin declarar Global\ explícitamente. Con el valor por defecto, cada usuario puede llegar a iniciar su propia instancia, lo que produce el fallo de «se suponía que impedía instancias múltiples, pero se iniciaron varias».

Además, un proceso que no está en la sesión 0 necesita el privilegio SeCreateGlobalPrivilege para crear un nuevo objeto de asignación de archivos o un objeto de enlace simbólico en el espacio de nombres global (no hace falta si solo se abre un objeto ya existente). Esta comprobación de privilegios se limita a los objetos de asignación de archivos y de enlace simbólico, y no se aplica a la creación de Mutex ni de eventos, pero conviene tenerla presente en cualquier diseño que use memoria compartida (archivos mapeados en memoria) en el espacio de nombres global.3

Como principio general para las aplicaciones de tipo cliente/servidor, se recomienda además no identificar «una conexión desde una máquina» con «una sesión de usuario». Dado que desde una misma máquina pueden establecerse simultáneamente varias sesiones (conexiones RDP de varios usuarios), se recomienda que el servidor identifique cada sesión, por ejemplo con ProcessIdToSessionId, y prepare un canal de comunicación distinto para cada una.18

7. Errores de diseño habituales en entornos de PC compartido y RDS

Existen varios patrones habituales de fallos que surgen en cuanto una aplicación diseñada pensando únicamente en el uso por consola de un PC individual se despliega en un PC compartido o en un entorno RDS.

  • Conflictos de ruta de archivos temporales. Por defecto, RDS crea una carpeta temporal independiente por sesión (bajo el perfil del usuario, con el ID de sesión incluido en el nombre).19 Es decir, si la aplicación usa sin más la variable de entorno %TEMP%, se beneficia directamente de este aislamiento. El problema surge cuando, en lugar de %TEMP%, la aplicación fija de antemano una ruta constante como C:\Work\temp. Si varias sesiones de la misma cuenta de usuario, o varios usuarios, escriben a la vez en esa misma ruta fija, es lógico que se disputen los archivos.
  • Confusión de AppData. Dónde guardar la configuración del usuario, la caché y los registros debe decidirse conociendo el mecanismo del perfil de usuario de Windows (local/itinerante, y el uso diferenciado de %LOCALAPPDATA% y %APPDATA%). En un PC compartido, un diseño descuidado en este punto se manifiesta fácilmente como «mi pantalla cambia por la configuración de otra persona» o «se sobrescriben los registros». Para más detalles, consulte «Introducción al perfil de usuario de Windows».
  • Suponer acceso simultáneo a dispositivos, dongles o puertos serie. Para acceder desde una sesión RDS a un dispositivo local o a un puerto serie del lado cliente hace falta pasar por el mecanismo de redirección. Si se pretende que varias sesiones usen a la vez una aplicación que depende de un dongle USB físico o de un dispositivo de comunicación serie, es habitual encontrarse con que el propio hardware no admite acceso simultáneo, o con que se ve o no se ve según cómo esté configurada la redirección. Cómo aparece ante la sesión del servidor un dispositivo conectado en el lado cliente depende de la configuración de la redirección, por lo que es imprescindible confirmarlo en la fase de diseño.20
  • Fijar de antemano la impresora o la unidad. Si se codifica de forma fija en la aplicación el nombre de la impresora predeterminada o la letra de unidad, esto falla con facilidad en un entorno de PC compartido, donde la impresora predeterminada redirigida o la unidad asignada varían según el usuario. Es más seguro obtener el nombre de la impresora en tiempo de ejecución y tratar las unidades, en la medida de lo posible, mediante rutas UNC.
  • Malinterpretar HKCU y el registro de usuario. También hay que tener en cuenta que HKEY_CURRENT_USER está vinculado al usuario que ha iniciado sesión, no a la sesión. En una configuración donde el mismo usuario tiene varias sesiones (por ejemplo, cuando se comparte la misma cuenta en RDS), el valor de HKCU se comparte entre todas las sesiones, así que si se coloca allí un «estado de ejecución que debería mantenerse independiente por sesión», se afecta sin querer a las demás sesiones. La información que necesita aislarse por sesión debe tratarse con el espacio de nombres de sesión del capítulo 6, o con rutas de archivo que incorporen el ID de sesión.

Todos estos son fallos de la clase que «no se nota en un PC individual» y que «solo se descubre cuando varios usuarios lo usan a la vez», así que si el entorno de pruebas se limita a una máquina de desarrollo individual, lo habitual es que el problema solo se reproduzca por primera vez en el entorno de producción de PC compartido o RDS.

8. Tabla de decisión — si incluir la compatibilidad con PC compartido/RDS

Si se debe incluir como requisito el funcionamiento en un entorno de PC compartido o RDS es algo que conviene confirmar en la fase de definición de requisitos, antes de que avance la implementación. Si se decide incluirlo, use los siguientes puntos como lista de verificación.

Aspecto Basta con suponer un PC individual/consola Se necesita compatibilidad con PC compartido/RDS (varios usuarios a la vez)
Archivos temporales/configuración Una ruta fija apenas causa problemas reales Es obligatorio usar rutas por usuario/por sesión, como %TEMP% o %LOCALAPPDATA% (cap. 7)
Objetos con nombre Basta con el valor por defecto (espacio de nombres de sesión) Si es «uno solo en toda la máquina», declare Global\ explícitamente. Si basta con «uno por sesión», deje el valor por defecto. Si quiere «uno por usuario, abarcando sus varias sesiones», combine Global\ con el SID del usuario (cap. 6; véase también el artículo sobre la prevención de instancias múltiples)
Dispositivos/dongles Conexión local directa, sencilla Diseñe partiendo de que el acceso será redirigido y de que puede no admitirse el acceso simultáneo
Interfaz y servicio Suele resolverse en el mismo escritorio Tenga en cuenta el aislamiento Session 0 y organícelo con un proceso aparte más IPC (cap. 3)
Licencias/facturación Se puede contar de forma simple por número de equipos Confirme en la definición de requisitos el criterio de sesiones simultáneas y si se necesitan RDS CAL13

El criterio de decisión es sencillo: acordar con el cliente, en una fase temprana del desarrollo, si «esta aplicación debe funcionar de forma autosuficiente en el PC de una sola persona, o si existe la posibilidad de que varias personas la usen a la vez en un terminal compartido o en un entorno RDS». Si se avanza en la implementación dejando esto sin aclarar, se acaba teniendo que rehacer todos los puntos anteriores.

9. Ejemplo de implementación — cómo combinar la detección de sesión con el uso de cada espacio de nombres

A continuación se recogen en código .NET los métodos de detección presentados en los capítulos 5 y 6, junto con el uso diferenciado de la nomenclatura de Mutex.

using System.Runtime.InteropServices;

internal static class SessionInfo
{
    [DllImport("user32.dll")]
    private static extern int GetSystemMetrics(int nIndex);

    private const int SM_REMOTESESSION = 0x1000;

    // En una app WPF, System.Windows.SystemParameters.IsRemoteSession devuelve la misma determinación
    public static bool IsRemoteSession() => GetSystemMetrics(SM_REMOTESESSION) != 0;

    // Determinación simplificada. Al no ser una API oficial, resérvela para registros/diagnóstico y no la use en ramas importantes
    public static string? SessionName() =>
        Environment.GetEnvironmentVariable("SESSIONNAME");
}

El Mutex que impide instancias múltiples distingue tres espacios de nombres según el objetivo: «uno solo en toda la máquina», «uno por sesión» y «uno por usuario, abarcando sus varias sesiones». El tercer caso (por usuario, a través de las sesiones) no se consigue solo con el Global\ por defecto; hace falta incluir el SID del usuario en el nombre, algo que se trata en detalle en «Prevención de instancias múltiples en una aplicación de Windows».

Al implementar «uno solo en toda la máquina» con Global\, es necesario configurar explícitamente la ACL. El motivo es que Mutex/MutexAcl.Create de .NET, incluso al limitarse a abrir un Mutex ya existente, solicita internamente, además de SYNCHRONIZE y MUTEX_MODIFY_STATE, también DELETE, READ_CONTROL, WRITE_DAC y WRITE_OWNER. Si se deja la ACL por defecto, cualquier usuario legítimo que no sea quien creó el Mutex por primera vez recibirá una UnauthorizedAccessException justo al obtener createdNew. Para evitarlo, hay que conceder al grupo «Usuarios autenticados» un permiso equivalente a FullControl.

Esta configuración tiene dos costes. El primero es que cualquier usuario autenticado queda en condiciones de reescribir después la ACL. Si esto tampoco resulta aceptable, hay que dejar de usar la clase Mutex y llamar directamente a CreateMutexEx mediante P/Invoke, solicitando solo los permisos de acceso mínimos necesarios. El segundo es que esta ACL solo se aplica si es uno mismo quien logra crear el Mutex en primer lugar. Un Mutex Global\ de nombre fijo corre el riesgo de que otro usuario se adelante a crearlo (ocupación del nombre, o «name squatting»); en ese caso, createdGlobal será false, o bien se producirá una UnauthorizedAccessException según la ACL del otro usuario. Si de verdad se quiere impedir esto, combínelo con un nombre difícil de adivinar u otro mecanismo de exclusión mutua (véase el capítulo 5 de «Prevención de instancias múltiples en una aplicación de Windows» para más detalles).

// Requiere using System.Security.AccessControl; y using System.Security.Principal;
// (también requiere el paquete NuGet System.Threading.AccessControl)

// Intención: aunque varios usuarios inicien sesión a la vez en RDS/un PC compartido, hay una sola instancia en toda la máquina
var globalMutexSecurity = new MutexSecurity();
globalMutexSecurity.AddAccessRule(new MutexAccessRule(
    new SecurityIdentifier(WellKnownSidType.AuthenticatedUserSid, domainSid: null),
    MutexRights.FullControl,
    AccessControlType.Allow));

using var globalMutex = MutexAcl.Create(
    initiallyOwned: false,
    name: @"Global\KomuraSoft.MyApp.SingleInstance",
    createdNew: out bool createdGlobal,
    mutexSecurity: globalMutexSecurity);

// Intención: uno por sesión. Basta con el espacio de nombres de sesión por defecto
using var perSessionMutex = new Mutex(
    initiallyOwned: false,
    name: "KomuraSoft.MyApp.SingleInstance",
    createdNew: out bool createdPerSession);

if (!createdGlobal)
{
    // Ya se ha iniciado en algún lugar de la máquina, incluida otra sesión
    return;
}

Crear un Mutex con Global\ no requiere ningún privilegio especial en sí mismo (como se ha explicado antes, la comprobación de privilegios solo se aplica a la creación de nuevos objetos de asignación de archivos y de enlace simbólico).3 Es un error usar el espacio de nombres de sesión por defecto con la intención de conseguir «uno por usuario». No es raro que el mismo usuario tenga a la vez una sesión RDP desconectada y una nueva sesión de consola o RDP; en ese caso, cada sesión se trata como un Mutex distinto, así que con el valor por defecto el mismo usuario puede llegar a iniciar una segunda instancia. Cuál de las tres opciones («uno en toda la máquina», «uno por sesión» o «uno por usuario, a través de las sesiones») es la correcta según los requisitos depende de la naturaleza de la aplicación, así que confirme los requisitos antes de decidir la nomenclatura.

10. Resumen

La «sesión» de Windows es un concepto que aparece de forma transversal en temas aparentemente dispares: el diseño de servicios, la operación del escritorio remoto y la prevención de instancias múltiples de objetos con nombre. La estructura básica que hay que retener se resume en tres puntos. Los servicios se ejecutan en Session 0, una sesión especial y aislada, y no pueden mostrar interfaz de usuario directamente. RDS está diseñado para admitir varias sesiones simultáneas, mientras que un sistema operativo cliente habitual se basa fundamentalmente en una sesión única. Y los objetos con nombre tienen un espacio de nombres por sesión, por lo que hay que decidir si se necesita Global\ según la intención.

Si se debe incluir como requisito el funcionamiento en un entorno de PC compartido o RDS es un tema en el que, si se descubre a mitad de la implementación, el retrabajo es considerable. Recomendamos encarecidamente confirmar en la fase de definición de requisitos «quién va a usar esta aplicación, en qué terminal y cuántas personas a la vez». Si tiene previsto desplegar una aplicación existente en un entorno de PC compartido o RDS, o si necesita investigar la causa de un fallo del tipo «se comporta de forma extraña cuando la usan varios usuarios», suele ser más rápido diagnosticarlo observando la configuración real, así que no dude en consultarnos.

Artículos relacionados

Áreas de consultoría relacionadas

KomuraSoft LLC se ocupa de revisiones de diseño de aplicaciones de Windows pensando en su despliegue en entornos de PC compartido o RDS, del diseño de la conversión en servicio y de la separación mediante IPC, y de la investigación de fallos propios del uso simultáneo por varios usuarios.

Referencias

  1. Microsoft Learn, Service Changes for Windows Vista. Sobre los antecedentes de la introducción del aislamiento Session 0, la imposibilidad de enviar y recibir mensajes de ventana entre servicios y aplicaciones, y el medio alternativo que ofrece WTSSendMessage 2 3 4 5

  2. Microsoft Learn, Windows Enterprise multi-session FAQ. Sobre el hecho de que la función multisesión, que permite tener varias sesiones interactivas a la vez y que antes solo era posible con Windows Server, se ofrece en exclusiva para Azure Virtual Desktop.  2 3 4

  3. Microsoft Learn, Kernel object namespaces. Sobre el espacio de nombres por sesión de los objetos con nombre del kernel, los prefijos Global\/Local\, y la necesidad de SeCreateGlobalPrivilege para crear nuevos objetos de asignación de archivos o de enlace simbólico en el espacio de nombres global.  2 3 4 5

  4. Microsoft Learn, Interactive Services. Sobre la imposibilidad de que un servicio interactúe directamente con el usuario, el diseño de iniciar con CreateProcessAsUser una aplicación GUI en un proceso aparte y coordinarla mediante IPC, y la recomendación de diferenciar el nombre de la canalización con el ID de sesión.  2 3

  5. Microsoft Learn, Named Pipes. Sobre el hecho de que las canalizaciones con nombre son accesibles desde cualquier proceso dentro de los límites de la comprobación de seguridad, y que son alcanzables de forma remota si el servicio de servidor está activo (esto sirve de base para explicar por qué es un mecanismo distinto del espacio de nombres de sesión Global\/Local\ de los Mutex, etc.). 

  6. Microsoft Learn, GetSystemMetrics function (winuser.h). Sobre la especificación de la determinación de sesión remota mediante SM_REMOTESESSION 2

  7. Microsoft Learn, Detecting the Remote Desktop Services environment. Sobre un código de ejemplo de GetSystemMetrics(SM_REMOTESESSION), la limitación por la que confunde una sesión remota con una local al usar RemoteFX vGPU, y la determinación alternativa mediante la clave de registro GlassSessionId 2

  8. Microsoft Security Bulletin, MS12-034 - Windows and Messages Vulnerability (CVE-2012-0180). Sobre una vulnerabilidad de escalada de privilegios que explota un fallo en el procesamiento de mensajes de ventana del controlador en modo kernel de Windows (win32k.sys) (un ejemplo de esta clase de ataque de escalada de privilegios a través de mensajes de ventana). 

  9. Microsoft Learn, schtasks create. Sobre el hecho de que la cuenta SYSTEM no tiene derecho de inicio de sesión interactivo, y de que el usuario no puede ver ni manipular programas o tareas que se ejecutan con privilegios SYSTEM. 

  10. Microsoft Learn, Elevated Task Model. Sobre la configuración en la que una aplicación de usuario estándar ejecuta el procesamiento que requiere privilegios de administrador a través de una tarea registrada como SYSTEM cuyo descriptor de seguridad permite que la inicie un usuario estándar. 

  11. Microsoft Learn, Remote Desktop Services overview in Windows Server. Sobre el hecho de que RDS es la base que ofrece escritorios basados en sesión de tipo multisesión.  2

  12. Microsoft Learn, Troubleshoot Remote desktop disconnected errors. Sobre el hecho de que, para uso administrativo, se permiten hasta dos conexiones remotas simultáneas sin RDS CAL, y de que superar ese número exige el rol RD Session Host y las RDS CAL adecuadas.  2

  13. Microsoft Learn, License Remote Desktop Services with Client Access Licenses (CALs). Sobre la diferencia entre los modelos de RDS CAL por dispositivo y por usuario, y el mecanismo de emisión y seguimiento a través del servidor de licencias.  2

  14. Microsoft Learn, qwinsta. Sobre el hecho de que la columna SESSIONNAME muestra el nombre asignado a la sesión (console para la consola, un nombre que empieza por rdp-tcp# para una conexión RDP), el ejemplo de salida citado en el cuerpo del artículo y el significado de cada columna (SESSIONNAME, USERNAME, STATE, TYPE, DEVICE), y el hecho de que la sesión actual se marca con > al inicio de la línea.  2 3

  15. Microsoft Learn, SystemParameters.IsRemoteSession Property. Sobre la propiedad que permite obtener desde WPF una determinación equivalente a SM_REMOTESESSION

  16. Microsoft Learn, WTSGetActiveConsoleSessionId function (winbase.h). Sobre la obtención del ID de la sesión conectada a la consola física, y el hecho de que devuelve 0xFFFFFFFF durante la transición.  2

  17. Microsoft Learn, WTSEnumerateSessions function (wtsapi32.h). Sobre la posibilidad de enumerar todas las sesiones de un servidor RD Session Host. 

  18. Microsoft Learn, Client/Server application guidelines. Sobre la recomendación de no identificar «una conexión desde una máquina» con «una sesión de usuario», y sobre la identificación de sesiones mediante ProcessIdToSessionId

  19. Microsoft Learn, Policy CSP - ADMX_TerminalServer (TS_TEMP_PER_SESSION). Sobre el hecho de que Remote Desktop Services crea por defecto una carpeta temporal independiente por sesión (bajo el perfil de usuario, con el ID de sesión incluido en el nombre). 

  20. Microsoft Learn, Peripheral hardware guidelines. Sobre el hecho de que la forma en que se ven las unidades e impresoras del lado cliente desde un servidor RD Session Host depende de la configuración de la redirección, y sobre el mecanismo para desactivar la redirección en el propio controlador del dispositivo. 

Artículos recientes con las mismas etiquetas para profundizar en temas cercanos.

Estas páginas sitúan el tema en un contexto más amplio de servicios y decisiones.

El artículo está directamente relacionado con los siguientes servicios.

Preguntas frecuentes

Preguntas habituales en las consultas sobre el tema del artículo.

¿Por qué un servicio de Windows no puede mostrar un cuadro de diálogo?
La razón es el aislamiento Session 0, vigente desde Windows Vista. Los servicios se ejecutan en una sesión dedicada, Session 0, aislada de la sesión interactiva del usuario que ha iniciado sesión. Se trata de un diseño de seguridad que bloquea estructuralmente los ataques de escalada de privilegios en los que un proceso con privilegios estándar envía mensajes de ventana manipulados a la ventana de un servicio que se ejecuta con privilegios SYSTEM. Si el servicio muestra un cuadro de diálogo, el usuario no lo ve, y el proceso puede quedar colgado esperando indefinidamente que alguien pulse un botón Aceptar que nadie puede pulsar. La solución consiste en separar la aplicación con interfaz de usuario en un proceso distinto que se ejecuta en la sesión del usuario y comunicarse con ella mediante IPC.
¿Por qué, al conectarse por RDP, la pantalla local pasa a la pantalla de bloqueo?
Porque los sistemas operativos cliente habituales, como Windows 10/11 Pro, se basan fundamentalmente en una sesión única: una configuración en la que la consola (el uso físico local) y una conexión remota coexisten al mismo tiempo no es, en principio, posible. Que varios usuarios inicien sesión simultáneamente y usen sesiones independientes solo es posible con Windows Server y el rol de Servicios de Escritorio remoto (RDS); superar las dos conexiones administrativas requiere el rol RD Session Host y licencias RDS CAL. La excepción es la edición «Enterprise multisession», reservada exclusivamente a Azure Virtual Desktop.
¿Por qué en un entorno RDS el mutex que impide iniciar varias instancias no funciona?
Porque los objetos con nombre del kernel (Mutex, eventos, semáforos, etc.) tienen un espacio de nombres independiente por cada sesión. Con un nombre de Mutex sin prefijo, la sesión de otro usuario lo trata como un Mutex distinto, de modo que cada usuario puede llegar a iniciar su propia instancia. Si se quiere limitar la instancia a una sola en toda la máquina, hay que anteponer explícitamente el prefijo Global\. Si se quiere «una sola instancia por usuario, abarcando todas sus sesiones», hay que combinar Global\ con el SID del usuario en el nombre.
¿Cuáles son las causas típicas de que una aplicación de negocio falle en un PC compartido o en un entorno RDS?
Hay cinco patrones habituales: conflictos de archivos temporales por usar una ruta fija en lugar de %TEMP%; una gestión poco cuidadosa del perfil de usuario (AppData); dar por sentado el acceso simultáneo a dispositivos como dongles USB o puertos serie; codificar de forma fija el nombre de la impresora o la letra de unidad; y guardar en HKCU un estado que debería mantenerse independiente por sesión. Ninguno de estos problemas se manifiesta en un PC individual: solo aparecen cuando varios usuarios lo usan al mismo tiempo, por lo que conviene confirmar si se necesita compatibilidad con PC compartido o RDS en una fase temprana de la definición de requisitos.

Perfil del autor

Página de presentación del autor del artículo.

Go Komura

Representante de KomuraSoft LLC

Especializado en desarrollo de software para Windows, consultoría técnica e investigación de fallos, sobre todo en proyectos con sistemas existentes y errores difíciles de reproducir.

Volver al blog