INTRODUCTION
This step-by-step article discusses how to use the
Microsoft Windows XP Fast User Switching feature in an application that you create
by using Microsoft Visual Basic .NET or Microsoft Visual Basic 2005. The Fast User Switching
feature permits multiple users to share the same computer. Each user has an
individual profile and an individual desktop. You can switch among user sessions without
logging off. Applications that you write must support the Fast User Switching feature so that you do not lose data or cause the computer to crash when you switch among user sessions.
To support the Fast User Switching feature, your application must store user data and
application data in valid locations.
Additionally, if your
application provides functionality that may fail when multiple users run the application at the same time, you must add code to your application to detect this condition and
to react appropriately. For example, an application may use a global resource in an
unsafe way, and this may cause the application to fail.
If a second application instance may affect
optional functionality, the application must do the following when the application starts:
- Detect that a user is already running the
application.
- Block any problematic features.
- Inform the current user why certain functionality may be
unavailable.
If a second application instance may affect primary
functionality, your application must also do the following:
- Detect that a user is already running the
application.
- Report an error condition to the current user and then exit
gracefully.
If the application has to know when it is running
in the active-user session and when session switches occur, the application can register to
receive session-notification messages.
back to the topRequirements
This
article assumes that you are familiar with the following topics:
- How to do Microsoft Windows application development by using Visual
Basic .NET or Visual Basic 2005.
- How to use the Fast User Switching feature in Windows XP.
- How to create new user account in Windows XP.
The following list outlines the recommended hardware, software,
network infrastructure, and service packs that you need:
- Windows XP Home Edition or Windows XP Professional.
- Microsoft Visual Studio .NET or Microsoft Visual Studio 2005
back to the topCreate a Windows application by using Visual Basic .NET
- Start Visual Studio .NET or Visual Studio 2005.
- On the File menu, point to
New, and then click Project.
The New
Project dialog box appears. - In the New Project dialog box, click Visual Basic
Projects under
Project Types.
Note In Visual Studio 2005, click Visual Basic under Project Types. - In the New Project dialog box, click Windows
Application under
Templates.
- In the Name box, type
FastSwitching.
- Click OK.
- To change the Text property of the Form1 form to the FastSwitchingForm value, follow these steps:
- In the Solution Explorer window, right-click the Form1.vb file, and then
click View Designer.
- Right-click the layout of the Form1 form, and then
click Properties.
- In the Properties window, locate the
Text property, and then type
FastSwitchingForm.
FastSwitchingForm is the value for the
Text property.
back to the topAdd code to receive session-switch notifications
If the application must know that when it is running in
the session of the active user and when session switches occur, the application can register to
receive the WM_WTSSESSION_CHANGE message by calling the
WTSRegisterSessionNotification function. To do this, follow these steps:
- In the Solution Explorer window, right-click the Form1.vb
file, and then click View Code.
- Put the following code at the top of the Form1.vb file:
Imports System.Runtime.InteropServices
- In the Form1.vb file, put the following code after the
Form1 class declaration:
'Constant declarations that you can use for message processing follow:
'
Private Const _WIN32_WINNT As Int32 = &H501
Private Const NOTIFY_FOR_THIS_SESSION As Int32 = &H0
Private Const WM_WTSSESSION_CHANGE As Int32 = &H2B1
Private Const WTS_CONSOLE_CONNECT As Int32 = &H1
Private Const WTS_CONSOLE_DISCONNECT As Int32 = &H2
Private Const WTS_SESSION_LOCK As Int32 = &H7
Private Const WTS_SESSION_UNLOCK As Int32 = &H8
Private Const WM_DESTROY As Int32 = &H2
Private Const WM_ACTIVATEAPP As Int32 = &H1C
The previous constants are used to process messages that the
application receives from the operating system. - Put the following code in the Form1.vb file after the
constant declarations that are mentioned in step 3 of this section:
'Declaration of API functions that you can use for message processing follow:
'
'The WTSUnRegisterSessionNotification function unregisters the specified
'window so that the specified window receives no more session-change notifications.
<DllImport("Wtsapi32.dll")> _
Private Shared Function WTSUnRegisterSessionNotification( _
ByVal hWnd As IntPtr) As Boolean
End Function
'The WTSRegisterSessionNotification function registers the specified
'window to receive session-change notifications.
<DllImport("Wtsapi32.dll")> _
Private Shared Function WTSRegisterSessionNotification( _
ByVal hWnd As IntPtr, ByVal dwFlags As Int32) As Boolean
End Function
'The PostQuitMessage function indicates to the system that a thread
'has made a request to quit. The PostQuitMessage function is typically used in
'response to a WM_DESTROY message.
<DllImport("user32.dll")> _
Private Shared Sub PostQuitMessage( _
ByVal nExitCode As Int32)
End Sub
'The OpenIcon function restores a minimized (iconic) window to its
'previous size and previous position. The OpenIcon function then activates the window.
<DllImport("user32.dll")> _
Private Shared Function OpenIcon(ByVal hwnd As IntPtr) As Boolean
End Function
'The SetForegroundWindow function puts the thread that created the
'specified window in the foreground and then activates the window.
<DllImport("user32.dll")> _
Private Shared Function SetForegroundWindow(ByVal hwnd As IntPtr) As Boolean
End Function
- Put the following code after the Windows Form Designer generated code statement in the Form1.vb file:
'
'The following processes Windows messages:
'
<System.Security.Permissions.PermissionSetAttribute _
(System.Security.Permissions.SecurityAction.Demand, Name:="FullTrust")> _
Protected Overrides Sub WndProc(ByRef m As Message) '
Select Case (m.Msg)
'
Case WM_WTSSESSION_CHANGE
Select Case (m.WParam.ToInt32)
Case WTS_CONSOLE_CONNECT
MessageBox.Show("WTS_CONSOLE_CONNECT", "WM_SESSION_CHANGE", MessageBoxButtons.OK)
Case WTS_CONSOLE_DISCONNECT
MessageBox.Show("WTS_CONSOLE_DISCONNECT", "WM_SESSION_CHANGE", MessageBoxButtons.OK)
Case WTS_SESSION_LOCK
MessageBox.Show("WTS_SESSION_LOCK", "WM_SESSION_CHANGE", MessageBoxButtons.OK)
Case WTS_SESSION_UNLOCK
MessageBox.Show("WTS_SESSION_UNLOCK", "WM_SESSION_CHANGE", MessageBoxButtons.OK)
Case Else
MessageBox.Show("test", "test", MessageBoxButtons.OK)
End Select
'
End Select
'
MyBase.WndProc(m)
'
End Sub
'
Protected Overrides Sub OnHandleCreated(ByVal e As System.EventArgs)
'
'The WTSRegisterSessionNotification function registers the specified
'window to receive session-change notifications.
'
WTSRegisterSessionNotification(Me.Handle, NOTIFY_FOR_THIS_SESSION)
'
MyBase.OnHandleCreated(e)
'
End Sub
'
Protected Overrides Sub OnHandleDestroyed(ByVal e As System.EventArgs)
'
WTSUnRegisterSessionNotification(Me.Handle)
PostQuitMessage(0)
'
End Sub
'
The following information is provided for the previous sample code: - The case statement in the WndProc method processes the WM_WTSSESSION_CHANGE
messages. The wParam property of the message contains a status code that indicates
the reason that the session-change notification is sent. The code detects a
subset of the available status codes and then displays message boxes that indicate
the status codes that are received.
- The OnHandleCreated method processes the HandleCreated event that is triggered when a handle is created for the form. Inside this method, the WTSRegisterSessionNotification function is called. The WTSRegisterSessionNotification function registers the specified window to receive session-change notifications.
- The OnHandleDestroyed method processes the HandleDestroyed event that is triggered when the handle of the form is in the process of being destroyed. Inside the OnHandleDestroyed method, the WTSUnRegisterSessionNotification function is called. The WTSUnRegisterSessionNotification function unregisters the specified window so that the specified window receives no additional session-change notifications.
Additionally, the
PostQuitMessage function is also called to indicate to the system that a thread has made a request to quit.
back to the topVerify session-switch notifications
This section assumes that you have at least two Windows XP user
accounts. If you only have one Windows XP user account, create another Windows XP user account before you follow these steps.
- On the Build menu, click Rebuild
FastSwitching.
- On the Debug menu, click
Start.
- Click Start, and then click Log Off.
The Log Off Windows dialog box appears. - Click Switch User.
- On the next screen, click the current user name to switch
to your previous user session.
- Make sure that you have received the WTS_SESSION_LOCK notification and the WTS_SESSION_UNLOCK notification.
- Click OK to dismiss both message boxes.
- Click Start, and then click Log Off.
The Log Off Windows dialog box appears. - Click Switch User.
- Switch to a session for a new user, and then switch back to the
original user session.
- Make sure that you have received the WTS_SESSION_LOCK notification, the WTS_CONSOLE_DISCONNECT notification, the WTS_SESSION_UNLOCK notification, and the WTS_CONSOLE_CONNECT notification.
- Click OK to dismiss all message boxes.
back to the topDetect an existing application instance
Your application may have to detect existing running instances
for the following reasons:
- To turn off certain functionality that cannot be shared
between multiple instances.
- To prevent subsequent instances from running if it is required. For example, if you build an application such as the Add or Remove Programs utility in Control Panel, you cannot run two instances of Add/Remove Windows Components.
Note You must make sure that the Windows XP
application supports multiple instances that are running at the same time in the same user session or
in different user sessions. An application that does not permit multiple instances of that application to run is
not considered a best-practice Windows XP application.
The
GetProcessesByName method is used to detect the existence of the instance of
the application that is running. Then the
ActivatePrevInstance function is called to verify whether the application is running in the session of the current user or in the
session of another user. The
ActivatePrevInstance function uses the text
property that is the title of the form as the parameter to verify. The
ActivatePrevInstance function verifies whether any
application is available with a title that matches the title that is in the parameter of
the
ActivatePrevInstance function.
To detect existing application
instances, put the following sample code at the end of the Form1.vb file of the
FastSwitching project. Put this sample code immediately before the
End Class statement:
Private Sub Form1_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles MyBase.Load
'
Dim blnChk As Boolean
'
'Checking for the existence of an
'application that is already running.
'
If UBound(Diagnostics.Process.GetProcessesByName _
(Diagnostics.Process.GetCurrentProcess.ProcessName)) > 0 Then
'
'Checking if there is already
'an instance that is running in this user's session.
'
blnChk = ActivatePrevInstance("FastSwitchingForm")
'
If (blnChk = False) Then
MsgBox("Another instance of " & _
Diagnostics.Process.GetCurrentProcess.ProcessName & _
" is already running in another user's session on this computer." & _
" You cannot run two instances at a time. You must use the other instance.")
End
End If
'
End If
'
End Sub
'
'This is a function to verify whether the application is already running.
'If the application is already running, set the focus on the application.
'
Private Function ActivatePrevInstance(ByVal argStrAppToFind As String) As Boolean
'
Dim PrevHndl As IntPtr = Nothing
Dim result As Boolean
Dim objProcess As New Process() 'This is a variable to hold an individual process.
Dim objProcesses() As Process 'This is a collection of all the processes that are running on the local computer.
'
objProcesses = Process.GetProcesses() 'Get all processes into the collection.()
'
For Each objProcess In objProcesses
'
'Check and then exit if there is an application that is already running.
'
If UCase(objProcess.MainWindowTitle) = UCase(argStrAppToFind) Then
MsgBox("Another instance of " & argStrAppToFind & _
" is already running on this computer. " & _
"You cannot run two instances at a time." & _
" You must use the other instance.")
PrevHndl = objProcess.MainWindowHandle
Exit For
End If
Next
'
If PrevHndl.Equals(IntPtr.Zero) Then
ActivatePrevInstance = False
Exit Function 'If no previous instance is found, exit the application.
Else
ActivatePrevInstance = True
End If
'
result = OpenIcon(PrevHndl) 'Restore the program.
result = SetForegroundWindow(PrevHndl) 'Activate the application.
End 'End the current instance of the application.
'
End Function
'
Note In the
Form1_Load method, the
ActivatePrevInstance method is called with the
FastSwitchingForm parameter. The
FastSwitchingForm parameter is the value of the
Text property that is the title of
the form in your application.
back to the
topVerify application detection
- On the Build menu, click Rebuild
FastSwitching.
- On the Debug menu, click
Start.
- Minimize the application.
- Start another instance of the application, and then verify that
the existing application instance is restored and is brought to the foreground.
- Try repeatedly to start additional application instances.
Every time make sure that the existing application instance is brought to
the foreground.
- Make sure that one instance of the application is running, and then switch to a
new user session.
- Try to start the application. Make sure that the message
box appears that explains that the application is already running in the session of another user.
- Click OK to dismiss the message box.
- Switch to the original user session, dismiss the session-switch notification message boxes, and then close the application.
back to the topTroubleshooting
If you are developing your application to support the Fast User Switching feature, make sure that the application interacts with the correct session. Do not assume that
session 0 is the current active desktop session. In Windows XP, the active
session can have any session number. Use the
WTSGetActiveConsoleSessionID function to identify the active session.
back to the
top