How to Think About Windows Session Isolation — Session 0, RDP, and Running Multiple Users Concurrently
· Go Komura · Session 0, Session Isolation, RDP, Remote Desktop, Windows Services, Multi-User, Windows, .NET, C#, Architecture, Technical Consulting
“A dialog raised from a service is invisible to everyone.” “The moment someone connected over RDP, the screen at the physical machine flipped to the login screen.” “Single-instance protection was supposed to stop double launches, but logging in as a different user happily started a second copy.” Business-app consulting engagements keep turning up these symptoms, and behind all of them sits the same gap: an incomplete understanding of the concept of a Windows “session.” Sessions cut across service design, remote desktop operations, and single-instance protection for named objects alike, yet it’s rare to see the topic explained systematically in one place.
This article organizes, from a practical standpoint, why Session 0 isolation was introduced, how sessions behave when connecting over RDP, how named objects are isolated per session, and the design mistakes that commonly show up in shared-PC and RDS (Remote Desktop Services) environments.
1. The short version
- Services run in a dedicated session called Session 0, isolated from the interactive session of any logged-on user. A service cannot show a dialog directly, and even if it tries, the user never sees it.1
- The officially recommended solution for a service that needs UI is to split the UI into a separate process (living in the user’s session) and talk to it over IPC. Launching a UI program as the interactive user via Task Scheduler is another option on the table.2
- Windows Server (RDS) is built around multiple concurrent sessions, but client OSes such as Windows 10/11 Pro are fundamentally single-session. Aside from the special “Enterprise multi-session” edition built for Azure Virtual Desktop, ordinary client OS installs are not designed to host multiple concurrent interactive sessions.3
- Named kernel objects (mutexes, events, semaphores, etc.) each have a per-session namespace. If you want to collapse an object down to a single instance across the whole machine and across sessions, you must explicitly prefix the name with
Global\— otherwise single-instance protection won’t behave the way you expect. Named pipes are outside this mechanism: by default they use a separate namespace reachable machine-wide (and remotely, if the server service is running), so if you need to distinguish by session or user, you have to embed something like the session ID into the pipe name itself.45 - You can determine the current session’s type via the
SESSIONNAMEenvironment variable orGetSystemMetrics(SM_REMOTESESSION). However, there are cases — such as when RemoteFX vGPU is in use — where the detection deviates from its intended behavior, so don’t over-rely on it.67 - Whether shared-PC/RDS behavior belongs in your requirements is something to confirm at the very start of the design phase. Temp-file path collisions, mixed-up AppData, and simultaneous access to devices/dongles are the classic accidents that erupt the moment an app built for a single standalone PC gets deployed onto RDS (see Chapter 7).
2. What Session 0 isolation is — and why the split from user sessions happened
Before Windows Vista / Windows Server 2008, a service and the first user to log on shared the same Session 0. Processes within the same session can exchange window messages regardless of their respective privilege levels. A standard-privilege process could send a crafted message to a window owned by a service running as SYSTEM, hijack the service’s message handling, and escalate its privileges — this whole class of privilege escalation, which exploits flaws in window message handling, was reported repeatedly through the 2000s, and in fact multiple privilege-escalation vulnerabilities exploiting flaws in the Windows kernel’s window message handling (win32k.sys) have been fixed over the years.8
This picture changed starting with Windows Vista. Session 0 became dedicated exclusively to services and applications not tied to an interactive user session, and the first user to log on is now assigned Session 1, the next Session 2, and so on — each in a different session from the service. Session 0 does not support interactive operation, and services can neither send messages to apps, nor can apps send messages to services. A service also cannot directly display UI elements such as a dialog box. With this change, the very path that let a standard-privilege process send messages directly into a service’s window was structurally closed off.1
In other words, Session 0 isolation isn’t merely an operational constraint of “services are background processes, so they don’t have a screen” — it’s a security design that structurally blocks window-message exchange across a privilege boundary. The one avenue left for a service to show UI at all is the WTSSendMessage function, an API that pops up a simple message box in another session, but it can’t be used for complex dialogs or two-way interaction.1
3. Services can’t show UI — the practical constraints and workaround patterns
The practical consequence of Session 0 isolation is simple. If a service calls MessageBox.Show or tries to display a form, nothing appears on the logged-on user’s screen. Worse, it’s a classic cause of the whole process hanging while it waits forever for an OK button that no one can ever click. When porting old code (something that once ran as an “interactive service” back in the Windows XP era) into a service, always check whether any UI-display calls have been left in.
There are two officially documented solutions.2
- Split the UI into a separate process (living in the interactive user’s session) and communicate over IPC. The service does the work and passes results or input requests to the UI process. The UI process presents them to the user as a notification-area icon or a dialog, and returns the operation’s result to the service. If you use IPC such as named pipes, you need to design the ACLs so nothing is unintentionally exposed over the network. In environments where multiple users are logged on side by side, the guidance is to distinguish sessions by, for example, embedding the session ID in the pipe name.2 For guidance on choosing an IPC mechanism, see the companion article published the same day, “A Decision Table for Windows Inter-Process Communication.” How to build the service itself, along with operational design points such as start type, run-as account, and recovery options, is covered in “How to Build a Windows Service.”
- Launch a program as the interactive user via Task Scheduler. Instead of keeping the program resident as a service, this configuration launches a program with UI under the logged-on user’s own privileges and session. One point to be careful of here: a “task registered under SYSTEM” must never be the one displaying UI. The SYSTEM account has no interactive logon rights, and a user cannot see or interact with a program or task running under SYSTEM privileges.9 The elevated-task model is meant to have a SYSTEM task — one whose security descriptor lets a standard user start it — take on only the operations that require administrator privileges, while the UI itself runs in a separate program under the logged-on user’s own privileges and session (or as the same operation registered as a task under the user’s own account); that division of labor is the whole premise.10 Keep the SYSTEM task strictly as a “headless broker” and always let the visible UI run in a process under the user’s own session.
Whichever approach you take, the key is to build “the service stays behind the scenes, UI lives in a separate process” into the design from day one. What we routinely see in practice is that trying to bolt this on later runs into UI-dependent code that’s already deeply baked into the service, driving up the cost of separating them.
4. How sessions behave over RDP — the difference between RDS and a client OS
The confusion of “it’s the same RDP on both the server and the client, so why does it behave differently?” stems from whether the Remote Desktop Services (RDS) role is present.
Windows Server + the RDS role is designed around multiple users logging on to a single server simultaneously, each working in an independent session with their own desktop and apps — a configuration built on multiple concurrent sessions from the ground up.11 Under RDS, ordinary RDP admin connections (for administrators) support up to two sessions without needing a CAL, but going beyond that number, or letting regular users connect concurrently, requires deploying the RD Session Host role along with proper RDS CALs (Client Access Licenses).12 CALs come in two models — per device and per user — and the licensing details need to be confirmed for each environment.13
Meanwhile, an ordinary client OS such as Windows 10/11 Pro or Enterprise does not carry the RDS role and is, in principle, single-session. There is a special edition of client OS called “Windows Enterprise multi-session” that can hold multiple interactive sessions at once, but it’s offered exclusively for Azure Virtual Desktop — ordinary, on-premises-distributed client OS installs are not designed to support multiple concurrent user sessions.3 In practice, matching the familiar experience of “I RDP’d into a colleague’s PC and the screen at their desk flipped to the lock screen,” it’s safest to assume that on client OSes, a physical console session (someone operating the machine locally) and a remote connection basically cannot coexist at the same time.
Here’s a summary of the differences that matter for business-app design.
| Aspect | Windows Server + RDS | Client OS (Windows 10/11 Pro, etc.) |
|---|---|---|
| Concurrent sessions | Multiple users can log on simultaneously11 | Fundamentally single-session. Multiple interactive sessions aren’t supported outside the AVD-only multi-session edition3 |
| Licensing | Requires RD Session Host + RDS CALs (beyond the two admin connections)12 | The Remote Desktop feature itself ships with the OS, but licensing requirements and permitted editions need separate confirmation |
| Typical use | Running business apps on shared terminals, thin-client environments | Remote maintenance of a personal PC, work-from-home access |
Failing to account for this difference — assuming “it worked on the client OS on my dev machine, so it should work the same way on the shared server’s RDS environment” — will cause you to overlook the multi-user-concurrency-specific bugs covered in Chapter 7. Conversely, if you only ever expect single-session operation on a client OS, there’s no need to pay the design cost of shared-PC support up front. This is a judgment call that should be made early in requirements definition (Chapter 8).
5. Determining the current session
There are plenty of situations where you want to know which session your process is currently running in — suppressing a heavy rendering effect during background processing, or disabling a bandwidth-hungry feature over a remote session, for example. There are several ways to determine this.
- The
SESSIONNAMEenvironment variable: set toConsolefor the console session, or to a name starting withRDP-Tcp#for an RDP connection. This is the same information shown in theSESSIONNAMEcolumn listed by theqwinstacommand.14 It’s usable for a quick check, but since it isn’t an official API, it’s safer not to rely on it too heavily. GetSystemMetrics(SM_REMOTESESSION): a Win32 API that tells you whether you’re in a remote session. It returns non-zero if the calling process is associated with a Terminal Services client session. However, there’s a caveat: starting with Windows 8/Server 2012, on remote sessions using RemoteFX vGPU, this function mistakenly reports a remote session as local. In that case, the documented workaround is to compare the registry’sGlassSessionIdagainst the current session ID.67- .NET (WPF)’s
SystemParameters.IsRemoteSession: exposes the same check thatGetSystemMetrics(SM_REMOTESESSION)performs, as a property you can read from a WPF app.15 From WinForms or a console app, you’d call it directly via P/Invoke. WTSGetActiveConsoleSessionId: an API that gets the session ID connected to the physical console. Comparing it against your own session ID lets you determine “am I running on the physical console?” Note that when no one is connected to the physical console (i.e., during a transition), it returns0xFFFFFFFF.16
Do not use WTSGetActiveConsoleSessionId to identify sessions in an RDS environment. True to its name, all it returns is “the session connected to the physical console” — a user logged on via RDP is not covered.16 If a service needs to know which session to show its companion UI in, this API is sufficient when you only ever expect a console-connected user, but in an RDS environment where multiple RDP users may be logged on simultaneously, you should instead enumerate the running sessions with WTSEnumerateSessions17 or simply use the session ID handed to you by a session-change notification (WM_WTSSESSION_CHANGE). This mix-up is a classic, hard-to-spot cause of bugs where the notification UI simply doesn’t appear — or appears in the wrong session — specifically for users connected over RDP. Keep this distinction firmly in mind when deciding which session to launch the UI process into for the IPC setup described in Chapter 3.
6. Per-session isolation of named objects
Named kernel objects — mutexes, events, semaphores, waitable timers, file mapping objects — each have an independent namespace per session. Even if you create a mutex named MyAppMutex in one session, trying to open an object of the same name from a different session creates a brand-new, distinct object. For processes that primarily run in Session 0, such as services, or for client/server setups that want to share an object across multiple sessions, you can explicitly place it in the global namespace by prefixing the name with Global\. Conversely, prefixing with Local\ explicitly places it in your own session’s namespace.4
This is where it really matters in practice: the mutex used for single-instance protection.
- If all you want is “prevent the same user from launching two copies of the app within the same session,” the default (unprefixed) mutex name is sufficient. The per-session namespace kicks in automatically, so a different user’s session is treated as a different mutex.
- If you want “even with multiple users logged on simultaneously in an RDS environment, limit the app to a single instance machine-wide,” you cannot achieve that goal without explicitly using
Global\. Left at the default, each user ends up able to launch their own copy — a bug that reads as “single-instance protection was supposed to work, but multiple instances launched anyway.”
Note also that a process outside Session 0 needs the SeCreateGlobalPrivilege privilege to newly create a file mapping object or symbolic link object in the global namespace (merely opening an existing object does not require it). This privilege check applies only to file mappings and symbolic links — it isn’t imposed on creating mutexes or events — but it’s worth remembering for any design that uses shared memory (a memory-mapped file) in the global namespace.4
There’s also a general principle for client/server applications: the guidance is not to equate “a connection from one machine” with “one user session.” Given that multiple sessions (multiple users’ RDP connections) from the same machine can be established simultaneously, the server side is recommended to identify sessions using something like ProcessIdToSessionId, and to provision a separate communication channel per session.18
7. Common design mistakes in shared-PC and RDS environments
There are a handful of recurring failure patterns that erupt the moment an app built with only single-PC, console-only use in mind gets deployed onto a shared PC or an RDS environment.
- Temp-file path collisions. By default, RDS creates a separate temp folder per session (under the user’s profile, with the session ID baked into the name).19 In other words, if your app straightforwardly uses the
%TEMP%environment variable, it gets the benefit of this isolation for free. The trouble starts when the app hard-codes a fixed path such asC:\Work\tempinstead of using%TEMP%. If multiple sessions of the same user account, or multiple users, write to the same fixed path simultaneously, of course they’ll fight over the file. - Mixed-up AppData. Where you save user settings, caches, and logs needs to be decided with a solid understanding of Windows’s user profile mechanism — local vs. roaming, and when to use
%LOCALAPPDATA%versus%APPDATA%. On a shared PC, sloppy design here tends to surface as “my screen changed because of someone else’s settings” or “the log got overwritten.” See “An Introduction to Windows User Profiles” for details. - Assuming simultaneous access to devices, dongles, and serial ports. Accessing a client-side local device or serial port from an RDS session goes through a redirection mechanism. If you try to let multiple sessions use an app that depends on a physical USB dongle or a serial-communication device concurrently, you run into problems where the hardware itself may not support concurrent access in the first place, or where visibility depends on the redirection configuration and may or may not be present. How a device attached on the client side appears from a session on the server side depends on the redirection configuration, so this must be confirmed at the design stage.20
- Hard-coded printers and drives. Hard-coding a default printer name or drive letter inside an app easily breaks down in a shared-PC environment where the redirected default printer or mapped drive differs per user. It’s safer to look up the printer name at runtime and, wherever possible, handle drives as UNC paths.
- Misreading HKCU / per-user registry. It’s also worth noting that
HKEY_CURRENT_USERis tied to the logged-on user, not the session. In a configuration where the same user has multiple sessions (for example, sharing the same account across RDS), HKCU values are shared across all of that user’s sessions — so storing runtime state that you actually want isolated per session in HKCU will unintentionally leak into other sessions. Information that needs per-session isolation should instead use the per-session namespaces from Chapter 6, or a file path that incorporates the session ID.
All of these are the kind of bug that “you’d never notice on a single standalone PC” and that “only surfaces once multiple users actually use the app at the same time” — so the typical arc is that a test environment consisting only of a single standalone dev machine never reproduces it, and it only shows up for the first time on a production shared PC or RDS environment.
8. Decision table — should “shared-PC/RDS support” be part of your requirements?
Whether shared-PC/RDS behavior belongs in your requirements is something to confirm during requirements definition, before implementation gets underway. Once you’ve decided to include it, use the following points as a checklist.
| Aspect | Fine to assume a standalone PC / console only | Needs shared-PC/RDS (multi-user concurrent) support |
|---|---|---|
| Temp files / settings | A fixed path causes little real harm | Mandate per-user/per-session paths such as %TEMP% / %LOCALAPPDATA% (Chapter 7) |
| Named objects | Fine to leave at the default (session namespace) | If you need “one instance machine-wide,” explicitly use Global\. If “one per session” is acceptable, leave the default as-is. If you want “one per user, spanning that user’s sessions,” combine Global\ with the user’s SID (Chapter 6; see also the article on single-instance protection) |
| Devices / dongles | Simple, direct local connection | Design around the fact that access goes through redirection and concurrent access may not be possible |
| UI and services | Easy to keep everything on one desktop | Split into a separate process + IPC, in light of Session 0 isolation (Chapter 3) |
| Licensing / billing | Simple to count by number of machines | Confirm the concept of concurrent session counts and whether RDS CALs are required, during requirements definition13 |
The axis for the decision is simple: agree with the client early in development on whether “this app just needs to run self-contained on one person’s PC” or “it may be used by multiple people at once on a shared terminal or RDS environment.” Leave this ambiguous while implementation proceeds, and you’ll end up rebuilding every one of the points above from scratch.
9. Implementation example — using session detection and namespaces appropriately
Here’s how the detection methods and mutex naming choices from Chapters 5 and 6 come together in .NET code.
using System.Runtime.InteropServices;
internal static class SessionInfo
{
[DllImport("user32.dll")]
private static extern int GetSystemMetrics(int nIndex);
private const int SM_REMOTESESSION = 0x1000;
// For a WPF app, System.Windows.SystemParameters.IsRemoteSession returns the same result
public static bool IsRemoteSession() => GetSystemMetrics(SM_REMOTESESSION) != 0;
// Quick-and-dirty check. Not an official API, so keep it to logging/diagnostics
// rather than important branching
public static string? SessionName() =>
Environment.GetEnvironmentVariable("SESSIONNAME");
}
The mutex used for single-instance protection needs to distinguish between three cases: “one machine-wide,” “one per session,” and “one per user, spanning that user’s sessions.” The third case (per-user, spanning sessions) cannot be achieved with the default Global\ alone — the name also needs to include the user’s SID. That is covered in detail in “Preventing Double Launches in Windows Apps.”
// Requires: using System.Security.AccessControl; and using System.Security.Principal;
// (also requires the NuGet package System.Threading.AccessControl)
// Intent: even with multiple users logged on simultaneously on RDS/a shared PC,
// keep exactly one instance machine-wide.
// .NET's Mutex/MutexAcl.Create requests SYNCHRONIZE and MUTEX_MODIFY_STATE
// internally, plus DELETE/READ_CONTROL/WRITE_DAC/WRITE_OWNER, even just to open
// an existing mutex. That means unless "Authenticated Users" is granted the
// equivalent of FullControl, a legitimate user other than the creator will get
// an UnauthorizedAccessException at the point of checking createdNew.
// (The tradeoff: this also leaves the ACL rewritable later by any authenticated
// user. If that's unacceptable, drop the Mutex class and call CreateMutexEx
// directly via P/Invoke, requesting only the minimum access rights you need)
//
// Caution: this ACL only applies if you're the one who managed to create the
// mutex first. A fixed-name Global\ mutex is at risk of being pre-created by
// another user (name squatting), in which case createdGlobal comes back false,
// or you get an UnauthorizedAccessException depending on that other party's ACL.
// If you genuinely need to lock other parties out, combine a hard-to-guess name
// with another exclusion mechanism (see Chapter 5 of the companion article for
// details)
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);
// Intent: one instance is enough per session (multiple sessions of the same
// user are fine being treated separately).
// Leave it in the default session namespace as-is
using var perSessionMutex = new Mutex(
initiallyOwned: false,
name: "KomuraSoft.MyApp.SingleInstance",
createdNew: out bool createdPerSession);
if (!createdGlobal)
{
// Already running somewhere else on the machine, including other sessions
return;
}
Creating a mutex prefixed with Global\ itself requires no special privilege (as noted earlier, the privilege check only applies to newly creating file mapping objects and symbolic link objects).4 It’s a mistake to leave the mutex in the default session namespace if your intent is “one per user.” It’s not unusual for the same user to simultaneously hold a disconnected RDP session and a fresh console/RDP session, and in that case each session is treated as a separate mutex — so with the default left as-is, the same user can end up launching a second instance. Which of “one machine-wide,” “one per session,” or “one per user across sessions” is the correct requirement depends on the nature of the app, so confirm the requirement before you settle on a naming scheme.
10. Summary
A Windows “session” is a concept that keeps surfacing across topics that seem unrelated at first glance — service design, remote desktop operations, single-instance protection for named objects. The core structure boils down to three points. A service runs in Session 0, a special, isolated session, and cannot directly show UI. RDS is built around multiple concurrent sessions, while an ordinary client OS is fundamentally single-session. And named objects have a per-session namespace, so you need to judge, based on intent, whether Global\ is required.
Whether shared-PC/RDS behavior belongs in your requirements is a topic where noticing it late in implementation causes a lot of rework. We strongly recommend confirming, during requirements definition, “who will use this app, on which machines, and how many people at once.” If you’re planning to deploy an existing app onto a shared-PC or RDS environment, or you need to investigate a bug where “things go wrong when multiple users use it,” it’s usually faster to diagnose while looking at the actual configuration — feel free to reach out.
Related Articles
- How to Build a Windows Service — A Worker Service Guide
- A Decision Table for Windows Inter-Process Communication
- Preventing Double Launches in Windows Apps — Named Mutexes and Activating an Existing Instance
- An Introduction to Windows User Profiles — AppData and NTUSER.DAT
- A Concrete Way to Isolate “Only the Operations That Need Admin Rights” in a Windows App
Related Consulting Areas
Komura Soft LLC handles design reviews for Windows apps aimed at eventual deployment on shared PCs or RDS environments, service/IPC separation design, and root-cause investigation of bugs specific to concurrent multi-user use.
- Technical Consulting / Design Review
- Windows App Development
- Modernization and Maintenance of Existing Windows Software
- Contact
References
-
Microsoft Learn, Service Changes for Windows Vista. On the background behind introducing Session 0 isolation, the resulting inability for services and applications to exchange window messages, and the
WTSSendMessagefunction as an alternative. ↩ ↩2 ↩3 -
Microsoft Learn, Interactive Services. On why a service cannot interact directly with a user, the design of using
CreateProcessAsUserto launch a separate GUI process and coordinate over IPC, and the guidance to distinguish pipe names by session ID. ↩ ↩2 ↩3 -
Microsoft Learn, Windows Enterprise multi-session FAQ. On the multi-session capability — holding multiple interactive sessions at once — previously being possible only on Windows Server, and now being offered exclusively for Azure Virtual Desktop. ↩ ↩2 ↩3
-
Microsoft Learn, Kernel object namespaces. On the per-session namespace for named kernel objects, the
Global\/Local\prefixes, and the requirement ofSeCreateGlobalPrivilegeto newly create a file mapping or symbolic link object in the global namespace. ↩ ↩2 ↩3 ↩4 -
Microsoft Learn, Named Pipes. On named pipes being accessible from any process within the bounds of security checks, and reachable remotely if the server service is running (the basis for why this is a different mechanism from the
Global\/Local\session namespaces used by mutexes, etc.). ↩ -
Microsoft Learn, GetSystemMetrics function (winuser.h). On the specification for detecting a remote session via
SM_REMOTESESSION. ↩ ↩2 -
Microsoft Learn, Detecting the Remote Desktop Services environment. On sample code for
GetSystemMetrics(SM_REMOTESESSION), the constraint that it misdetects a remote session as local when RemoteFX vGPU is in use, and the alternative detection via theGlassSessionIdregistry key. ↩ ↩2 -
Microsoft Security Bulletin, MS12-034 - Windows and Messages Vulnerability (CVE-2012-0180). On a privilege-escalation vulnerability exploiting a flaw in the Windows kernel-mode driver’s (win32k.sys) window message handling (one example of this class of attack via window messages). ↩
-
Microsoft Learn, schtasks create. On the SYSTEM account having no interactive logon rights, and a user being unable to see or interact with a program or task running under SYSTEM privileges. ↩
-
Microsoft Learn, Elevated Task Model. On the configuration where a standard-user application performs operations that require administrator privileges through a task registered as SYSTEM but with a security descriptor configured so a standard user can still launch it. ↩
-
Microsoft Learn, Remote Desktop Services overview in Windows Server. On RDS being the foundation that provides multi-session, session-based desktops. ↩ ↩2
-
Microsoft Learn, Troubleshoot Remote desktop disconnected errors. On up to two simultaneous remote connections being possible for administrative purposes without an RDS CAL, and the RD Session Host role plus proper RDS CALs being required beyond that. ↩ ↩2
-
Microsoft Learn, License Remote Desktop Services with Client Access Licenses (CALs). On the difference between the per device and per user RDS CAL models, and the licensing server’s issuance/tracking mechanism. ↩ ↩2
-
Microsoft Learn, qwinsta. On the
SESSIONNAMEcolumn showing the name assigned to a session (consolefor the console, or a name starting withrdp-tcp#for an RDP connection). ↩ -
Microsoft Learn, SystemParameters.IsRemoteSession Property. On the property that exposes the
SM_REMOTESESSION-equivalent check from WPF. ↩ -
Microsoft Learn, WTSGetActiveConsoleSessionId function (winbase.h). On retrieving the session ID connected to the physical console, and returning
0xFFFFFFFFduring a transition. ↩ ↩2 -
Microsoft Learn, WTSEnumerateSessions function (wtsapi32.h). On being able to enumerate all sessions on an RD Session Host server. ↩
-
Microsoft Learn, Client/Server application guidelines. On why “a connection from one machine” must not be equated with “one user session,” and identifying sessions via
ProcessIdToSessionId. ↩ -
Microsoft Learn, Policy CSP - ADMX_TerminalServer (TS_TEMP_PER_SESSION). On Remote Desktop Services creating a separate temp folder per session by default (under the user profile, with the session ID in the name). ↩
-
Microsoft Learn, Peripheral hardware guidelines. On how client-side drives and printers appear from an RD Session Host server depending on the redirection configuration, and the mechanism for a device driver to disable redirection. ↩
Related Articles
Recent articles sharing the same tags. Deepen your understanding with closely related topics.
Preventing Multiple Instances of a Windows App — Named Mutexes and Activating the Existing Window on a Second Launch
This article organizes the classic requirement for business Windows apps — 'don't let the same app launch twice' — around a named Mutex. ...
Choosing Windows Inter-Process Communication ── A Decision Table for Named Pipes / TCP / gRPC / Shared Memory / COM
How do you choose the right way for Windows applications to talk to each other? This article organizes named pipes, local TCP, gRPC, shar...
How to Choose Where a Windows App Stores Local Data — A Decision Table for SQLite / JSON / Registry / Access
Where — and in what format — should a Windows desktop app store its data? This article organizes the choice between AppData and ProgramDa...
Integrating Entra ID Authentication into WinForms/WPF Apps — A Practical Architecture with MSAL.NET and the WAM Broker
A practical, hands-on look at integrating Entra ID (formerly Azure AD) authentication into WinForms/WPF desktop apps: the public client m...
Date, Time, and Timezones in Business Apps — From DateTime Pitfalls to the UTC-Storage Principle and Test Design
Timestamps drift by nine hours after a server migration; only the overseas office's dates roll back to the previous day — we trace date/t...
Related Topics
These topic pages place the article in a broader service and decision context.
Windows Technical Topics
Topic hub for KomuraSoft LLC's Windows development, investigation, and legacy-asset articles.
Where This Topic Connects
This article connects naturally to the following service pages.
Windows App Development
We support Windows desktop applications that involve resident processing, device integration, operational logging, and maintainable structure.
Technical Consulting & Design Review
We help clarify design direction, architectural boundaries, lifetime ownership, and how to handle legacy Windows assets.
Author Profile
Profile page for the article author.
Go Komura
Representative of KomuraSoft LLC
Focused on Windows software development, technical consulting, and investigations into failures that are difficult to reproduce.
Public links