I don't have the specific details off the top of my head. But I believe
what you're looking for is the PERF_COUNTER_COUNTER performance counter,
which you can retrieve via WMI. There's not support specifically for
that particular counter (or most of them, for that matter) in .NET, but
.NET does have the general-purpose API for WMI that you can use to get
at the counter.
Note that the counter doesn't actually retrieve the absolute number of
context switches, but you can derive that information from the
information it does retrieve.
The AppDomain.GetCurrentThreadId() method retrieves (if I recall
correctly) the thread ID for the OS thread currently executing that
method. The Thread.ManagedThreadId property retrieves the thread ID for
the _managed_ thread currently executing the property getter.
The .NET specification allows for different implementations of threads
in managed code, depending on the .NET implementation (see my previous
comment about server versions of .NET, for example). In particular,
while it is convenient for .NET to map a managed thread directly to an
unmanaged thread, there is no requirement that it do so.
This means that even in a single managed thread, calling
GetCurrentThreadId() may return different numbers at different times,
depending on which OS thread is actually hosting the managed thread at
The most common situation in which this would happen is on those
implementations of .NET in which a thread is running in a fiber, and so
doesn't even have a real OS thread dedicated to it. But even without
fibers, .NET has the option of running a given managed thread on
different OS threads at different times.
In practice, as long as you're running a client version of .NET, I
believe it's a pretty safe bet that the OS thread remains the same for a
given managed thread. This would be especially true for WinForms
programs, at least in the main GUI thread, because Forms wraps the
unmanaged Win32 API USER and GDI objects, which themselves have affinity
for the thread on which they are created. That is, it's a requirement
for the objects to run on that thread.
In theory, .NET is not really constrained by that requirement, but in
practice it would be silly to run the managed thread that owns those
objects on multiple OS threads, because then .NET would still have to
suffer the overhead of marshaling message processing for those objects
back to the correct OS thread.
Which is all a long way of saying that, I don't really know why you care
what Spy++ says about your .NET program, but for your purposes calling
AppDomain.GetCurrentThreadId() is probably fine. Don't use it in
production code, but for learning about and "looking under the hood" of
.NET, it should be good enough.
For what it's worth, all of the above is really more about unmanaged
code than managed. To some extent, if you are concerned about (for
example) context switches, then managed code is a really hard
environment in which to take control of that. The whole point of
managed code is that it operates in what is essentially a virtual
machine that exists as a layer between your code and the actual
operating system. As such, you have a lot less control over specific
performance aspects such as threading and context switches.
If you're just curious about how things work together, that's fine. But
you should be wary