none
Use Process.Start to start an application without Administrator privileges on Windows 7

    Question

  • Visual Basic 2010 - Net Framework 4.0 Client

    I have an application (application #1) running with Administrator privileges on Windows 7.

    I want application # 1 to start another application (application #2) without Administrator privileges so application #2 is running as a standard user.

    Is there a way to do this?  I have been using Process.Start.

    Friday, February 24, 2012 8:16 PM

Answers

  • Hi Pbhonl,

    Please try this code, the button1 will start a process as standard user, and button2 will start a process as the same privilege as the base process. Please run this code with administrator privilege:

    Imports System.Runtime.InteropServices
    
    Public Class ChangeProcessToken
        <DllImport("advapi32.dll", SetLastError:=True)> _
        Private Shared Function OpenProcessToken(ByVal ProcessHandle As IntPtr, ByVal DesiredAccess As Integer, ByRef TokenHandle As IntPtr) As Boolean
        End Function
    
        <DllImport("advapi32.dll", SetLastError:=True)> _
        Public Shared Function GetTokenInformation(ByVal TokenHandle As IntPtr, ByVal TokenInformationClass As TOKEN_INFORMATION_CLASS, _
        ByVal TokenInformation As IntPtr, ByVal TokenInformationLength As System.UInt32, _
        ByRef ReturnLength As System.UInt32) As Boolean
        End Function
    
        <DllImport("User32.dll", SetLastError:=True)> _
        Public Shared Function GetShellWindow() As IntPtr
        End Function
    
        <DllImport("user32.dll", SetLastError:=True)> _
        Private Shared Function GetWindowThreadProcessId(ByVal hwnd As IntPtr, _
                              ByRef lpdwProcessId As IntPtr) As Integer
        End Function
    
        <DllImport("kernel32.dll")> _
        Private Shared Function OpenProcess(ByVal dwDesiredAccess As UInteger, <MarshalAs(UnmanagedType.Bool)> ByVal bInheritHandle As Boolean, ByVal dwProcessId As Integer) As IntPtr
        End Function
    
        <DllImport("advapi32.dll", SetLastError:=True)> _
        Private Shared Function DuplicateTokenEx( _
        ByVal ExistingTokenHandle As IntPtr, _
        ByVal dwDesiredAccess As UInt32, _
        ByRef lpThreadAttributes As SECURITY_ATTRIBUTES, _
        ByVal ImpersonationLevel As Integer, _
        ByVal TokenType As Integer, _
        ByRef DuplicateTokenHandle As System.IntPtr) As Boolean
        End Function
    
        <DllImport("advapi32.dll", SetLastError:=True)> _
        Private Shared Function LookupPrivilegeValue(lpSystemName As String, _
       lpName As String, ByRef lpLuid As LUID) As Boolean
        End Function
    
        ' Use this signature if you want the previous state information returned
        <DllImport("advapi32.dll", SetLastError:=True)> _
        Private Shared Function AdjustTokenPrivileges( _
        ByVal TokenHandle As IntPtr, _
        ByVal DisableAllPrivileges As Boolean, _
        ByRef NewState As TOKEN_PRIVILEGES, _
        ByVal BufferLengthInBytes As Integer, _
        ByRef PreviousState As TOKEN_PRIVILEGES, _
        ByRef ReturnLengthInBytes As Integer _
      ) As Boolean
        End Function
    
        <DllImport("advapi32", SetLastError:=True, CharSet:=CharSet.Unicode)>
        Public Shared Function CreateProcessWithTokenW(hToken As IntPtr, dwLogonFlags As Integer, lpApplicationName As String, lpCommandLine As String, dwCreationFlags As Integer, lpEnvironment As IntPtr, lpCurrentDirectory As IntPtr, ByRef lpStartupInfo As STARTUPINFO, ByRef lpProcessInformation As PROCESS_INFORMATION) As Boolean
        End Function
    
        <StructLayout(LayoutKind.Sequential)> _
        Structure SECURITY_ATTRIBUTES
            Public nLength As Integer
            Public lpSecurityDescriptor As IntPtr
            Public bInheritHandle As Integer
        End Structure
    
        Public Enum TOKEN_INFORMATION_CLASS
            TokenUser = 1
            TokenGroups
            TokenPrivileges
            TokenOwner
            TokenPrimaryGroup
            TokenDefaultDacl
            TokenSource
            TokenType
            TokenImpersonationLevel
            TokenStatistics
            TokenRestrictedSids
            TokenSessionId
            TokenGroupsAndPrivileges
            TokenSessionReference
            TokenSandBoxInert
            TokenAuditPolicy
            TokenOrigin
            TokenElevationType
            TokenLinkedToken
            TokenElevation
            TokenHasRestrictions
            TokenAccessInformation
            TokenVirtualizationAllowed
            TokenVirtualizationEnabled
            TokenIntegrityLevel
            TokenUIAccess
            TokenMandatoryPolicy
            TokenLogonSid
            MaxTokenInfoClass
        End Enum
    
        Structure TOKEN_PRIVILEGES
            Public PrivilegeCount As Integer
            Public TheLuid As LUID
            Public Attributes As Integer
        End Structure
    
        Structure LUID
            Public LowPart As UInt32
            Public HighPart As UInt32
        End Structure
    
        <StructLayout(LayoutKind.Sequential, CharSet:=CharSet.Unicode)> _
        Structure STARTUPINFO
            Public cb As Integer
            Public lpReserved As String
            Public lpDesktop As String
            Public lpTitle As String
            Public dwX As Integer
            Public dwY As Integer
            Public dwXSize As Integer
            Public dwYSize As Integer
            Public dwXCountChars As Integer
            Public dwYCountChars As Integer
            Public dwFillAttribute As Integer
            Public dwFlags As Integer
            Public wShowWindow As Short
            Public cbReserved2 As Short
            Public lpReserved2 As Integer
            Public hStdInput As Integer
            Public hStdOutput As Integer
            Public hStdError As Integer
        End Structure
    
        Structure PROCESS_INFORMATION
            Public hProcess As IntPtr
            Public hThread As IntPtr
            Public dwProcessId As Integer
            Public dwThreadId As Integer
        End Structure
    
        Public Const SE_PRIVILEGE_ENABLED = &H2L
        Public Const PROCESS_QUERY_INFORMATION = &H400
        Public Const TOKEN_ASSIGN_PRIMARY = &H1
        Public Const TOKEN_DUPLICATE = &H2
        Public Const TOKEN_IMPERSONATE = &H4
        Public Const TOKEN_QUERY = &H8
        Public Const TOKEN_QUERY_SOURCE = &H10
        Public Const TOKEN_ADJUST_PRIVILEGES = &H20
        Public Const TOKEN_ADJUST_GROUPS = &H40
        Public Const TOKEN_ADJUST_DEFAULT = &H80
        Public Const TOKEN_ADJUST_SESSIONID = &H100
        Public Const SecurityImpersonation = 2
        Public Const TokenPrimary = 1
        Public Const SE_INCREASE_QUOTA_NAME = "SeIncreaseQuotaPrivilege"
    
        Private Sub Button1_Click(sender As System.Object, e As System.EventArgs) Handles Button1.Click
            Dim currentProcess As Process = Process.GetCurrentProcess
            'Enable SeIncreaseQuotaPrivilege in this process.  (This requires administrative privileges.)
            Dim hProcessToken As IntPtr = Nothing
            OpenProcessToken(currentProcess.Handle, TOKEN_ADJUST_PRIVILEGES, hProcessToken)
            Dim tkp As TOKEN_PRIVILEGES
            tkp.PrivilegeCount = 1
            LookupPrivilegeValue(Nothing, SE_INCREASE_QUOTA_NAME, tkp.TheLuid)
            tkp.Attributes = SE_PRIVILEGE_ENABLED
    
            AdjustTokenPrivileges(hProcessToken, False, tkp, 0, Nothing, Nothing)
    
            'Get window handle representing the desktop shell.  This might not work if there is no shell window, or when
            'using a custom shell.  Also note that we're assuming that the shell is not running elevated.
            Dim hShellWnd As IntPtr = GetShellWindow()
    
            'Get the ID of the desktop shell process.
            Dim dwShellPID As IntPtr
            GetWindowThreadProcessId(hShellWnd, dwShellPID)
    
            'Open the desktop shell process in order to get the process token.
            Dim hShellProcess As IntPtr = OpenProcess(PROCESS_QUERY_INFORMATION, False, dwShellPID)
            Dim hShellProcessToken As IntPtr = Nothing
            Dim hPrimaryToken As IntPtr = Nothing
    
            'Get the process token of the desktop shell.
            OpenProcessToken(hShellProcess, TOKEN_DUPLICATE, hShellProcessToken)
    
            'Duplicate the shell's process token to get a primary token.
            Dim dwTokenRights As Integer = TOKEN_QUERY Or TOKEN_ASSIGN_PRIMARY Or TOKEN_DUPLICATE Or TOKEN_ADJUST_DEFAULT Or TOKEN_ADJUST_SESSIONID
            DuplicateTokenEx(hShellProcessToken, dwTokenRights, Nothing, SecurityImpersonation, TokenPrimary, hPrimaryToken)
    
            Dim si As STARTUPINFO = Nothing
            Dim pi As PROCESS_INFORMATION = Nothing
    
            si.cb = Marshal.SizeOf(si)
            CreateProcessWithTokenW(hPrimaryToken, 0, "C:\Program Files (x86)\Microsoft Visual Studio 10.0\Common7\IDE\devenv.exe", " E:\Projects\WinApp-NetWork\WinApp-NetWork.sln", 0, Nothing, Nothing, si, pi)
            Console.WriteLine(pi.dwProcessId)
        End Sub
    
        <DllImport("kernel32.dll")> _
        Shared Function CreateProcess(lpApplicationName As IntPtr, _
       lpCommandLine As IntPtr, ByRef lpProcessAttributes As SECURITY_ATTRIBUTES, _
       ByRef lpThreadAttributes As SECURITY_ATTRIBUTES, bInheritHandles As Boolean, _
       dwCreationFlags As UInt32, lpEnvironment As IntPtr, lpCurrentDirectory As String, _
       <[In]()> ByRef lpStartupInfo As STARTUPINFO, _
       <[Out]()> ByRef lpProcessInformation As PROCESS_INFORMATION) As Boolean
        End Function
    
        Private Sub Button2_Click(sender As System.Object, e As System.EventArgs) Handles Button2.Click
            Dim p As Process = New Process
            p.StartInfo.FileName = "C:\Program Files (x86)\Microsoft Visual Studio 10.0\Common7\IDE\devenv.exe"
            p.StartInfo.Arguments = "E:\Projects\WinApp-NetWork\WinApp-NetWork.sln"
            p.Start()
            Console.WriteLine(p.Id)
        End Sub
    End Class

    I hope this will be helpful.

    Best regards,


    Mike Feng
    MSDN Community Support | Feedback to us
    Please remember to mark the replies as answers if they help and unmark them if they provide no help.

    • Proposed as answer by Mike FengModerator Tuesday, March 6, 2012 12:54 PM
    • Marked as answer by pbhonl Thursday, March 8, 2012 5:23 PM
    Tuesday, March 6, 2012 12:54 PM
    Moderator
  • Hi Pbhonl,

    Welcome to the MSDN Forum.

    Please take a look at this article: http://blogs.microsoft.co.il/blogs/sasha/archive/2009/07/09/launch-a-process-as-standard-user-from-an-elevated-process.aspx 

    Aaron Margosis tells this story and offers a C++ solution to launch a process as standard user. The general steps are to obtain the user token of the shell process (explorer.exe), make a primary token off of that and then launch the new process with that token.

    I’ve taken the liberty of adapting the sample so that it can be used from managed code, and added it to the UAC Helpers project on CodePlex, release v0.1 (last time it was updated was over a year and a half ago, so it’s about time that we stir it up a little bit). The new functionality is available in the UserAccountControl.CreateProcessAsStandardUser method, and it’s very easy to use (if the process is not elevated, it just falls back to usingSystem.Diagnostics.Process.Start).

    I hope this will be helpful.

    Best regards,


    Mike Feng
    MSDN Community Support | Feedback to us
    Please remember to mark the replies as answers if they help and unmark them if they provide no help.

    • Proposed as answer by Sarathi R Monday, February 27, 2012 5:33 PM
    • Marked as answer by Mike FengModerator Monday, March 5, 2012 3:54 PM
    Monday, February 27, 2012 3:08 PM
    Moderator

All replies

  • [Process.Start Method (String, String, SecureString, String)]

    [Process.Start Method (ProcessStartInfo)]

    [ProcessStartInfo Class] --> Property UserName, Property Password.


    在現實生活中,你和誰在一起的確很重要,甚至能改變你的成長軌跡,決定你的人生成敗。 和什麼樣的人在一起,就會有什麼樣的人生。 和勤奮的人在一起,你不會懶惰; 和積極的人在一起,你不會消沈; 與智者同行,你會不同凡響; 與高人為伍,你能登上巔峰。

    Saturday, February 25, 2012 5:44 AM
  • Hi Pbhonl,

    Welcome to the MSDN Forum.

    Please take a look at this article: http://blogs.microsoft.co.il/blogs/sasha/archive/2009/07/09/launch-a-process-as-standard-user-from-an-elevated-process.aspx 

    Aaron Margosis tells this story and offers a C++ solution to launch a process as standard user. The general steps are to obtain the user token of the shell process (explorer.exe), make a primary token off of that and then launch the new process with that token.

    I’ve taken the liberty of adapting the sample so that it can be used from managed code, and added it to the UAC Helpers project on CodePlex, release v0.1 (last time it was updated was over a year and a half ago, so it’s about time that we stir it up a little bit). The new functionality is available in the UserAccountControl.CreateProcessAsStandardUser method, and it’s very easy to use (if the process is not elevated, it just falls back to usingSystem.Diagnostics.Process.Start).

    I hope this will be helpful.

    Best regards,


    Mike Feng
    MSDN Community Support | Feedback to us
    Please remember to mark the replies as answers if they help and unmark them if they provide no help.

    • Proposed as answer by Sarathi R Monday, February 27, 2012 5:33 PM
    • Marked as answer by Mike FengModerator Monday, March 5, 2012 3:54 PM
    Monday, February 27, 2012 3:08 PM
    Moderator
  • Thank you for your response to my question.

    I use Visual Studio 2008 and VB.net.  I see a lot of C++ code here.  I downloaded realse v0.1 but I can't even get the solution to load in my Visual Studio.

    Is there any way you could provide me with some VB.NET sample code that would accomplish starting an applcation as a standard user from an application running with Administrator privileges?

    Thanks again for your help,

    pbhonl



    • Edited by pbhonl Monday, March 5, 2012 8:49 PM
    Monday, March 5, 2012 8:41 PM
  • Hi Pbhonl,

    Please try this code, the button1 will start a process as standard user, and button2 will start a process as the same privilege as the base process. Please run this code with administrator privilege:

    Imports System.Runtime.InteropServices
    
    Public Class ChangeProcessToken
        <DllImport("advapi32.dll", SetLastError:=True)> _
        Private Shared Function OpenProcessToken(ByVal ProcessHandle As IntPtr, ByVal DesiredAccess As Integer, ByRef TokenHandle As IntPtr) As Boolean
        End Function
    
        <DllImport("advapi32.dll", SetLastError:=True)> _
        Public Shared Function GetTokenInformation(ByVal TokenHandle As IntPtr, ByVal TokenInformationClass As TOKEN_INFORMATION_CLASS, _
        ByVal TokenInformation As IntPtr, ByVal TokenInformationLength As System.UInt32, _
        ByRef ReturnLength As System.UInt32) As Boolean
        End Function
    
        <DllImport("User32.dll", SetLastError:=True)> _
        Public Shared Function GetShellWindow() As IntPtr
        End Function
    
        <DllImport("user32.dll", SetLastError:=True)> _
        Private Shared Function GetWindowThreadProcessId(ByVal hwnd As IntPtr, _
                              ByRef lpdwProcessId As IntPtr) As Integer
        End Function
    
        <DllImport("kernel32.dll")> _
        Private Shared Function OpenProcess(ByVal dwDesiredAccess As UInteger, <MarshalAs(UnmanagedType.Bool)> ByVal bInheritHandle As Boolean, ByVal dwProcessId As Integer) As IntPtr
        End Function
    
        <DllImport("advapi32.dll", SetLastError:=True)> _
        Private Shared Function DuplicateTokenEx( _
        ByVal ExistingTokenHandle As IntPtr, _
        ByVal dwDesiredAccess As UInt32, _
        ByRef lpThreadAttributes As SECURITY_ATTRIBUTES, _
        ByVal ImpersonationLevel As Integer, _
        ByVal TokenType As Integer, _
        ByRef DuplicateTokenHandle As System.IntPtr) As Boolean
        End Function
    
        <DllImport("advapi32.dll", SetLastError:=True)> _
        Private Shared Function LookupPrivilegeValue(lpSystemName As String, _
       lpName As String, ByRef lpLuid As LUID) As Boolean
        End Function
    
        ' Use this signature if you want the previous state information returned
        <DllImport("advapi32.dll", SetLastError:=True)> _
        Private Shared Function AdjustTokenPrivileges( _
        ByVal TokenHandle As IntPtr, _
        ByVal DisableAllPrivileges As Boolean, _
        ByRef NewState As TOKEN_PRIVILEGES, _
        ByVal BufferLengthInBytes As Integer, _
        ByRef PreviousState As TOKEN_PRIVILEGES, _
        ByRef ReturnLengthInBytes As Integer _
      ) As Boolean
        End Function
    
        <DllImport("advapi32", SetLastError:=True, CharSet:=CharSet.Unicode)>
        Public Shared Function CreateProcessWithTokenW(hToken As IntPtr, dwLogonFlags As Integer, lpApplicationName As String, lpCommandLine As String, dwCreationFlags As Integer, lpEnvironment As IntPtr, lpCurrentDirectory As IntPtr, ByRef lpStartupInfo As STARTUPINFO, ByRef lpProcessInformation As PROCESS_INFORMATION) As Boolean
        End Function
    
        <StructLayout(LayoutKind.Sequential)> _
        Structure SECURITY_ATTRIBUTES
            Public nLength As Integer
            Public lpSecurityDescriptor As IntPtr
            Public bInheritHandle As Integer
        End Structure
    
        Public Enum TOKEN_INFORMATION_CLASS
            TokenUser = 1
            TokenGroups
            TokenPrivileges
            TokenOwner
            TokenPrimaryGroup
            TokenDefaultDacl
            TokenSource
            TokenType
            TokenImpersonationLevel
            TokenStatistics
            TokenRestrictedSids
            TokenSessionId
            TokenGroupsAndPrivileges
            TokenSessionReference
            TokenSandBoxInert
            TokenAuditPolicy
            TokenOrigin
            TokenElevationType
            TokenLinkedToken
            TokenElevation
            TokenHasRestrictions
            TokenAccessInformation
            TokenVirtualizationAllowed
            TokenVirtualizationEnabled
            TokenIntegrityLevel
            TokenUIAccess
            TokenMandatoryPolicy
            TokenLogonSid
            MaxTokenInfoClass
        End Enum
    
        Structure TOKEN_PRIVILEGES
            Public PrivilegeCount As Integer
            Public TheLuid As LUID
            Public Attributes As Integer
        End Structure
    
        Structure LUID
            Public LowPart As UInt32
            Public HighPart As UInt32
        End Structure
    
        <StructLayout(LayoutKind.Sequential, CharSet:=CharSet.Unicode)> _
        Structure STARTUPINFO
            Public cb As Integer
            Public lpReserved As String
            Public lpDesktop As String
            Public lpTitle As String
            Public dwX As Integer
            Public dwY As Integer
            Public dwXSize As Integer
            Public dwYSize As Integer
            Public dwXCountChars As Integer
            Public dwYCountChars As Integer
            Public dwFillAttribute As Integer
            Public dwFlags As Integer
            Public wShowWindow As Short
            Public cbReserved2 As Short
            Public lpReserved2 As Integer
            Public hStdInput As Integer
            Public hStdOutput As Integer
            Public hStdError As Integer
        End Structure
    
        Structure PROCESS_INFORMATION
            Public hProcess As IntPtr
            Public hThread As IntPtr
            Public dwProcessId As Integer
            Public dwThreadId As Integer
        End Structure
    
        Public Const SE_PRIVILEGE_ENABLED = &H2L
        Public Const PROCESS_QUERY_INFORMATION = &H400
        Public Const TOKEN_ASSIGN_PRIMARY = &H1
        Public Const TOKEN_DUPLICATE = &H2
        Public Const TOKEN_IMPERSONATE = &H4
        Public Const TOKEN_QUERY = &H8
        Public Const TOKEN_QUERY_SOURCE = &H10
        Public Const TOKEN_ADJUST_PRIVILEGES = &H20
        Public Const TOKEN_ADJUST_GROUPS = &H40
        Public Const TOKEN_ADJUST_DEFAULT = &H80
        Public Const TOKEN_ADJUST_SESSIONID = &H100
        Public Const SecurityImpersonation = 2
        Public Const TokenPrimary = 1
        Public Const SE_INCREASE_QUOTA_NAME = "SeIncreaseQuotaPrivilege"
    
        Private Sub Button1_Click(sender As System.Object, e As System.EventArgs) Handles Button1.Click
            Dim currentProcess As Process = Process.GetCurrentProcess
            'Enable SeIncreaseQuotaPrivilege in this process.  (This requires administrative privileges.)
            Dim hProcessToken As IntPtr = Nothing
            OpenProcessToken(currentProcess.Handle, TOKEN_ADJUST_PRIVILEGES, hProcessToken)
            Dim tkp As TOKEN_PRIVILEGES
            tkp.PrivilegeCount = 1
            LookupPrivilegeValue(Nothing, SE_INCREASE_QUOTA_NAME, tkp.TheLuid)
            tkp.Attributes = SE_PRIVILEGE_ENABLED
    
            AdjustTokenPrivileges(hProcessToken, False, tkp, 0, Nothing, Nothing)
    
            'Get window handle representing the desktop shell.  This might not work if there is no shell window, or when
            'using a custom shell.  Also note that we're assuming that the shell is not running elevated.
            Dim hShellWnd As IntPtr = GetShellWindow()
    
            'Get the ID of the desktop shell process.
            Dim dwShellPID As IntPtr
            GetWindowThreadProcessId(hShellWnd, dwShellPID)
    
            'Open the desktop shell process in order to get the process token.
            Dim hShellProcess As IntPtr = OpenProcess(PROCESS_QUERY_INFORMATION, False, dwShellPID)
            Dim hShellProcessToken As IntPtr = Nothing
            Dim hPrimaryToken As IntPtr = Nothing
    
            'Get the process token of the desktop shell.
            OpenProcessToken(hShellProcess, TOKEN_DUPLICATE, hShellProcessToken)
    
            'Duplicate the shell's process token to get a primary token.
            Dim dwTokenRights As Integer = TOKEN_QUERY Or TOKEN_ASSIGN_PRIMARY Or TOKEN_DUPLICATE Or TOKEN_ADJUST_DEFAULT Or TOKEN_ADJUST_SESSIONID
            DuplicateTokenEx(hShellProcessToken, dwTokenRights, Nothing, SecurityImpersonation, TokenPrimary, hPrimaryToken)
    
            Dim si As STARTUPINFO = Nothing
            Dim pi As PROCESS_INFORMATION = Nothing
    
            si.cb = Marshal.SizeOf(si)
            CreateProcessWithTokenW(hPrimaryToken, 0, "C:\Program Files (x86)\Microsoft Visual Studio 10.0\Common7\IDE\devenv.exe", " E:\Projects\WinApp-NetWork\WinApp-NetWork.sln", 0, Nothing, Nothing, si, pi)
            Console.WriteLine(pi.dwProcessId)
        End Sub
    
        <DllImport("kernel32.dll")> _
        Shared Function CreateProcess(lpApplicationName As IntPtr, _
       lpCommandLine As IntPtr, ByRef lpProcessAttributes As SECURITY_ATTRIBUTES, _
       ByRef lpThreadAttributes As SECURITY_ATTRIBUTES, bInheritHandles As Boolean, _
       dwCreationFlags As UInt32, lpEnvironment As IntPtr, lpCurrentDirectory As String, _
       <[In]()> ByRef lpStartupInfo As STARTUPINFO, _
       <[Out]()> ByRef lpProcessInformation As PROCESS_INFORMATION) As Boolean
        End Function
    
        Private Sub Button2_Click(sender As System.Object, e As System.EventArgs) Handles Button2.Click
            Dim p As Process = New Process
            p.StartInfo.FileName = "C:\Program Files (x86)\Microsoft Visual Studio 10.0\Common7\IDE\devenv.exe"
            p.StartInfo.Arguments = "E:\Projects\WinApp-NetWork\WinApp-NetWork.sln"
            p.Start()
            Console.WriteLine(p.Id)
        End Sub
    End Class

    I hope this will be helpful.

    Best regards,


    Mike Feng
    MSDN Community Support | Feedback to us
    Please remember to mark the replies as answers if they help and unmark them if they provide no help.

    • Proposed as answer by Mike FengModerator Tuesday, March 6, 2012 12:54 PM
    • Marked as answer by pbhonl Thursday, March 8, 2012 5:23 PM
    Tuesday, March 6, 2012 12:54 PM
    Moderator
  • Mike,

    This was very helpful.  I have my applications working because of your help.

    Thank you,

    Pete

    Thursday, March 8, 2012 5:25 PM