Microsoft KB Archive/841291

= How to write an application that supports the Fast User Switching feature by using Visual Basic .NET or Visual Basic 2005 in Windows XP =

Article ID: 841291

Article Last Modified on 11/16/2007

-

APPLIES TO


 * Microsoft Visual Basic 2005
 * Microsoft Visual Basic .NET 2003 Standard Edition
 * Microsoft Visual Basic .NET 2002 Standard Edition
 * Microsoft Windows XP Home Edition
 * Microsoft Windows XP Professional

-







For a Microsoft Visual C++ .NET version of this article, see 310153.



IN THIS TASK

 * INTRODUCTION
 * Requirements
 * Create a Windows application by using Visual Basic .NET
 * Verify session-switch notifications
 * Detect an existing application instance
 * Verify application detection
 * Troubleshooting
 * REFERENCES



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 top

Requirements
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 top

Create 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.</li> To change the Text property of the Form1 form to the FastSwitchingForm value, follow these steps: <ol style="list-style-type: lower-alpha;"> In the Solution Explorer window, right-click the Form1.vb file, and then click View Designer.</li> Right-click the layout of the Form1 form, and then click Properties.</li> In the Properties window, locate the Text property, and then type FastSwitchingForm.

FastSwitchingForm is the value for the Text property.</li></ol> </li></ol>

back to the top

Add 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: <ol> In the Solution Explorer window, right-click the Form1.vb file, and then click View Code.</li>  Put the following code at the top of the Form1.vb file: Imports System.Runtime.InteropServices </li>  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. </li>  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(&quot;Wtsapi32.dll&quot;)> _ 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(&quot;Wtsapi32.dll&quot;)> _ 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(&quot;user32.dll&quot;)> _ 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(&quot;user32.dll&quot;)> _ 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(&quot;user32.dll&quot;)> _ Private Shared Function SetForegroundWindow(ByVal hwnd As IntPtr) As Boolean

End Function </li>  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:=&quot;FullTrust&quot;)> _ 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(&quot;WTS_CONSOLE_CONNECT&quot;, &quot;WM_SESSION_CHANGE&quot;, MessageBoxButtons.OK) Case WTS_CONSOLE_DISCONNECT MessageBox.Show(&quot;WTS_CONSOLE_DISCONNECT&quot;, &quot;WM_SESSION_CHANGE&quot;, MessageBoxButtons.OK) Case WTS_SESSION_LOCK MessageBox.Show(&quot;WTS_SESSION_LOCK&quot;, &quot;WM_SESSION_CHANGE&quot;, MessageBoxButtons.OK) Case WTS_SESSION_UNLOCK MessageBox.Show(&quot;WTS_SESSION_UNLOCK&quot;, &quot;WM_SESSION_CHANGE&quot;, MessageBoxButtons.OK) Case Else MessageBox.Show(&quot;test&quot;, &quot;test&quot;, 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: <ul> 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.</li> 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.</li> 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.</li></ul> </li></ol>

back to the top

Verify 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.
 * 1) On the Build menu, click Rebuild FastSwitching.
 * 2) On the Debug menu, click Start.
 * 3) Click Start, and then click Log Off.

The Log Off Windows dialog box appears.
 * 1) Click Switch User.
 * 2) On the next screen, click the current user name to switch to your previous user session.
 * 3) Make sure that you have received the WTS_SESSION_LOCK notification and the WTS_SESSION_UNLOCK notification.
 * 4) Click OK to dismiss both message boxes.
 * 5) Click Start, and then click Log Off.

The Log Off Windows dialog box appears.
 * 1) Click Switch User.
 * 2) Switch to a session for a new user, and then switch back to the original user session.
 * 3) 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.
 * 4) Click OK to dismiss all message boxes.

back to the top

Detect 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(&quot;FastSwitchingForm&quot;) '           If (blnChk = False) Then MsgBox(&quot;Another instance of &quot; & _               Diagnostics.Process.GetCurrentProcess.ProcessName & _                &quot; is already running in another user's session on this computer.&quot; & _                &quot; You cannot run two instances at a time. You must use the other instance.&quot;) 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(&quot;Another instance of &quot; & argStrAppToFind & _               &quot; is already running on this computer. &quot; & _                &quot;You cannot run two instances at a time.&quot; & _                &quot; You must use the other instance.&quot;) 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 top

Verify application detection

 * 1) On the Build menu, click Rebuild FastSwitching.
 * 2) On the Debug menu, click Start.
 * 3) Minimize the application.
 * 4) Start another instance of the application, and then verify that the existing application instance is restored and is brought to the foreground.
 * 5) Try repeatedly to start additional application instances. Every time make sure that the existing application instance is brought to the foreground.
 * 6) Make sure that one instance of the application is running, and then switch to a new user session.
 * 7) 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.
 * 8) Click OK to dismiss the message box.
 * 9) Switch to the original user session, dismiss the session-switch notification message boxes, and then close the application.

back to the top

Troubleshooting
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

<div class="references_section">