Export (0) Print
Expand All

Microsoft Windows XP Fast User Switching: Design Guide for Building Business Applications

 

Sterling Reasor, Christopher Evans, and Victor Tan
Microsoft Corporation

June 2001

Summary: This article addresses Fast User Switching, a new feature in Microsoft Windows XP that leverages the data separation technology of NT profiles and provides a fast and convenient mechanism for switching between user accounts. (14 printed pages)

Contents

Introduction
NT Profiles
Windows Session Switching
Network Connectivity
Creating User Accounts During Out Of Box Experience (OOBE)

Introduction

Microsoft® Windows® XP is a new operating system based on Windows 2000 technology. Fast User Switching is a new feature in Windows XP that leverages the data separation technology of Windows NT® profiles and provides a fast and convenient mechanism for switching between user accounts.

Data Separation Using NT Profiles

Windows XP is a personal system; each user of the computer gets a separate Windows account. So, in a family of three, Mom, Dad, and Billy each get an account. Individual Windows accounts represent a departure from Windows 95 and Windows 98, in which all users in a household typically shared one account.

Users are encouraged to create these accounts during the setup process. Accounts can also be created later using the Control Panel. These accounts are really NT Profiles, which means that each user's data is separated. By default these accounts are not password protected, but users can choose to set passwords on their accounts.

Fast User Switching Feature

Windows XP introduces the Fast User Switching feature. In Windows XP, it is not necessary for a user to log off the computer. Instead, the user's account is always logged on and the user can switch quickly between all open accounts. For example, Dad comes home and starts using his machine. He opens Microsoft PowerPoint® and starts working on a document. Billy then comes up to him and asks to use the computer. Billy goes to the Welcome screen, clicks Billy Smith, logs on, and starts playing a game. Meanwhile, Dad remains logged on; Dad's PowerPoint presentation is open and his Internet connection is preserved. If Dad wants to, he can switch to his open account without logging off Billy. In essence, with Windows XP many users can simultaneously use the computer.

When a Windows XP machine is left alone with a user logged on, the system will return to the Welcome screen while keeping all the applications running. Additionally, notifications appear on the Welcome screen providing information such as the number of users who are logged on, whether a user has unread e-mail, and how many programs are running.

Remote Desktop in Windows XP Professional

Windows XP Professional also enables users to access data and applications from other machines. On business workstations, Windows XP Professional allows users to access their desktops from remote computers.

For example, Dad can lock his workstation at the office, go home, and connect to his office workstation. The home computer can remotely control the applications running on the office workstation. When Dad returns to the office, he can unlock his desktop and continue working where he left off at home.

NT Profiles

Both Fast User Switching and Remote Desktop use terminal services technology and work with most earlier Microsoft Win32® applications without requiring any changes. If your application is Windows 2000 Logo Certified, or has followed the Application Specification for Windows 2000, your application should run fine in Windows XP. Additionally, you can take advantage of a new message that can help you run your application even better on Windows XP systems. This message, as well as other new ways to exploit Fast User Switching, is described later in this document.

Infrastructure for NT Profiles

Windows 2000 provides an underlying infrastructure that supports state separation of user data, user settings, and computer settings. Applications that use this infrastructure properly have the following end-user benefits:

  • A user can easily back up individual documents and settings without backing up application and operating system files.
  • Multiple users can share a single computer and maintain each user's preferences and settings.
  • A user can work at multiple computers and maintain documents and settings from one computer to another.

Create Start Menu Entries for All Users

If your application is meant to be run by anyone who has access to the machine, then create the application's start menu entry in the all users profile. You can use SHGetFolderPath with the appropriate CSIDL to store application data depending on your needs. For per computer, nonuser specific, and nonroaming data, use CSIDL_COMMON_APPDATA.

Default to the My Documents Folder for Storage of User-Created Data

To default to the My Documents folder, call the Common File Open/Save with no parameters. Otherwise, target the My Documents folder directly by passing CSIDL_PERSONAL to SHGetFolderPath(). The My Documents folder is for user-created data, but not for temporary storage or application state data. For imaging applications, it is recommended to use CSIDL_MYPICTURES, a descendant of My Documents, in place of My Documents.

Do not create a mapped file in a central location that is not specific to the user's profile, such as the Windows directory. Temporary files, memory-mapped files, and documents should all be stored in the appropriate subdirectory of the user's profile directory. The API SHGetFolderPathAndSubDir makes working in the AppData part of the name space much easier as it will create the subdirectories for you.

You can use SHGetFolderPath to find the appropriate storage location for files. Passing CSIDL_COMMON_APPDATA to your application returns a file system directory that serves as a common repository for application-specific data. Passing CSIDL_COMMON_PROGRAMS returns the All Users start menu. Users cannot write to other users' shared documents—they can only read other users' shared documents. Applications may modify this default security setting on an application-specific subdirectory of CSIDL_COMMON_DOCUMENTS. Along with the CSIDL_COMMON file folders are the security issues that go along with them. By default you need to set up the ACLs property if you intend full read/write by all users including limited users.

CSIDL_LOCAL_APPDATA should be used for data that should change when the user changes, such as temporary files.

Classify and Store Application Data Correctly

Application data such as user preferences, application state, and temp files, must be stored either in application data folders or the registry according to the following classifications:

  • Per user, roaming
  • Per user, non-roaming
  • Per machine (non-user specific and non-roaming)

Degrade Gracefully on "Access denied" Messages

Degrading gracefully can be accomplished by:

  • Disabling the operation in the first place.

    This is the approach taken by System Settings in Control Panel. Users cannot set system-wide environment variables, but they can set their own environment variables. Thus, when a User launches this applet, the system-wide environment variable option is "grayed-out." When an administrator launches the applet, the system-wide environment variables can be modified.

  • Display an appropriate error message. For example:
    • "You must be an administrator to perform this operation"
    • "You only have permissions to view the properties of this object"
    • "You must have xyz privilege to perform this operation"
    • "You must have write access to abc.xyz to perform this operation"

Using the Registry

Applications may also use the registry for storing read/write application data and configuration files.

The HKCU registry is appropriate for storing small amounts of data (less than 64KB) and for policy settings that are per user.

Avoid writing to HKLM during runtime, because normal Users have Read Only access to the entire HKLM tree by default. In addition, HKLM does not support roaming.

Larger, file-based data should be placed in the Application Data folder. For example, Internet Explorer's Temporary Internet Cache is stored within the user profile and not in the registry.

At install time your application must not store more than a total of 128KB across HKCU and HKLM. Note that HK Classes Root is excluded.

For more information, see Designed for Windows 2000 Logo at http://msdn.microsoft.com/certification/appspec.asp.

Windows Session Switching

Listening for Session Switch Events

Typically, an application does not need to be notified when a session switch occurs. However, if the application needs to be aware when its desktop is current, it can register for session switch notifications. Applications that access the serial port or another shared resource on the computer should check for this. To register for a notification, use the following function, which can be found in Wtsapi32.h:

BOOL WINAPI
WTSRegisterSessionNotification(
    HWND hWnd,         // Window handle
    DWORD dwFlags         // Flags
    );

The registered HWND receives the message WM_WTSSESSION_CHANGE.

In dwFlags you can specify:

  • NOTIFY_FOR_THIS_SESSION. A window is notified only about the session change events that affect the session to which window belongs.
  • NOTIFY_FOR_ALL_SESSIONS. A window is notified for all session change events.

The action happening on the session can be found in wParam code, which may contain one of the following flags.

WTS_CONSOLE_CONNECT
WTS_CONSOLE_DISCONNECT
WTS_REMOTE_CONNECT
WTS_REMOTE_DISCONNECT
WTS_SESSION_LOGON
WTS_SESSION_LOGOFF
WTS_SESSION_LOCK
WTS_SESSION_UNLOCK

lParam contains the sessionId for the session affected.

When your process no longer requires these notifications or is terminating, it should call the following to unregister its notification.

BOOL WINAPI
WTSUnRegisterSesssionNotification(
    HWND hWnd      // window handle.
    );

The HWND values passed to WTSRegisterSessionNotification are reference counted, so you must call WTSUnRegisterSessionNotification exactly the same number of times that you call WTSRegisterSessionNotification.

Applications can use the WTS_CONSOLE_CONNECT, WTS_CONSOLE_DISCONNECT, WTS_REMOTE_CONNECT, WTS_REMOTE_DISCONNECT messages to track their state, as well as to release and acquire console specific resources.

Ensuring One Version of Your Application Is Running

Many applications must ensure that there is only one instance of the application running. There are many ways to do this that will work in Windows XP.

Use FindWindow or FindWindowEx to search for a window that your application opens. If it is already open, use this to determine that the application is already open.

Create a Mutex or Semaphore when your application is opened and close it when the application exits. The namespace is separated for each desktop, allowing a unique list of mutexes and semaphores.

System Services Interaction

From a programmatic standpoint, several cases need to be addressed:

  • The server process receives a direct request from a client process.

    In this case, the message is probably transmitted via LPC or RPC. In either case, there are APIs to get the client token. Use WTSQueryUserToken. Once the client token is obtained, the server can call advapi32!CreateProcessAsUser. This brings up the process on the correct window station, assuming that the client user token has a session tag, which it should.

  • The server process receives some form of notification and needs to display the User Interface (UI) in the current user's context.

    The server must get the current user's token. The service then must duplicate the token into the server process (client process relative to TS) for the given session identifier, which the server process can give to TS. In this case, the server process must be running as SYSTEM.

  • The server process receives some form of notification and needs to display the UI, but it doesn't have to be in the current user's context.

    In this case, the server process can duplicate its primary process token and change the session identifier to the current session identifier. The current session identifier can be obtained by using WTSGetActiveConsoleSessionID.

With the addition of the Remote Desktop feature in Windows XP Professional, applications must not use more bandwidth than is necessary. Applications should avoid extensive screen drawings and animation effects if the desktop is connected remotely. User desktops can be switched between remote and console dynamically. Applications should use the WTS_REMOTE_CONNECT and WTS_REMOTE_DISCONNECT messages to synchronize with the remote v/s local connection state. In addition, you can find out whether the session is remote or console at any time by calling GetSystemMetrics(SM_REMOTESESSION).

By using the session moniker and IUserNotification, your COM app can launch on the active user's session's desktop. You can use the session moniker via IMoniker::BindToObject, which will also work remotely. For BindToObject, you must use the full syntax as described below.

HRESULT CoCreateOnConsoleWithBindToObject(REFCLSID rclsid, REFIID riid, void** ppv)
{
    *ppv = NULL;
    
    IBindCtx* pbc;
    HRESULT hr = CreateBindCtx(0, &pbc);
    if (SUCCEEDED(hr)) 
    {
        WCHAR sz[128], szClsid[64];
        
        StringFromGUID2(rclsid, szClsid, ARRAYSIZE(szClsid)); 
        LPWSTR psz = szClsid + 1;   // skip "{"
        while (*psz != L'}') 
            psz++;
        *psz = NULL;
        
        lstrcpyW(sz, L"Session:Console!clsid:");
        lstrcatW(sz, &szClsid[1]);
        
        // Parse the name and get a moniker:
        ULONG ulEaten;
        IMoniker* pmk;
        hr = MkParseDisplayName(pbc, sz, &ulEaten, &pmk);
        if (SUCCEEDED(hr))
        {
            IClassFactory *pcf;
            hr = pmk->BindToObject(pbc, NULL, IID_PPV_ARG(IClassFactory, &pcf));
            if (SUCCEEDED(hr))
            {
                hr = pcf->CreateInstance(NULL, riid, ppv);
                pcf->Release();
            }
            pmk->Release();
        }
        pbc->Release();
    }
    return hr;
}

Please keep in mind that this is sample code and will not compile if it is just copied and pasted.

If you are writing software for use with biometric authentication, such as a thumbprint reader, and you would like leverage Fast User Switching, please e-mail fusqa@microsoft.com to license a special DLL.

Network Connectivity

With Fast User Switching, it is possible to leave programs running and have another user log on to the machine. It is also possible to leave connections to the Internet active when switching from one user to another. Depending on the application being run, an independent software vendor (ISV) may wish either to terminate a connection or leave it running.

Always-On Connections

Connections that remain active at all times are unaffected by Fast User Switching. Typically, always-on connections are xDSL or cable modem connections. Wireless networking connections are also considered always-on.

User-Initiated Connections

User initiated connections include Virtual Private Networking, PPPoX and Remote Access Server (RAS) dialup connections. All user-initiated connections remain active when the Welcome screen is displayed. For many connections, if User A initiated a network connection and then User B switches on as the active session, User A's connection will be disconnected.

Always-On User-Initiated Connections

Some user-initiated network connections can remain active when users Fast User Switch from one session to another. Network connections will remain active if:

  • The connection is available to all users.
  • The credentials for the connection are saved.
  • In Home Edition, all connections created with the New Connection Wizard are implicitly made available to all users.

Exceptions

If a user initiates a network connection and then logs off (does not switch users), the network connection will be disconnected.

Under all circumstances, a connection with Internet Connection Sharing (ICS) enabled will remain connected even if the users switch windows sessions and/or all the users log off the computer. ICS connections can be manually disconnected or may still time out and disconnect automatically.

Notes

Current RAS APIs create an all-user connection by default when called in the context of a user with administrator rights.

Any user can shutdown an all-user/global connection.

Active connections are shown across all sessions by an icon in the Notification Area.

All connections that are not all-user connections AND have globally saved credentials are dropped silently when switching users.

User Interface Changes

For Windows XP Home Edition and Professional joined to a workgroup, the option for an all-user/global-global connection is checked by default.

RAS API Changes

The following flags have been defined:

   #define RASCF_AllUsers      0x00000001
   #define RASCF_GlobalCreds   0x00000002

The rasconnw structure has been extended to include the RASCONNW.dwSessionId and RASCONNW.dwFlags.

  • RASCONNW.dwSessionId: this is the logon session id of the user who created the connection.
  • RASCF_AllUsers: this flag is set when the entry used in the connection is an all-users connection.
  • RASCF_GlobalCreds: this flag is set when the credentials used are the default credentials, such as credentials set with RasSetCredentials(RASCM_DefaultCreds).

In addition, the szDnsSuffix field has been added to RASENTRY structure to facilitate the setting of the dnssuffix programmatically.

RASCONNW

{
    DWORD    dwSize;
    HRASCONN hrasconn;
    WCHAR    szEntryName[ RAS_MaxEntryName + 1 ];
 
#if (WINVER >= 0x400)
    WCHAR    szDeviceType[ RAS_MaxDeviceType + 1 ];
    WCHAR    szDeviceName[ RAS_MaxDeviceName + 1 ];
#endif
#if (WINVER >= 0x401)
    WCHAR    szPhonebook [ MAX_PATH ];
    DWORD    dwSubEntry;
#endif
#if (WINVER >= 0x500)
    GUID    guidEntry;
#endif
#if (WINVER >= 0x501)
   DWORD   dwSessionId;
   DWORD   dwFlags;
#endif 
};

Please keep in mind that this is sample code and will not compile if it is just copied and pasted.

E-mail Notifications

When using Fast User Switching it is possible to display on the Welcome logon screen the number of unread e-mail messages for a logged user. Under the user name on the Welcome screen will be a hyperlink indicating the number of unread messages. Clicking on the hyperlink will reveal which account the messages are from. For example, Bob has three unread e-mail messages. Clicking on the hyperlink will show Bob@MSN.com (2) and Bob@hotmail.com (1). The APIs to do this are SHGetUnreadMailCount and SHSetUnreadMailCount and are in shellapi.h. When the unread e-mail count is set, the date is stamped as well. If the unread e-mail entry is more than three days old, the Welcome screen will disregard the entries.

Terminal Services

On a normal terminal services disconnect, we do nothing particular to RAS and FUS connectoid dropping.

Creating User Accounts During Out Of Box Experience (OOBE)

Multiple Internet Service Provider (ISP) accounts should not be created under a single Windows user account. All Windows XP users should have their own ISP account under their NT Profile. This easily allows for data separation, privacy and security. ISP Client Software (or any other app that has profiles) should store credentials in the user's account—and only that user's credentials in that user account. If there is only one set of ISP credentials associated with an account, then when the user launches the software, the app will not need to identify the user. As such, there would be no need to ask users to re-identify themselves since they have already been identified and authenticated by Windows.

With Windows XP, ISV's can create separate user accounts during the Out Of Box Experience (OOBE). However, we recommend that user account creation and deletion should be left up to the users to manage. If several applications each created user accounts independently, users could easily become confused by an excessive amount of user accounts. If it is absolutely necessary to create user accounts, make sure that the user confirms this decision before creating the accounts. The steps to create an account are listed below.

Before Window's NT Profiles are created, ISV's can run a setup application that creates the profile needed for the application. For example, this setup application may create Bob@MSN.com. This setup application can associate one application profile to one Windows account. At the Windows XP OOBE screen, users are asked to provide an account name. Next to the edit box for each user will be non-editable static text indicating which ISP account is associated with each account. In this example, 1st User would have Bob@MSN.com for the non-editable static text. OOBE will write 1st User's profile name in Profilename=<string> allowing the application to make an association for one application profile to one Windows user account under one key in the registry. In addition, the values for the OOBE key can be set before an upgrade so that one ISP account will be associated with one Windows account. This will allow upgrading from a non NT Profiles based operating system, like Windows Millennium.

During ISP signup in OOBE, the ISP Application writes in HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Setup\OOBE a new key called ISPAccount=<string>, in which the default value is the ISP's name, for example, MSN. In the new key, the ISP application writes ISPUserName=<string> where the default value is the user's ISP account, for example, Bob@MSN.com. Additionally, if the ISP adds a value ISPUserFriendlyName="<friendly name>", OOBE will suggest this name as the default for the 1st profile to be created.

During Windows account creation in OOBE, OOBE reads from ISPUserName and writes a non-editable text value in the User Interface next to the account creation edit box. For example, 1st User would have Bob@MSN.com next to the edit box as non-editable static text. Users can then pick any name they want for the Windows account name.

OOBE reads from the first account creation edit box and writes the Windows account name to Profilename=<string>. For example, 1st User names the account Bob Smith. The user logs on, the ISP application runs and looks for the above keys. If the current windows user matches the "Profilename," then the ISP application knows to associate ISPUserName with Profilename.

API's for creating, enumerating, and loading user profiles are all documented in "Whistler Beta 1 Edition" in the Platform SDK section of MSDN Online.

The following sample code creates the account as a user account. If the users want to be administrators, you would do the NetLocalGroupAddMembers again with "Administrators" instead of "Users." The domain field below would include the machine name. It might be sufficient for it to be "".

STDMETHODIMP CreateUserAccount(BSTR bstrLoginName)
{
    HRESULT hr;

    hr = E_FAIL;

    if ( bstrLoginName && *bstrLoginName )
    {
        NET_API_STATUS nasRet;
        USER_INFO_1 usri1 = {0};
        usri1.usri1_name     = bstrLoginName;
        usri1.usri1_password = TEXT("");
        usri1.usri1_flags    = UF_NORMAL_ACCOUNT |
       UF_SCRIPT | UF_PASSWD_NOTREQD | UF_DONT_EXPIRE_PASSWD;
        usri1.usri1_priv     = USER_PRIV_USER;

        nasRet = NetUserAdd(NULL,           // local computer
                            1,              // structure level
                            (LPBYTE)&usri1, // user infomarmation
                            NULL);          // don't care

        if ( nasRet == NERR_Success )
        {
            TCHAR szDomainAndName[256];
            LOCALGROUP_MEMBERS_INFO_3 lgrmi3;
            PLOCALGROUP_INFO_0 plgi0;

            wnsprintf(szDomainAndName, 
                      ARRAYSIZE(szDomainAndName), 
                      TEXT("%s\\%s"),
                      _szDomain,      /// this would be the machine name
                      bstrLoginName);

            lgrmi3.lgrmi3_domainandname = szDomainAndName;

            // by default newly created accounts will be user accounts

           nasRet = NetLocalGroupAddMembers(
                       NULL,
                       TEXT("Users"),
                       3,
                       (LPBYTE)&lgrmi3,
                       1);
        }

        hr = HRESULT_FROM_WIN32(nasRet);
    }
    else

    {
        hr = E_INVALIDARG;
    }

    return hr;
}

Please keep in mind that this is sample code and will not compile if it is just copied and pasted.

While this creates the account, it doesn't create the profile until the user is logged on the first time. You could force this by calling LogonUser, but there will be a noticeable impact in performance.

Enumerating Users

    dwPreferredSize = (sizeof(NET_DISPLAY_USER) + (3 * UNLEN) * s_iMaximumUserCount);
    pNDU = NULL;
    lError = NetQueryDisplayInformation(NULL,                  // NULL means LocalMachine
                                        1,                     // query User information
                                        0,                     // starting with the first user
                                        s_iMaximumUserCount,   // return a max of 100 users
                                        dwPreferredSize,       // preferred buffer size
                                        &dwEntriesRead,
                                        reinterpret_cast<void**>(&pNDU));

Don't Forget To Call

        (NET_API_STATUS)NetApiBufferFree(pNDU);

To read/write other users registries, use RegLoadKey and RegUnloadKey to load the hive. Please make sure to unload the key when you are done.

Show:
© 2014 Microsoft