Home | Contact Us | FAQ | Search & Site Map | Link to Us
Sign In | Join | Other 45 Sites in Network
HomeAnnouncementsFree MagazinesWhite PapersSubmit Content
Discussion GroupsASP.NETWindows FormsLanguages.NET FrameworkVisual Studio.NET
Articles.NET FrameworkASP.NETToolsWindows Forms
.NET DirectoryOpen Source ProjectsUser GroupsWeb Resources
Related Topics
Visual Basic 6SQL ServerMS AccessOther DB ProductsMS Server ProductsMore Topics ...

.NET Forum / .NET Framework / CLR / January 2006

Tip: Looking for answers? Try searching our database.

Performance monitoring: strange error in .Net 2.0

Thread view: 
Enable EMail Alerts  Start New Thread
Thread rating: 
Lucvdv - 05 Jan 2006 15:18 GMT
There seems to be a strange interaction/conflict between
System.Diagnostics.PerformanceCounter objects and
System.Security.Principal.WindowsIdentity.Impersonate() in CLR 2.0, that
didn't exist in 1.1.

I arrived at this by letting VS.Net 2005 convert a VB.Net 2003 project and
just starting it in the debugger after making a few minor modifications
(all related to variable initialization): it compiled without errors, but
started throwing exceptions that I believe it shouldn't, and that didn't
occur in 1.1.

My actual project (or even the relevant part of it) is too large and too
proprietary to post it in a newsgroup, but I created a sample program that
reproduces the problem and I'll paste it at the bottom of this message
(VB.Net 2003 .vb file, not converted to VS.2005 yet).

It uses LogonUser to log on to an account name/password as specified in two
CONST's near the top of the file; DuplicateHandleEx to duplicate the
handle, and the passes the duplicate to WindowsIdentity.Impersonate.

The account I tested it with has administator rights.

So first create a temporary local user account to test it with, and put
that account's name and password in the two constants.

Start a debug build in VS.Net.  When you click the START button it should
start printing CPU use and system uptime in the debug output window every
500 msec until you click STOP.

After verifying that this works, create a copy of the project directory,
and open the solution in VS.2005 and run it there.

VS.Net 2003:
It just works as expected.

VS.Net 2005:
One of the performance counters (CPU use) only works the first time its
NextValue method is called; from the second call on it throws a
Win32Exception "Access denied".

Now comment out the "Logon" line in Button1_Click, and the error doesn't
occur anymore.

The code (the Win32 API declarations are copied & pasted from my real
application, some parts may not be used here).
I tried to avoid broken lines (word-wrap by newsreaders), but please don't
shoot if...

'=================== Form1.vb ============================================

Public Class Form1
   Inherits System.Windows.Forms.Form

   Private Const ACCOUNT_NAME As String = "UserName"
   Private Const ACCOUNT_PASS As String = "PassWord"

#Region " Windows Form Designer generated code "

   Public Sub New()
       MyBase.New()

       'This call is required by the Windows Form Designer.
       InitializeComponent()

       'Add any initialization after the InitializeComponent() call

   End Sub

   'Form overrides dispose to clean up the component list.
   Protected Overloads Overrides Sub Dispose(ByVal disposing As Boolean)
       If disposing Then
           If Not (components Is Nothing) Then
               components.Dispose()
           End If
       End If
       MyBase.Dispose(disposing)
   End Sub

   'Required by the Windows Form Designer
   Private components As System.ComponentModel.IContainer

   'NOTE: The following procedure is required by the Windows Form Designer
   'It can be modified using the Windows Form Designer.  
   'Do not modify it using the code editor.
   Friend WithEvents Button1 As System.Windows.Forms.Button
   Friend WithEvents Button2 As System.Windows.Forms.Button
   Friend WithEvents Timer1 As System.Windows.Forms.Timer

   <System.Diagnostics.DebuggerStepThrough()> _
   Private Sub InitializeComponent()
       Me.components = New System.ComponentModel.Container
       Me.Button1 = New System.Windows.Forms.Button
       Me.Button2 = New System.Windows.Forms.Button
       Me.Timer1 = New System.Windows.Forms.Timer(Me.components)
       Me.SuspendLayout()
       '
       'Button1
       '
       Me.Button1.Location = New System.Drawing.Point(16, 24)
       Me.Button1.Name = "Button1"
       Me.Button1.TabIndex = 0
       Me.Button1.Text = "Start"
       '
       'Button2
       '
       Me.Button2.Location = New System.Drawing.Point(104, 24)
       Me.Button2.Name = "Button2"
       Me.Button2.TabIndex = 1
       Me.Button2.Text = "Stop"
       '
       'Timer1
       '
       Me.Timer1.Interval = 500
       '
       'Form1
       '
       Me.AutoScaleBaseSize = New System.Drawing.Size(5, 13)
       Me.ClientSize = New System.Drawing.Size(292, 273)
       Me.Controls.Add(Me.Button2)
       Me.Controls.Add(Me.Button1)
       Me.Name = "Form1"
       Me.Text = "Form1"
       Me.ResumeLayout(False)

   End Sub

#End Region

   Private Enum LOGONTYPE As Integer
       LOGON32_LOGON_INTERACTIVE = 2
       LOGON32_LOGON_NETWORK = 3
       LOGON32_LOGON_BATCH = 4
       LOGON32_LOGON_SERVICE = 5
       LOGON32_LOGON_NEW_CREDENTIALS = 9
   End Enum

   Private Enum LOGONPROVIDER As Integer
       LOGON32_PROVIDER_DEFAULT = 0
       LOGON32_PROVIDER_WINNT35 = 1
       LOGON32_PROVIDER_WINNT40 = 2
       LOGON32_PROVIDER_WINNT50 = 3
   End Enum

   Private Enum SECURITY_IMPERSONATION_LEVEL As Integer
       SecurityAnonymous = 0
       SecurityIdentification = 1
       SecurityImpersonation = 2
       SecurityDelegation = 3
   End Enum

   Private Enum TOKEN_TYPE
       tokenprimary = 1
       tokenimpersonation
   End Enum

   Private Declare Auto Function LogonUser Lib "advapi32" ( _
       ByVal Username As String, _
       ByVal Domain As String, _
       ByVal Password As String, _
       ByVal dwLogonType As LOGONTYPE, _
       ByVal dwLogonProvider As LOGONPROVIDER, _
       ByRef hToken As IntPtr _
       ) As Boolean

   Private Declare Auto Function DuplicateToken Lib "advapi32" ( _
       ByVal ExistingTokenHandle As IntPtr, _
       ByVal ImpersonationLevel As SECURITY_IMPERSONATION_LEVEL, _
       ByRef DuplicateTokenHandle As IntPtr _
       ) As Boolean

   Private Declare Auto Function DuplicateTokenEx Lib "advapi32" ( _
       ByVal hExistingToken As IntPtr, _
       ByVal dwDesiredAccess As Int32, _
       ByVal lpTokenAttributes As IntPtr, _
       ByVal ImpersonationLevel As SECURITY_IMPERSONATION_LEVEL, _
       ByVal TokenType As TOKEN_TYPE, _
       ByRef hNewToken As IntPtr _
       ) As Boolean

   Private Declare Auto Function CloseHandle Lib "kernel32" ( _
       ByVal hObject As IntPtr) As Boolean

   Private hUserToken As IntPtr = IntPtr.Zero
   Private hFullToken As IntPtr = IntPtr.Zero

   Private Impersonation _
       As System.Security.Principal.WindowsImpersonationContext = Nothing

   Public Enum LogonMode
       LogonImpersonate
       LogonDelegate
   End Enum

   Private pdCPU, pdUPT As System.Diagnostics.PerformanceCounter

   '----------------------------------------------------------------------

   Private Sub LogOn()
       Try
           Dim LMode As LOGONTYPE
           LMode = LOGONTYPE.LOGON32_LOGON_INTERACTIVE

           If LogonUser(ACCOUNT_NAME, System.Environment.MachineName, _
               ACCOUNT_PASS, LMode, _
               LOGONPROVIDER.LOGON32_PROVIDER_WINNT50, hUserToken) Then

               If DuplicateTokenEx(hUserToken, 0, IntPtr.Zero, _
                   SECURITY_IMPERSONATION_LEVEL.SecurityDelegation, _
                   TOKEN_TYPE.tokenprimary, hFullToken) Then

                   Impersonation = _
       System.Security.Principal.WindowsIdentity.Impersonate(hFullToken)

                   Debug.WriteLine("Logon OK")
               End If
           End If
       Catch ex As Exception
           Debug.WriteLine(ex.ToString())
       End Try
   End Sub

   Private Sub LogOff()
       If Not Impersonation Is Nothing Then
           Impersonation.Undo()
           Impersonation = Nothing
       End If
       If hFullToken.ToInt32 <> 0 Then
           CloseHandle(hFullToken)
           hFullToken = IntPtr.Zero
       End If
       If hUserToken.ToInt32 <> 0 Then
           CloseHandle(hUserToken)
           hUserToken = IntPtr.Zero
       End If
   End Sub

   Private Sub Button1_Click(ByVal sender As System.Object, _
   ByVal e As System.EventArgs) Handles Button1.Click
       LogOn()
       pdCPU = New System.Diagnostics.PerformanceCounter("Processor", _
           "% Processor Time", "_Total")
       pdUPT = New System.Diagnostics.PerformanceCounter("System", _
           "System Up Time")
       Timer1.Enabled = True
   End Sub

   Private Sub Button2_Click(ByVal sender As System.Object, _
   ByVal e As System.EventArgs) Handles Button2.Click
       Timer1.Enabled = False
       pdCPU = Nothing
       pdUPT = Nothing
       LogOff()
   End Sub

   Private Sub Timer1_Tick(ByVal sender As System.Object, _
   ByVal e As System.EventArgs) Handles Timer1.Tick
       Dim s1 As Single, s2 As Single
       Try
           s1 = pdCPU.NextValue
       Catch ex As Exception
           s1 = -1.0
       End Try
       Try
           s2 = pdUPT.NextValue
       Catch ex As Exception
           s2 = -1.0
       End Try
       Debug.WriteLine(s1 & ", " & s2)
   End Sub

End Class
Lucvdv - 06 Jan 2006 10:06 GMT
> There seems to be a strange interaction/conflict between
> System.Diagnostics.PerformanceCounter objects and
> System.Security.Principal.WindowsIdentity.Impersonate() in CLR 2.0, that
> didn't exist in 1.1.
<snip>

It's working through a workaround in my app now, but it still looks like a
bug in CLR 2.0 to me.

My workaround is starting a separate thread before logging on to the other
account.  That thread creates the performance objects, reads the counter
values at regular intervals, and sends them to the main code (form) through
Invoke.
Willy Denoyette [MVP] - 06 Jan 2006 10:44 GMT
| > There seems to be a strange interaction/conflict between
| > System.Diagnostics.PerformanceCounter objects and
[quoted text clipped - 9 lines]
| values at regular intervals, and sends them to the main code (form) through
| Invoke.

It works for me, did you check the impersonating identity in your timer
event handler?
Note also that your code is impersonating an interactive logon type, why?
This is not needed, you should use batch or network type logons, interactive
is way too expensive and you need to create an impersonating token from the
direct token which is another expensive action.

Willy.
Lucvdv - 06 Jan 2006 14:28 GMT
> It works for me, did you check the impersonating identity in your timer
> event handler?

Did you try the code I posted, or construct your own?

It fails in VS.Net 2005 here, while it runs successfully in VS.Net 2003.

This is some output under the VS.2005 debugger:

| Logon OK
| 0, 0
[quoted text clipped - 4 lines]
| A first chance exception of type 'System.ComponentModel.Win32Exception' occurred in System.dll
| -1, 26432.1

This is what it gives under VS.2003:

| Logon OK
| 0, 0
[quoted text clipped - 3 lines]
| 3.12376, 26522.88
| 1.561242, 26523.38

To test it, I started under an account with administrator rights and
impersonated another account with administrator rights (neither of the two
the 'original' administrator created by setup, but both new accounts that
were removed from 'users' and put in the administrators group instead).

> Note also that your code is impersonating an interactive logon type, why?

The real app uses either interactive or LOGON32_LOGON_NEW_CREDENTIALS,
depending on settings.

It's not a web app, but a simple windows forms app that has to connect to a
SQL server that may be running either locally or on another machine.

The user's own account will not always have access to the server, the app
impersonates another account for that reason.

Remote works with NEW_CREDENTIALS, but it refused to work locally if I
didn't use interactive logon.  It might work if I force it to connect to
the server differently (force TCP/IP), but the overhead for the new logon
didn't look too high (you don't notice any delay).

I can still (and maybe will) change it later, the app is only half finished
yet.
Willy Denoyette [MVP] - 06 Jan 2006 14:49 GMT
Yep, your code.
What I don't get is why it would throw an AV exception, your process runs as
A and you impersonate using B's credentials, both are members of
"administrators", if impersonation fails the process token (A) is used when
it succeeds the thread token (B) is used, but both have administrative
privileges so no exception should be throw'd.

Are you sure you logged off/logged on again after changing group membership?
If not you were still using the old token!

Willy.

| > It works for me, did you check the impersonating identity in your timer
| > event handler?
[quoted text clipped - 47 lines]
| I can still (and maybe will) change it later, the app is only half finished
| yet.
Lucvdv - 06 Jan 2006 15:58 GMT
> Yep, your code.
> What I don't get is why it would throw an AV exception, your process runs as
[quoted text clipped - 5 lines]
> Are you sure you logged off/logged on again after changing group membership?
> If not you were still using the old token!

Absolutely sure, both accounts were created sometime last year ;)

It wouldn't explain why it works in 1.1 and not in 2.0 either.

But I did find out that it's limited to my development machine alone.

I made some adjustments so the results (including the result of the logon
attempt) can be seen outside of the debugger: release builds work on a test
machine (Win2000 Pro), but on my development machine (XP Pro) they still
fail.

All machines equipped with latest OS service packs etc.

A side effect of my virus scanner, MS anti-spyware, something...?
Willy Denoyette [MVP] - 06 Jan 2006 16:45 GMT
| > Yep, your code.
| > What I don't get is why it would throw an AV exception, your process runs as
[quoted text clipped - 20 lines]
|
| A side effect of my virus scanner, MS anti-spyware, something...?

Not sure if you are using the "VS hosting process" when running from within
VS. When that's the case turn it off an try again, this is how I ran your
code. The hosting process loads your application in a separate AD, that
might be the reason for the strange behavior.

Willy.
Lucvdv - 09 Jan 2006 07:56 GMT
> Not sure if you are using the "VS hosting process" when running from within
> VS. When that's the case turn it off an try again, this is how I ran your
> code. The hosting process loads your application in a separate AD, that
> might be the reason for the strange behavior.
>
> Willy.

The release build was running outside of the debugger (after changing the
Debug.WriteLine to something more visible), and it still does it.

It must be a side effect of something else I installed, and security tools
are my prime suspects because it relates to security.
Willy Denoyette [MVP] - 09 Jan 2006 14:25 GMT
| > Not sure if you are using the "VS hosting process" when running from within
| > VS. When that's the case turn it off an try again, this is how I ran your
[quoted text clipped - 8 lines]
| It must be a side effect of something else I installed, and security tools
| are my prime suspects because it relates to security.

No, it doesn't matter whether you are running a debug or retail build, when
you are running in VS the default is "use the hosting process". Check your
project properties you'll see what I mean.

Willy.
Lucvdv - 10 Jan 2006 07:19 GMT
> | > Not sure if you are using the "VS hosting process" when running from
> within
[quoted text clipped - 14 lines]
> you are running in VS the default is "use the hosting process". Check your
> project properties you'll see what I mean.

What I meant is, I also tried a release build without Visual Studio
running, and the symptoms remain.

Free Magazines

Get these publications absolutely FREE for up to 12 months. There are no hidden fees and no obligation. Simply choose a title, complete the application form and submit it. Read more ...

Oracle MagazineNetwork ComputingComputer WorldBio-IT WorldeWeekInformation WeekInfosecurity
 
Sign In
Join
My Latest Posts
My Monitored Threads
My Blog
My Photo Gallery
My Profile
My Homepage

Start New Thread
Enable EMail Alerts
Rate this Thread



©2008 Advenet LLC   Privacy Policy - Terms of Use
This website includes both content owned or controlled by Advenet as well as content owned or controlled by third parties.