<?xml version="1.0" encoding="UTF-8" ?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en-US"><title type="html">Dan's Blog</title><subtitle type="html">Software Engineer - Red Gate Software</subtitle><id>http://www.simple-talk.com/community/blogs/dana/atom.aspx</id><link rel="alternate" type="text/html" href="http://www.simple-talk.com/community/blogs/dana/default.aspx" /><link rel="self" type="application/atom+xml" href="http://www.simple-talk.com/community/blogs/dana/atom.aspx" /><generator uri="http://communityserver.org" version="2.0.60217.2664">Community Server</generator><updated>2005-12-02T16:53:00Z</updated><entry><title>If you see SID, tell him</title><link rel="alternate" type="text/html" href="http://www.simple-talk.com/community/blogs/dana/archive/2008/03/17/45354.aspx" /><id>http://www.simple-talk.com/community/blogs/dana/archive/2008/03/17/45354.aspx</id><published>2008-03-17T13:29:00Z</published><updated>2008-03-17T13:29:00Z</updated><content type="html">&lt;p&gt;As a small addendum to my &lt;a href="/community/blogs/dana/archive/2008/03/17/45352.aspx" title="SID. Vicious?"&gt;previous blog&lt;/a&gt; on the subject of authenticating users, and checking for administrator privileges, under Windows 2000, XP and Vista, I should add this little note. It turns out, thanks to the sort of heavily industrious testing that's par for the course here at Red Gate, that LogonUser / SSPI has a habit under certain circumstances of accepting invalid logins. When a computer is not on a domain, but a workgroup, credentials validation seems to take a different route with respect to validating the existence of the domain to which the login belongs. Specifically, if it can find a local account matching the username and password from the (username,password,domain) tuple, then if not on a domain, the domain part is often summarily ignored and authentication says "yes, that's fine, this user is acceptable".&lt;/p&gt;&lt;p&gt;I suspect it does this for some good reason. Exactly what that reason is I couldn't say, though I wouldn't be surprised to find that it was something in the area of permitting users to easily access machines on a workgroup provided their usernames and passwords match. &lt;/p&gt;&lt;p&gt;This is all very fine and splendid until one comes to use the login for other purposes. The CreateService API, for example, doesn't find it as amusing as the authentication APIs with regard to accepting invalid domains. &lt;/p&gt;&lt;p&gt;So in order to make the previous authentication code robust, we have to do some more legwork. After succeeding with the LogonUser / SSPI APIs, we need to manually verify the correctness of the domain before treating the user provided credentials as correct.&lt;/p&gt;&lt;p&gt;Once again this solution involved a pinch of Google and a dash of experimentation. On the way I learned a little more about SIDs, which I discussed previously. The string format of a SID, it turns out, is a very literal interpretation of the "black box" contents of the SID data structure. It's &lt;a href="http://msdn2.microsoft.com/en-us/library/aa379597%28VS.85%29.aspx" title="SID Components (Windows)"&gt;described in some detail on MSDN&lt;/a&gt;, but briefly each SID is composed as follows:&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;S-&amp;lt;revision&amp;gt;-&amp;lt;identifier authority&amp;gt;-&amp;lt;first sub authority&amp;gt;-&amp;lt;second sub authority&amp;gt;[-&amp;lt;third sub authority&amp;gt; ... ]&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;All SIDs to date are revision 1, so we always start "S-1-...". The identifier authority tells us where the SID was originally issued, in broad terms. The subsequent sub authorities are also known as &lt;i&gt;relative identifiers&lt;/i&gt;, or RIDs.&lt;/p&gt;&lt;p&gt;Now a user's SID looks like the following:&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;
S-1-5-xx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-nnnn&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;S-1-5 means "SID version 1, issued by "NT Authority" (the originator of pretty much all user, computer and domain SIDs). It turns out that "xx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx" is the standard format for the RID of a computer or domain. "nnnn" is a number indicating which user we're dealing with on that domain.&lt;/p&gt;&lt;p&gt;Domains and computers themselves have valid SIDs. This is understandable, given that a SID is pretty universal as the identifier used to identify something for security purposes. Can you guess what a computer or domain SID looks like?&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;S-1-5-xx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;That's right - we just lop the last sub-authority off the end.&lt;/p&gt;&lt;p&gt;What are the magic numbers in the "xx-xxxxxxxxx-...." portion? Well, they're unique to the computer or domain in question. A computer's SID is generated when Windows is installed, and stay the same for its lifetime. (This has actually led to issues in companies which literally clone machines for deployment purposes; they'd end up with identical SIDs, leading to confusion between users and other fun. SysInternals provide &lt;a href="http://technet.microsoft.com/en-gb/sysinternals/bb897418.aspx" title="NewSID utility"&gt;an handy utility&lt;/a&gt; to change a computer's SID in this sort of situation.)&amp;nbsp; A domain's SID is generated when the domain is set up. I suspect that it is the computer SID of the first (chronologically) domain controller on the domain (the primary domain controller in pre Windows 2000 terms, before domain controllers started taking joint and several liability for domain security, particularly assigning SIDs) but I haven't got any evidence for that.&amp;nbsp; Certainly, in the good old days, the primary and backup domain controllers had to have the same computer ID, which would seem to fit.&lt;/p&gt;&lt;p&gt;The upshot of this discussion is that once a user is logged in it's quite easy to determine whether the account in question is a user account or a domain account, provided one has the SIDs for the domain or computer. One simply lops off the last "-nnnn" portion of the SID, and then can compare SIDs either the nice way via the EqualSID() API or, if you're feeling hacky, by strcmp'ing the SID strings. &lt;/p&gt;&lt;p&gt;When looking at this problem initially, my plan was to do just that. Initially I couldn't find an easy way to get hold of a computer or domain SID. I then tripped over the &lt;a href="http://technet.microsoft.com/en-us/sysinternals/bb897417.aspx" title="psgetsid by Mark Russinovich"&gt;psgetsid &lt;/a&gt;tool from &lt;a href="http://www.sysinternals.com" title="SysInternals"&gt;SysInternals&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;This tool is capable of converting account, machine and Windows domain names to SIDs, and SIDs to names, on local or remote computers. A quick dumpbin revealed how it does it:&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;C:\&amp;gt; vcvars32.bat&lt;br&gt;Setting environment for using Microsoft Visual Studio 2005 x86 tools.&amp;nbsp;&lt;/p&gt;&lt;p&gt;C:\&amp;gt; dumpbin d:\data\sysinternals\psgetsid.exe /imports | more&lt;br&gt;Microsoft (R) COFF/PE Dumper Version 8.00.50727.762&lt;br&gt;Copyright (C) Microsoft Corporation.&amp;nbsp; All rights reserved.&lt;br&gt;&lt;br&gt;Dump of file d:\data\sysinternals\psgetsid.exe&lt;br&gt;&lt;br&gt;File Type: EXECUTABLE IMAGE&lt;br&gt;&lt;br&gt;&amp;nbsp; Section contains the following imports:&lt;/p&gt;&lt;p&gt;&amp;nbsp; ...&lt;/p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; ADVAPI32.dll&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 40A000 Import Address Table&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 40A7C4 Import Name Table&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 0 time date stamp&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 0 Index of first forwarder reference&lt;br&gt;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 140 IsValidSid&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 116 GetSidIdentifierAuthority&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 119 GetSidSubAuthorityCount&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 118 GetSidSubAuthority&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 1D AllocateAndInitializeSid&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; AF DeleteService&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 42 ControlService&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 1AD OpenSCManagerA&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 1AF OpenServiceA&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 249 StartServiceA&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 1C3 QueryServiceStatus&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 64 CreateServiceA&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 3E CloseServiceHandle&lt;br&gt;&lt;b&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 149 LookupAccountSidA&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 147 LookupAccountNameA&lt;/b&gt;&lt;br&gt;&lt;/blockquote&gt;&lt;p&gt;LookupAccountSid and LookupAccountName are the important APIs here. LookupAccountSid takes a SID and returns its name. LookupAccountName takes a name and returns its SID. In both cases invalid names/SIDs are picked up.&lt;/p&gt;&lt;p&gt;This last property made my job even simpler. All I need to know is whether the domain part of the user supplied (username,password,domain) is in some sense valid. So all I need to do is ti call LookupAccountName to fetch the SID for that domain or computer. If it fails, then I reject the login.&lt;/p&gt;&lt;p&gt;To get this to work, I added the following method to my security classes (adapted, as always, from the internet via cut and paste, followed by some tidying and/or bug fixing): &lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; public static void GetSidForAccountOrDomain(string strAccountName,&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; out string accountSid, out string strDomainName,&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; out short AccountType)&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; {&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; int lSidSize;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; int lDomainNameSize;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; IntPtr Sid = IntPtr.Zero;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; string strServer = null;&lt;br&gt;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; // First get the required buffer sizes for SID and domain name.&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; if (!NativeSecurityApis.LookupAccountName(&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; strServer,&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; strAccountName,&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; Sid,&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; ref lSidSize,&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; null,&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; ref lDomainNameSize,&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; ref AccountType))&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; {&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; if (Marshal.GetLastWin32Error() == NativeSecurityApis.ERROR_INSUFFICIENT_BUFFER)&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; {&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; // Allocate the buffers with actual sizes that are required&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; // for SID and domain name.&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; strName = new StringBuilder(lDomainNameSize);&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; Sid = Marshal.AllocHGlobal(lSidSize);&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; if (!NativeSecurityApis.LookupAccountName(&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; strServer,&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; strAccountName,&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; Sid,&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; ref lSidSize,&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; strName,&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; ref lDomainNameSize,&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; ref AccountType))&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; throw new Win32Exception(); // last error&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; else&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; throw new Win32Exception(); // last error&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; else&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; throw new InvalidOperationException("Expected LookupAccountName to fail given no buffers"); // shouldn't get here&lt;br&gt;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; strDomainName = strName.ToString();&lt;br&gt;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; IntPtr pString;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; if (!NativeSecurityApis.ConvertSidToStringSid(Sid, out pString))&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; throw new Win32Exception(); // last error&lt;br&gt;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; accountSid = Marshal.PtrToStringAuto(pString);&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; NativeSecurityApis.LocalFree(pString);&lt;br&gt;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; //Console.WriteLine("Domain Name: {0}", strDomainName);&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; //Console.WriteLine("Account Sid: {0}", sidText);&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; Marshal.FreeHGlobal(Sid);&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; } &lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;Then, a more than elementary wrapper to ensure that a given domain is valid:&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; private static void VerifyDomain(IntPtr hToken, string domain)&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; {&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; if (string.IsNullOrEmpty(domain) || domain == ".")&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; return; // don't attempt to verify null or local (BUILTIN\.) domain&lt;br&gt;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; // attempt to look up the SID of the "domain", be it an actual domain&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; // or a computer. If we succeed, then it's valid and LogonUser() should&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; // pick&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; try&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; {&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; string domainSid;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; string canonicalDomainName;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; short accountType;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; GetSidForAccountOrDomain(domain, out domainSid, out canonicalDomainName, out accountType);&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; catch (Exception ex)&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; {&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; throw new LogonException(string.Format("Cannot verify domain {0} is valid.", domain), ex);&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;&amp;nbsp;Then I simply modified the IsUserAdmin() function described in my previous entry, so it now reads as follows:&lt;/p&gt;&lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; public static bool IsUserAdmin(string userName, string domain, string password)&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; {&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; IntPtr hToken;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; if (NativeSecurityApis.LogonUser(userName, domain, password, NativeSecurityApis.LOGON32_LOGON_INTERACTIVE, NativeSecurityApis.LOGON32_PROVIDER_DEFAULT, out hToken))&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; {&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; try&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; {&lt;br&gt;&lt;b&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; VerifyDomain(hToken, domain);&lt;/b&gt;&lt;br&gt;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; return IsUserAdmin(hToken);&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; finally&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; {&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; NativeSecurityApis.CloseHandle(hToken);&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; else&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; {&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; try&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; {&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; WindowsPrincipal principal = Win32SSPI.LogonUser(userName, domain, password);&lt;br&gt;&lt;br&gt;&lt;b&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; IntPtr hPrincipalToken = (principal.Identity as WindowsIdentity).Token;&lt;/b&gt;&lt;br&gt;&lt;b&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; VerifyDomain(hPrincipalToken, domain);&lt;/b&gt;&lt;br&gt;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; return IsUserAdmin(principal);&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; catch (Exception ex)&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; {&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; throw new LogonException(ex.Message, ex);&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;So that was pretty straightforward. At least, relative to the prior steps I describe in my previous blog entries. So far the C# code to authenticate a user, and check that they are an administrator, is still sub 1000 lines including whitespace, comments and API import definitions. Cheap at the price?&amp;nbsp;&lt;/p&gt;&lt;img src="http://www.simple-talk.com/community/aggbug.aspx?PostID=45354" width="1" height="1"&gt;</content><author><name>Dan J Archer</name><uri>http://www.simple-talk.com/community/user/Profile.aspx?UserID=2114</uri></author></entry><entry><title>SID. Vicious?</title><link rel="alternate" type="text/html" href="http://www.simple-talk.com/community/blogs/dana/archive/2008/03/17/45352.aspx" /><id>http://www.simple-talk.com/community/blogs/dana/archive/2008/03/17/45352.aspx</id><published>2008-03-17T13:27:00Z</published><updated>2008-03-17T13:27:00Z</updated><content type="html">&lt;p&gt;
    Some activities on any operating system fall into that category of "should be extraordinarily
    simple, and yet is full of the sort of pitfalls that cause headaches, confusion
    and (at least in my case) bouts of cursing and ranting".&amp;nbsp;&lt;/p&gt;
&lt;p&gt;
    My favourite of the moment is a simple security task: authenticating credentials
    provided by the user to ensure they are valid; and detecting programmatically if
    an authenticated user is an administrator. The fun-inducing caveat: this code has
    to work on Windows 2000, XP and Vista.&lt;/p&gt;
&lt;p&gt;
    I'll give a few code examples in this article. A couple of caveats: firstly, none
    but the last will be the complete and correct solution, so quick cutting and pasting
    may be inadvisable. Secondly, the code is C# and will assume the existence of a
    class called NativeSecurityApis in which all the relevant native API declarations
    are located. For those needing to create such a class, I recommend the pinvoke.net
    website.&lt;/p&gt;
&lt;p&gt;
    So, let's pretend we know nothing about this task, and type appropriate phrases
    into Google. Most of the code samples and advice one will find explain how to check
    if the current user is an administrator. In .NET this is triviality itself:&lt;/p&gt;
&lt;font face="Courier New" size="2"&gt;&lt;p&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; public static bool IsUserAdmin()&lt;br&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; {&lt;br&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; return new WindowsPrincipal(WindowsIdentity.GetCurrent()).IsInRole(WindowsBuiltInRole.Administrator);&lt;br&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br&gt;
&lt;/p&gt;&lt;/font&gt;
&lt;p&gt;
    Which is splendid. Two problems here: we are checking the current user, not the
    user whose user and password we have to hand; and, this code doesn't work on Windows
    Vista. We'll come back to that later.
&lt;/p&gt;
&lt;p&gt;
    &lt;span&gt;The Simple Approach &lt;/span&gt;
&lt;/p&gt;
&lt;p&gt;
    Leaving aside the latter problem for the moment, let's focus on the former problem.
    Given a username and password, how do we authenticate that user?&lt;/p&gt;
&lt;p&gt;
    Google again provides. We call the LogonUser() API. This takes a user name, domain,
    and password, and returns a handle to a token representing that user; or, if the
    login failed, an error. Super. Then we have a choice: we can either temporarily
    "become" that user via impersonation, and then use the above to check whether "we"
    are an administrator, and then revert back to being ourselves; or we use the WindowsIdentity
    constructor which takes the sort of token we have just acquired, and check that
    user. Impersonation is a useful thing to know about, but strictly speaking isn't
    relevant here. So we should take the second route.
&lt;/p&gt;
&lt;p&gt;
    Slight obstacles: firstly, how do we robustly turn a user-supplied username and
    password into the triple required by LogonUser: user name, domain, password. Secondly,
    LogonUser is not provided by .NET, so we have to PInvoke our way to the underlying
    API. Thirdly, it's badly implemented on Windows 2000, such that you have to more
    or less be SYSTEM in order for it to ever work, and is therefore useless for this
    purpose.&lt;/p&gt;
&lt;p&gt;
    Let's leave the latter two aside (again) for the moment, and press on. So let's
    imagine we've asked the user for a username and password. The user may have supplied
    a username in one of these formats:&lt;/p&gt;
&lt;font face="Courier New" size="2"&gt;&lt;p&gt;
    "UserName" (unqualified; local user name)&lt;br&gt;
    "DOMAIN\UserName" (old style)&lt;br&gt;
    "UserName@DOMAIN" (so called "UPN format")&lt;/p&gt;&lt;/font&gt;
&lt;p&gt;
    Can we just supply all of these these to LogonUser, with a null domain string, and
    have it work? I'll give you a clue: no. So, which of these can we just supply these
    to LogonUser? Well, number three is fine. As the API documentation suggests, UPN
    fomat is accepted if you specify a doman of null. The others aren't supported. What,
    you say, supplying "UserName" on its own is not allowed? That's right. In the world
    of Windows Security, the domain for the local machine can be expressed as ".", but
    not as null. And in the second case, you have to manually strip the domain out yourself
    and pass it in as a separate parameter, as LogonUser isn't bright enough to do this
    for you.&lt;/p&gt;
&lt;p&gt;
    Another incidental bit of fun is that if you are, as I was, doing all this in an
    installer which also has to install a Windows Service using .NET's AssemblyInstaller
    classes, you'd better steer clear of UPN format, as usernames not in "DOMAIN\UserName"
    format will cause exceptions courtesy of tht component. Fun!
&lt;/p&gt;
&lt;p&gt;
    So either way, time to write some highly trivial but irritating code to split out
    a (username,password) tuple into a (username,domain,password) tuple.&lt;/p&gt;
&lt;font face="Courier New" size="2"&gt;&lt;p&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; public static string GetDomain(ref string
    userName, bool includeUPNFormat)&lt;br&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; {&lt;br&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; string[] domainAndUser
    = userName.Split(new char[] { '\\' });&lt;br&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; if (domainAndUser.Length
    &amp;gt; 2)&lt;br&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;
    throw new ArgumentException("Username format incorrect.");&lt;br&gt;
    &lt;br&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; string domain
    = null;&lt;br&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; if (domainAndUser.Length
    == 2)&lt;br&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; {&lt;br&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;
    domain = domainAndUser[0];&lt;br&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;
    userName = domainAndUser[1];&lt;br&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; else if (includeUPNFormat)&lt;br&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; {&lt;br&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;
    domainAndUser = userName.Split(new char[] { '@' });&lt;br&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;
    if (domainAndUser.Length &amp;gt; 2)&lt;br&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;
    throw new ArgumentException("Username format incorrect");&lt;br&gt;
    &lt;br&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;
    if (domainAndUser.Length == 2)&lt;br&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;
    {&lt;br&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;
    domain = domainAndUser[1];&lt;br&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;
    userName = domainAndUser[0];&lt;br&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;
    }&lt;br&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br&gt;
    &lt;br&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; if (domain ==
    null)&lt;br&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;
    domain = ".";&lt;br&gt;
    &lt;br&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; return domain;&lt;br&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; }
    &lt;br&gt;
&lt;/p&gt;&lt;/font&gt;
&lt;p&gt;
    That little bit of fun over with, we can now call LogonUser(). If the credentials
    are valid we get a token back. If they're not, we get an error. Super.&lt;/p&gt;
&lt;p&gt;
    Then it only remains to check whether the user corresponding to our nice token is
    an administrator, as already described; and our job is done.&amp;nbsp;&lt;/p&gt;
&lt;font face="Courier New" size="2"&gt;&lt;p&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; public static bool IsUserAdmin(string
    userName, string password)&lt;br&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; {&lt;br&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; string name =
    userName;&lt;br&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; string domain
    = GetDomain(ref name, true);&lt;br&gt;
    &lt;br&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; return IsUserAdmin(name,
    domain, password);&lt;br&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br&gt;
    &lt;br&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; public static bool IsUserAdmin(string
    userName, string domain, string password)&lt;br&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; {&lt;br&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; IntPtr hToken;&lt;br&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; if (NativeSecurityApis.LogonUser(userName,
    domain, password, NativeSecurityApis.LOGON32_LOGON_INTERACTIVE, NativeSecurityApis.LOGON32_PROVIDER_DEFAULT,
    out hToken))&lt;br&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; {&lt;br&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;
    try&lt;br&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;
    {&lt;br&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;
    return new WindowsPrincipal(new WindowsIdentity(hToken)).IsInRole(WindowsBuiltInRole.Administrator);&lt;br&gt;
    &amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;
    }&lt;br&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;
    finally&lt;br&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;
    {&lt;br&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;
    NativeSecurityApis.CloseHandle(hToken);&lt;br&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;
    }&lt;br&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; else&lt;br&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; {&lt;br&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;
    throw new Win32Exception(); // last error&lt;br&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br&gt;
    &amp;nbsp;&lt;/p&gt;&lt;/font&gt;
&lt;p&gt;
    Coping with Windows 2000&lt;/p&gt;
&lt;p&gt;
    Now as noted previously, if your code has to work on Win2K, then your headaches
    aren't quite over. On Win2K, LogonUser is implemented via the low level API call
    LsaLogonUser, the user owning the calling process is required to have a grubby privilege
    called SeTcbPrivilege, otherwise known as "Act as part of the operating system".
    This is because LsaLogonUser allows some rather nasty things to be done other than
    just checking credentials. Very few Windows processes (those running as SYSTEM)
    are likely to have this privilege; everyone else doesn', for obvious security reasons,
    and so therefore can't call LogonUser.&lt;/p&gt;
&lt;p&gt;
    Microsoft's suggested workaround is to use SSPI authentication instead of calling
    LogonUser. This is a very protracted client/server authentication approach designed
    for authenticating users remotely (such as over a SQL Server connection which uses
    "Windows Authentication"). For once, it's quite a lot of grubby native code to call,
    but in .NET 2.0, the lovely NegotiateStream class was added which is capable of
    executing the right manoevres for us.&lt;/p&gt;&lt;p&gt;One may ask why, if SSPI authentication works on Windows 2000 and above, we shouldn't just always use it instead of LogonUser? Well, one argument is because of its particular behaviour w.r.t. the "Guest" account on Windows XP and above. As noted in http://support.microsoft.com/default.aspx?scid=kb;EN-US;180548 SSPI may always try and logon as "Guest", or indeed do no authentication at all and just claim to have logged in, depending on registry settings. This would make our attempt to validate user credentials rather academic. SSPI should therefore be a fallback approach, not a replacement for calling LogonUser where we can. &lt;/p&gt;
&lt;p&gt;
    Now I've seen a code example bandied around the internet which purports to be "the"
    way to get this to work, but I found it had several problems. Firstly it wasn't
    reliably callable more than once; especially if an authentication attempt actually
    failed. If you're calling from an application which has any kind of UI, this is
    problematic. Secondly, its use of asynchronous calls meant that it had a nasty race
    condition which made itself particularly manifest on successive authentication attempts.
    Here's a fixed up version which doesn't exhibit these problems:&lt;/p&gt;
&lt;font face="Courier New" size="2"&gt;&lt;p&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp; internal class Win32SSPI&lt;br&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp; {&lt;br&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; private static readonly TcpListener tcpListener
    = new TcpListener(IPAddress.Loopback, 0);&lt;br&gt;
    &lt;br&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; static Win32SSPI()&lt;br&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; {&lt;br&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; tcpListener.Start();&lt;br&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br&gt;
    &lt;br&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; /// &amp;lt;summary&amp;gt;&lt;br&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; /// Logon user using SSPI authentication.&lt;br&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; /// &amp;lt;/summary&amp;gt;&lt;br&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; /// &amp;lt;param name="userName"&amp;gt;The
    username (without domain qualifications, so no DOMAIN\user or user@DOMAIN here)&amp;lt;/param&amp;gt;&lt;br&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; /// &amp;lt;param name="domain"&amp;gt;The domain
    name (or ".")&amp;lt;/param&amp;gt;&lt;br&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; /// &amp;lt;param name="password"&amp;gt;The
    password.&amp;lt;/param&amp;gt;&lt;br&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; /// &amp;lt;returns&amp;gt;A valid WindowsPricipal.
    Throws an exception on failure.&amp;lt;/returns&amp;gt;&lt;br&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; public static WindowsPrincipal LogonUser(string
    userName, string domain, string password)&lt;br&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; {&lt;br&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; try&lt;br&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; {&lt;br&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;
    // need a full duplex stream - loopback is easiest way to get that&lt;br&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;
    WindowsIdentity id = null;&lt;br&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;
    AutoResetEvent waitEvent = new AutoResetEvent(false);&lt;br&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;
    IAsyncResult result = tcpListener.BeginAcceptTcpClient(delegate(IAsyncResult asyncResult)&lt;br&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;
    {&lt;br&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;
    try&lt;br&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;
    {&lt;br&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;
    using (NegotiateStream serverSide = new NegotiateStream(&lt;br&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;
    tcpListener.EndAcceptTcpClient(asyncResult).GetStream()))&lt;br&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;
    {&lt;br&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;
    serverSide.AuthenticateAsServer(CredentialCache.DefaultNetworkCredentials,&lt;br&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;
    ProtectionLevel.None, TokenImpersonationLevel.Impersonation);&lt;br&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;
    id = (WindowsIdentity)serverSide.RemoteIdentity;&lt;br&gt;
    &lt;br&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;
    }&lt;br&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;
    }&lt;br&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;
    catch (Exception)&lt;br&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;
    {&lt;br&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;
    // ack.&lt;br&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;
    }&lt;br&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;
    finally&lt;br&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;
    {&lt;br&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;
    waitEvent.Set();&lt;br&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;
    }&lt;br&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;
    }, null);&lt;br&gt;
    &lt;br&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;
    using (NegotiateStream clientSide = new NegotiateStream(new TcpClient("localhost",&lt;br&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;
    ((IPEndPoint)tcpListener.LocalEndpoint).Port).GetStream()))&lt;br&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;
    {&lt;br&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;
    clientSide.AuthenticateAsClient(new NetworkCredential(userName, password, domain),&lt;br&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;
    "", ProtectionLevel.None, TokenImpersonationLevel.Impersonation);&lt;br&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;
    }&lt;br&gt;
    &lt;br&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;
    waitEvent.WaitOne();&lt;br&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;
    waitEvent.Close();&lt;br&gt;
    &lt;br&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;
    if (id != null)&lt;br&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;
    return new WindowsPrincipal(id);&lt;br&gt;
    &lt;br&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;
    throw new LogonException("Cannot authenticate user using SSPI.");&lt;br&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; catch (Exception
    ex)&lt;br&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; {&lt;br&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;
    throw new LogonException("Cannot authenticate user using SSPI.", ex);&lt;br&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br&gt;
    &lt;br&gt;
&lt;/p&gt;&lt;/font&gt;
&lt;p&gt;
    So, now we can adjust our IsUserAdmin() function to try this approach if LogonUser
    fails. Since it will fail on Windows 2000, this gives us a working fallback position
    without having to write any "what OS version is this" type code (which is notoriously
    a bad idea).&lt;br&gt;
&lt;/p&gt;
&lt;font face="Courier New" size="2"&gt;&lt;p&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; public static bool IsUserAdmin(string
    userName, string domain, string password)&lt;br&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; {&lt;br&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; IntPtr hToken;&lt;br&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; if (NativeSecurityApis.LogonUser(userName,
    domain, password, NativeSecurityApis.LOGON32_LOGON_INTERACTIVE, NativeSecurityApis.LOGON32_PROVIDER_DEFAULT,
    out hToken))&lt;br&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; {&lt;br&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;
    try&lt;br&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;
    {&lt;br&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;
    return new WindowsPrincipal(new WindowsIdentity(hToken)).IsInRole(WindowsBuiltInRole.Administrator);&lt;br&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;
    }&lt;br&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;
    finally&lt;br&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;
    {&lt;br&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;
    NativeSecurityApis.CloseHandle(hToken);&lt;br&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;
    }&lt;br&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; else&lt;br&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; {&lt;br&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;
    try&lt;br&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;
    {&lt;br&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;
    WindowsPrincipal principal = Win32SSPI.LogonUser(userName, domain, password);&lt;br&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;
    return prinicipal.IsInRole(WindowsBuiltInRole.Administrator);&lt;br&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;
    }&lt;br&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;
    catch (Exception ex)&lt;br&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;
    {&lt;br&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;
    throw new LogonException(ex.Message, ex);&lt;br&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;
    }&lt;br&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br&gt;
&lt;/p&gt;&lt;/font&gt;
&lt;p&gt;UAC, one can log
    Coping with Windows Vista&lt;/p&gt;
&lt;p&gt;
    And now we come to the real fun. Windows Vista introduced UAC (User Account Control)
    for reasons which are outside the scope of this article. Under UAC, one can log
    in as a user who is, in theory, an administrator, but not actually have administrator
    privileges until they are absolutely required (because some application is about
    to do something which needs them). Only then is the user "elevated" to having full
    administrative rights.&lt;/p&gt;
&lt;p&gt;
    This has a consequence for our authentication code: it no longer works. On Vista,&amp;nbsp;
    calling WindowsPrincipal.IsInRole(WindowsBuiltInRole.Administrator) will return
    false unless the user is currently elevated to proper administrator status. If we've
    just come from a LogonUser call, they won't be so elevated; nor do we really want
    to attempt to elevate them (even if this were feasible, which it really isn't).
    So how do we check if the user is, in theory, an administrator, despite the fact
    that they aren't currently administrating?&lt;/p&gt;
&lt;p&gt;
    Google provides a bunch of code snippets resembling the following:&lt;/p&gt;
&lt;font face="Courier New" size="2"&gt;&lt;p&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;
    string sddlAdmin = "S-1-5-32-544";&amp;nbsp; //Sid of administrators group&lt;br&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;
    IdentityReference adminSid = new SecurityIdentifier(sddlAdmin);&lt;br&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;
    if (principal.Identity is WindowsIdentity &amp;amp;&amp;amp;&lt;br&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;
    ((WindowsIdentity)principal.Identity).Groups.Contains(adminSid))&lt;br&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;
    {&lt;br&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;
    return true;&lt;br&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;
    }&lt;br&gt;
    &amp;nbsp;&lt;/p&gt;&lt;/font&gt;
&lt;p&gt;
    Which is excellent, except for the fact that this doesn't have a snowball's chance
    in hell of working either. This code is simply a protracted way of calling the same
    APIs as WindowsPrincipal.IsInRole(). Doomed to failure.&lt;/p&gt;
&lt;p&gt;
    There are native APIs which look like they should help: IsUserAdmin(), previously
    an undocumented and unsupported internal API, is now a documented but very-likely-to-become-obsolete
    public API. Likewise the CheckTokenMembership() API which can check if a user is
    an administrator, as follows:&lt;/p&gt;
&lt;font face="Courier New" size="2"&gt;&lt;p&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; public static bool IsImpersonationTokenUserAdmin(IntPtr
    hToken)&lt;br&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; {&lt;br&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; bool success;&lt;br&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; IntPtr pNTAuthority
    = Marshal.AllocHGlobal(Marshal.SizeOf(NativeSecurityApis.SID_IDENTIFIER_AUTHORITY.SECURITY_NT_AUTHORITY));&lt;br&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; Marshal.StructureToPtr(NativeSecurityApis.SID_IDENTIFIER_AUTHORITY.SECURITY_NT_AUTHORITY,
    pNTAuthority, false);&lt;br&gt;
    &lt;br&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; try&lt;br&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; {&lt;br&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;
    IntPtr pSidAdministratorsGroup;&lt;br&gt;
    &lt;br&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;
    success = NativeSecurityApis.AllocateAndInitializeSid(&lt;br&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;
    pNTAuthority,&lt;br&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;
    2,&lt;br&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;
    NativeSecurityApis.SECURITY_BUILTIN_DOMAIN_RID,&lt;br&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;
    NativeSecurityApis.DOMAIN_ALIAS_RID_ADMINS,&lt;br&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;
    0, 0, 0, 0, 0, 0,&lt;br&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;
    out pSidAdministratorsGroup);&lt;br&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;
    try&lt;br&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;
    {&lt;br&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;
    if (success)&lt;br&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;
    {&lt;br&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;
    if (!NativeSecurityApis.CheckTokenMembership(hToken, pSidAdministratorsGroup, out
    success))&lt;br&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;
    {&lt;br&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;
    success = false;&lt;br&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;
    }&lt;br&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;
    }&lt;br&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;
    }&lt;br&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;
    finally&lt;br&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;
    {&lt;br&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;
    NativeSecurityApis.FreeSid(pSidAdministratorsGroup);&lt;br&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;
    }&lt;br&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; finally&lt;br&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; {&lt;br&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;
    Marshal.FreeHGlobal(pNTAuthority);&lt;br&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br&gt;
    &lt;br&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; return success;&lt;br&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br&gt;
&lt;/p&gt;&lt;/font&gt;
&lt;p&gt;
    But this equally turns out to be damned all use. All routes are answering the same
    question: "is this user currently an administrator". Not, as we want, "does this
    user have the theoretical capacity to be an administrator". What to do?&lt;br&gt;
&lt;/p&gt;
&lt;p&gt;
    After much painful searching, I can across this article: http://www.microsoft.com/technet/technetmag/issues/2007/06/ACL/default.aspx
    which explains the changes to the Windows security APIs in Vista. I'd been asking
    myself "what's the difference between the token for an adminstrator under XP, and
    the token for an administrator under Vista?" And this article provides the answer.
    It all comes down to SIDs and attributes.&lt;/p&gt;
&lt;p&gt;
    What's a SID? Well, to quote from the Microsoft Knowledge base:&lt;/p&gt;
&lt;p&gt;
    "A security identifier (SID) is a unique value of variable length that is used to
    identify a security principal or security group in Windows operating systems. Well-known
    SIDs are a group of SIDs that identify generic users or generic groups. Their values
    remain constant across all operating systems." I'd add that SIDs have an obscure
    binary format, and a more readily readable string format.
    &lt;br&gt;
&lt;/p&gt;
&lt;p&gt;
    How is this relevant to us? Well, here I'll refer you to MSDN for a decent explanation
    - http://msdn2.microsoft.com/en-us/library/aa374862(VS.85).aspx - but briefly, a
    logged in user is assigned an access token, which contains a set of SIDs (security
    IDs) corresponding to the various groups of which that user is a member, and a set
    of privileges which the user has. Securable "things" such as files have access control
    lists (ACLs) which allow or deny various different SIDs access to the thing in question.
    When it needs to know if a user has permission on some object, Windows runs through
    these lists in tandem; as soon as it hits a relevant "deny" entry, or if it doesn't
    find any "allow" entries, then access is denied; otherwise, it's allowed.&lt;/p&gt;
&lt;p&gt;
    SIDs are usually accompanied by flags (known as attributes) in a SID_AND_ATTRIBUTES
    structure. Generally the SIDs associated with a user's access token are "positive"
    flags, if you like: they list groups of which that user is a member. Here, for example,
    is a dump of the SIDs I have when logged into my own XP machine, with their display
    names, SID strings, and corresponding attributes in text format:&lt;/p&gt;
&lt;font face="Courier New" size="2"&gt;&lt;p&gt;
    "FOO\Domain Users"&amp;nbsp; S-1-5-21-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxx Mandatory EnabledByDefault
    Enabled&lt;br&gt;
    "Everyone"&amp;nbsp; S-1-1-0 Mandatory EnabledByDefault Enabled&lt;br&gt;
    "MYMACHINE\Debugger Users"&amp;nbsp; S-1-5-21-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxx Mandatory
    EnabledByDefault Enabled&lt;br&gt;
    "BUILTIN\Administrators"&amp;nbsp; S-1-5-32-544 Mandatory EnabledByDefault Enabled Owner&lt;br&gt;
    "BUILTIN\Users"&amp;nbsp; S-1-5-32-545 Mandatory EnabledByDefault Enabled&lt;br&gt;
    "NT AUTHORITY\INTERACTIVE"&amp;nbsp; S-1-5-4 Mandatory EnabledByDefault Enabled&lt;br&gt;
    "NT AUTHORITY\Authenticated Users"&amp;nbsp; S-1-5-11 Mandatory EnabledByDefault Enabled&lt;br&gt;
    "LOCAL"&amp;nbsp; S-1-2-0 Mandatory EnabledByDefault Enabled&lt;br&gt;
    "FOO\SoftwareDeveloper"&amp;nbsp; S-1-5-21-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxx Mandatory
    EnabledByDefault Enabled&lt;br&gt;
    "FOO\UserOfVirtualMachinesInSomeWay"&amp;nbsp; S-1-5-21-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxx
    Mandatory EnabledByDefault Enabled&lt;br&gt;
    "BAR\LargeAdministrativeCheese"&amp;nbsp; S-1-5-21-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxx
    Mandatory EnabledByDefault Enabled&lt;br&gt;
&lt;/p&gt;&lt;/font&gt;
&lt;p&gt;
    Where you see "xxxx", replace with an arbitrary sequence of numbers particular to
    my current domain.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;
    So you can see I'm a user on a domain; I'm an administrator; I've been properly
    authenticated; I'm a member of a group of people who write software for a living;
    and I also log on to virtual machines, and can administer the BAR domain with impunity.&lt;/p&gt;
&lt;p&gt;
    If you want to try this on your own machine, you can download the "whoami" tool
    which is part of the "Windows XP Service Pack 2 Support Tools" from Microsoft. It
    gives you all of the above except for the permission flags.
    &lt;br&gt;
&lt;/p&gt;
&lt;p&gt;
    To see the same on Windows Vista, we can run the bult in "whoami" command, which
    produces rather similar output. The key difference is as follows:&lt;/p&gt;
&lt;font face="Courier New" size="2"&gt;&lt;p&gt;
    "BUILTIN\Administrators"&amp;nbsp; S-1-5-32-544 UseForDenyOnly&amp;nbsp;
&lt;/p&gt;&lt;/font&gt;
&lt;p&gt;
    This is the essential difference between an "un-elevated" administrator on Vista,
    and an administrator on 2000 and XP. Previously the administrator had the BUILTIN\Administrators
    SID enabled. In Vista pre elevation, administrators have the BUILTIN\Administrators
    SID but set to "deny only". Hence the lack of actual administrative powers until
    elevated.&lt;/p&gt;
&lt;p&gt;
    This proves to be about the only way we can tell an un-elevated administrator on
    Vista from a standard user, who will not exhibit the BUILTIN\Administrators SID
    at all.&lt;/p&gt;
&lt;p&gt;
    So we have something we can check on Vista. How do we go about it? Well, we end
    up rewriting our existing IsUserAdmin(username,domain,password) method as follows:&lt;/p&gt;
&lt;font face="Courier New" size="2"&gt;&lt;p&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; public static bool IsUserAdmin(string
    userName, string domain, string password)&lt;br&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; {&lt;br&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; IntPtr hToken;&lt;br&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; if (NativeSecurityApis.LogonUser(userName,
    domain, password, NativeSecurityApis.LOGON32_LOGON_INTERACTIVE, NativeSecurityApis.LOGON32_PROVIDER_DEFAULT,
    out hToken))&lt;br&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; {&lt;br&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;
    try&lt;br&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;
    {&lt;br&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;
    return IsUserAdmin(hToken);&lt;br&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;
    }&lt;br&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;
    finally&lt;br&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;
    {&lt;br&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;
    NativeSecurityApis.CloseHandle(hToken);&lt;br&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;
    }&lt;br&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; else&lt;br&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; {&lt;br&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;
    try&lt;br&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;
    {&lt;br&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;
    WindowsPrincipal principal = Win32SSPI.LogonUser(userName, domain, password);&lt;br&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;
    return IsUserAdmin(principal);&lt;br&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;
    }&lt;br&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;
    catch (Exception ex)&lt;br&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;
    {&lt;br&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;
    throw new LogonException(ex.Message, ex);&lt;br&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;
    }&lt;br&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br&gt;
&lt;/p&gt;&lt;/font&gt;
&lt;p&gt;
    We'll add another helper overload for the case where, courtesy of the SSPI authentication
    for Windows 2000, we have a WindowsPrincipal rather than a token:&lt;/p&gt;
&lt;font face="Courier New" size="2"&gt;&lt;p&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; public static bool IsUserAdmin(WindowsPrincipal
    principal)&lt;br&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; {&lt;br&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; WindowsIdentity
    identity = principal.Identity as WindowsIdentity;&lt;br&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; return IsUserAdmin(identity.Token);&lt;br&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;/p&gt;&lt;/font&gt;
&lt;p&gt;
    &amp;nbsp;And we can then write the function underlying both of these overloads:&lt;/p&gt;
&lt;font face="Courier New" size="2"&gt;&lt;p&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; public static bool IsUserAdmin(IntPtr
    hToken)&lt;br&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; {&lt;br&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; string adminSid
    = NativeSecurityApis.STRING_SID_BUILTIN_ADMINISTRATORS; // "S-1-5-32-544"&lt;br&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; IntPtr pTokenGroups
    = GetTokenGroups(hToken);&lt;br&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; try&lt;br&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; {&lt;br&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;
    foreach (SidAndAttributes sid in GetTokenGroupStringSids(pTokenGroups))&lt;br&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;
    {&lt;br&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;
    if (StringComparer.InvariantCultureIgnoreCase.Compare(sid.SidText, adminSid) ==
    0)&lt;br&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;
    {&lt;br&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;
    if (sid.IsGroupEnabled /* what we'd normally expect */||&lt;br&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;
    sid.IsGroupUseForDenyOnly /* Vista: present but is deny only */ )&lt;br&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;
    {&lt;br&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;
    return true;&lt;br&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;
    }&lt;br&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;
    }&lt;br&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;
    }&lt;br&gt;
    &lt;br&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;
    return false; // no admin SID, or not enabled and not deny only.&lt;br&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; finally&lt;br&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; {&lt;br&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;
    FreeTokenGroups(pTokenGroups);&lt;br&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;/p&gt;&lt;/font&gt;
&lt;p&gt;
    &amp;nbsp;This function uses a lot of helper mojo which we haven't defined yet, but
    demonstrates the basic algorithm. We acquire the set of user groups associated with
    the authenticated user's token; we iterate through them looking for the BUILTIN\Administrator
    SID; and we count the user as an administrator if the SID's attributes are either
    "enabled" (2000, XP) or "deny only" (Vista).&lt;/p&gt;
&lt;p&gt;
    To get the token's groups, we employ the following methods. We call the GetTokenInformation()
    API which can pull out lots of interesting things about a user's token.&lt;/p&gt;
&lt;font face="Courier New" size="2"&gt;&lt;p&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; private static IntPtr GetTokenGroups(IntPtr
    hToken)&lt;br&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; {&lt;br&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; uint dwSize =
    0;&lt;br&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; if (!NativeSecurityApis.GetTokenInformation(hToken,
    NativeSecurityApis.TOKEN_INFORMATION_CLASS.TokenGroups, IntPtr.Zero, dwSize, out
    dwSize))&lt;br&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; {&lt;br&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;
    if (Marshal.GetLastWin32Error() != NativeSecurityApis.ERROR_INSUFFICIENT_BUFFER)&lt;br&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;
    throw new Win32Exception();&lt;br&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br&gt;
    &lt;br&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; IntPtr pTokenGroups
    = Marshal.AllocHGlobal((int)dwSize);&lt;br&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; try&lt;br&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; {&lt;br&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;
    if (!NativeSecurityApis.GetTokenInformation(hToken, NativeSecurityApis.TOKEN_INFORMATION_CLASS.TokenGroups,
    pTokenGroups, dwSize, out dwSize))&lt;br&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;
    throw new Win32Exception();&lt;br&gt;
    &lt;br&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;
    return pTokenGroups;&lt;br&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; catch (Exception)&lt;br&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; {&lt;br&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;
    Marshal.FreeHGlobal(pTokenGroups);&lt;br&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;
    throw;&lt;br&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br&gt;
    &lt;br&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; private static void FreeTokenGroups(IntPtr
    pTokenGroups)&lt;br&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; {&lt;br&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; Marshal.FreeHGlobal(pTokenGroups);&lt;br&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;/p&gt;&lt;/font&gt;
&lt;p&gt;
    Essentially we call GetTokenInformation() to find out how much memory we need to
    allocate for a copy of the token group information; then we allocate said memory
    and call the API again to fetch the information.&lt;/p&gt;
&lt;p&gt;
    The token groups we've retrieved are defined in the platform SDK as follows:&lt;/p&gt;
&lt;pre class="code" id="ctl00_rs1_mainContentContainer_ctl03"&gt;typedef struct _TOKEN_GROUPS {&lt;br&gt;  DWORD GroupCount;&lt;br&gt;  SID_AND_ATTRIBUTES Groups[ANYSIZE_ARRAY];&lt;br&gt;} TOKEN_GROUPS, &lt;br&gt; *PTOKEN_GROUPS;&lt;/pre&gt;
&lt;pre class="code" id="ctl00_rs1_mainContentContainer_ctl02"&gt;typedef struct _SID_AND_ATTRIBUTES {&lt;br&gt;  PSID Sid;&lt;br&gt;  DWORD Attributes;&lt;br&gt;} SID_AND_ATTRIBUTES, &lt;br&gt; *PSID_AND_ATTRIBUTES;&lt;/pre&gt;
&lt;p&gt;
    So we have a memory block which contains a group count, then a SID for each group
    with an accompanying set of attribute flags. We can treat the contents of the SID
    as a black box, since all we need to do is to be able to compare these SIDs against
    the standard SID for BUILTIN\Administrators; if we find a match, we then ensure
    that the SID's attributes include either SE_GROUP_ENABLED or SE_GROUP_USE_FOR_DENY_ONLY.&lt;/p&gt;
&lt;p&gt;
    A caveat in dealing with the above is that in the platform SDK these structures
    are not explicitly packed. The compiler will therefore align each field on the nearest
    n byte boundary where n is 4 on 32 bit systems and 8 on 64 bit systems. Consequently
    on 64 bit systems, their layout in memory equivalent to the following:&amp;nbsp;&lt;/p&gt;
&lt;pre class="code" id="ctl00_rs1_mainContentContainer_ctl03"&gt;#pragma pack(push,1) &lt;br&gt;typedef struct _TOKEN_GROUPS_64 {&lt;br&gt;  DWORD GroupCount;&lt;br&gt;  DWORD __Unused;&lt;br&gt;  SID_AND_ATTRIBUTES_64 Groups[ANYSIZE_ARRAY];&lt;br&gt;} TOKEN_GROUPS, &lt;br&gt; *PTOKEN_GROUPS;&lt;/pre&gt;
&lt;pre class="code" id="ctl00_rs1_mainContentContainer_ctl02"&gt;typedef struct _SID_AND_ATTRIBUTES_64 {&lt;br&gt;  PSID Sid;&lt;br&gt;  DWORD Attributes;&lt;br&gt;  DWORD __Unused;&lt;br&gt;} SID_AND_ATTRIBUTES, &lt;br&gt; *PSID_AND_ATTRIBUTES;&lt;br&gt;#pragma pack(pop)&lt;/pre&gt;
&lt;p&gt;
    Unfortunately there's no magic we can insert into a C# structure definition to say
    "Align this structure in the same way it would be aligned in C++ by default". So
    when reading this information I chose to just read it in an IntPtr/Int32 at a time
    using the Marshal class. One could equally define multiple versions of structures
    with different packing and switch between them based on platform/sizeof(IntPtr).&lt;/p&gt;
&lt;p&gt;
    I created the following simple wrapper to hold a SID and its attributes:&lt;/p&gt;
&lt;font face="Courier New" size="2"&gt;&lt;p&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp; internal class SidAndAttributes&lt;br&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp; {&lt;br&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; private readonly string m_SidText;&lt;br&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; private readonly int m_Attributes;&lt;br&gt;
    &lt;br&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; public SidAndAttributes(IntPtr pSid,
    int attributes)&lt;br&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; {&lt;br&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; IntPtr pString;&lt;br&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; if (!NativeSecurityApis.ConvertSidToStringSid(pSid,
    out pString))&lt;br&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;
    throw new Win32Exception(); // last error&lt;br&gt;
    &lt;br&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; m_SidText = Marshal.PtrToStringAuto(pString);&lt;br&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; NativeSecurityApis.LocalFree(pString);&lt;br&gt;
    &lt;br&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; m_Attributes
    = attributes;&lt;br&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br&gt;
    &lt;br&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; public string SidText&lt;br&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; {&lt;br&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; get { return
    m_SidText; }&lt;br&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br&gt;
    &lt;br&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; public int Attributes&lt;br&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; {&lt;br&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; get { return
    m_Attributes; }&lt;br&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br&gt;
    &lt;br&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; public bool IsGroupEnabled&lt;br&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; {&lt;br&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; get { return
    (m_Attributes &amp;amp; NativeSecurityApis.SE_GROUP_ENABLED) == NativeSecurityApis.SE_GROUP_ENABLED;
    }&lt;br&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br&gt;
    &lt;br&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; public bool IsGroupUseForDenyOnly&lt;br&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; {&lt;br&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; get { return
    (m_Attributes &amp;amp; NativeSecurityApis.SE_GROUP_USE_FOR_DENY_ONLY) == NativeSecurityApis.SE_GROUP_USE_FOR_DENY_ONLY;
    }&lt;br&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br&gt;
    &amp;nbsp; &amp;nbsp; }&lt;/p&gt;&lt;/font&gt;
&lt;p&gt;
    &amp;nbsp;
    &lt;br&gt;
    And used the following method to traverse the TOKEN_GROUPS structure and read its
    array of SID_AND_ATTRIBUTES structures into an IEnumerable&amp;lt;SidAndAttributes&amp;gt;:&lt;br&gt;
&lt;/p&gt;
&lt;font face="Courier New" size="2"&gt;&lt;p&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; private static IEnumerable&amp;lt;SidAndAttributes&amp;gt;
    GetTokenGroupStringSids(IntPtr pTokenGroups)&lt;br&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; {&lt;br&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; List&amp;lt;SidAndAttributes&amp;gt;
    list = new List&amp;lt;SidAndAttributes&amp;gt;();&lt;br&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; if (pTokenGroups
    == IntPtr.Zero)&lt;br&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;
    return list;&lt;br&gt;
    &lt;br&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; int groupCount
    = Marshal.ReadInt32(pTokenGroups, 0);&lt;br&gt;
    &lt;br&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; // read in SID_AND_ATTRIBUTES
    items.&lt;br&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; // Due to the
    way these are packed, sizeof(SID_AND_ATTRIBUTES) and sizeof(TOKEN_GROUPS) varies
    &lt;br&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; // depending
    on whether the platform is 32 or 64 bit.&lt;br&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; //&lt;br&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; long sizeof_Int32
    = Marshal.SizeOf(typeof(Int32));&lt;br&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; long sizeof_IntPtr
    = Marshal.SizeOf(typeof(IntPtr));&lt;br&gt;
    &lt;br&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; long offset =
    (long)pTokenGroups;&lt;br&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; offset += Marshal.SizeOf(typeof(Int32));&lt;br&gt;
    &lt;br&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; if (Marshal.SizeOf(typeof(IntPtr))
    != Marshal.SizeOf(typeof(Int32)))&lt;br&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;
    offset += sizeof_Int32; // extra padding on Win64&lt;br&gt;
    &lt;br&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; for (int iGroup
    = 0; iGroup &amp;lt; groupCount; iGroup++)&lt;br&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; {&lt;br&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;
    IntPtr pSid = Marshal.ReadIntPtr((IntPtr)offset);&lt;br&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;
    offset += sizeof_IntPtr;&lt;br&gt;
    &lt;br&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;
    int attributes = Marshal.ReadInt32((IntPtr)offset);&lt;br&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;
    offset += sizeof_Int32;&lt;br&gt;
    &lt;br&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;
    if (Marshal.SizeOf(typeof(IntPtr)) != Marshal.SizeOf(typeof(Int32)))&lt;br&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;
    offset += sizeof_Int32; // extra padding on Win64&lt;br&gt;
    &lt;br&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;
    SidAndAttributes item = new SidAndAttributes(pSid, attributes);&lt;br&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;
    list.Add(item);&lt;br&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br&gt;
    &lt;br&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; return list;&lt;br&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br&gt;
    &amp;nbsp;&lt;/p&gt;&lt;/font&gt;
&lt;p&gt;
    &amp;nbsp;And finally, we're done.&lt;/p&gt;
&lt;p&gt;
    Conclusion&lt;/p&gt;
&lt;p&gt;I don't see any reason why this entire task can't be made courtesy of a single API call. On Windows XP, it requires a handfull of calls; on Windows 2000, a large sequence (hidden by C#, thankfully) of mandatory but rather tangential calls; on Windows Vista, less than a dozen API calls to check something which is in fact little more than an artifact of the implementation of UAC, there being no obvious other route to take. In any case, there is no reason why information on how to actually validate user credentials, and check whether that user account has administrator privileges, should be difficult to track down or comprehend. Hopefully this article somewhat addresses that issue.&lt;/p&gt;&lt;p&gt;&lt;br&gt;&lt;/p&gt;&lt;img src="http://www.simple-talk.com/community/aggbug.aspx?PostID=45352" width="1" height="1"&gt;</content><author><name>Dan J Archer</name><uri>http://www.simple-talk.com/community/user/Profile.aspx?UserID=2114</uri></author></entry><entry><title>Step up to Red Alert!</title><link rel="alternate" type="text/html" href="http://www.simple-talk.com/community/blogs/dana/archive/2008/02/05/43648.aspx" /><id>http://www.simple-talk.com/community/blogs/dana/archive/2008/02/05/43648.aspx</id><published>2008-02-05T13:12:00Z</published><updated>2008-02-05T13:12:00Z</updated><content type="html">&lt;P&gt;"Step up to Red Alert!"&lt;BR&gt;"Sir, are you absolutely sure? It does mean changing the bulb..."&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; -- Red Dwarf&amp;nbsp;&lt;/P&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;Computers are stupid. This is a well established fact, which has been en-harped on by much greater and perspicacious authorities than I. They, that is, computers, tend to blindly and relentlessly do what they're told. Given conflicting, asymptotic or just plain dumb instructions the machine will blindly and relentlessly attempt to execute said instructions with blindingly relentless blindness, and without relenting. &lt;/P&gt;
&lt;P&gt;But despite this we have come to rely on them utterly. Many centuries ago, a lone imbicile might work away in his hut for many, many years, perchance to dream of such levels of productive, innovative dullardry. Even the Industrial Revolution didn't come close, even having conceived the idea that if one were to shepherds right-thinking imbiciles together from a large area into a sort of pen, or congregation, then their productivity would greatly increase. Certainly the noise level and the smell increased, and these were signs that the humours were ticking over nicely, thereby giving the industrialist a warm feeling of success and busy-ness, ne business. But, alack, no cluster of imbiciles savant, however skilled, can replace a computer's ability for quickly and unthinkingly doing as instructed, even if it takes said machine into the valley, so to speak, of the shadow of death. Wherein lie monsterous hazards known by obscure, demonic names: O'exate'oofor'ooofyve, Ess'teegee'eee'filenotfound, Bee'ess'o'dy. &lt;/P&gt;
&lt;P&gt;Which almost brings me to my point, and although I hesitate to arrive so alacritously having only taken a more or less single and relatively brief tangent, this may be universally judged to be a good thing. Computers are stupid, and given the chance will rush headlong into the abyss without so much as a by your leave. And since they're capable of making mistakes at such high velocity (bing-bing-bing-bing, 3.5 billion mistakes per second without touching the sides) it can be a hard task to keep them on the straight and narrow. To shepherd them, as it were, through the valley. To catch the cogs flying from the difference engine before they decapitate an innocent passer by.&lt;/P&gt;
&lt;P&gt;There are many professionals whose job it is to do just that, for SQL Servers. And it can be a difficult and thankless task. DBAs, and other professions sharing similar responsibilities, have to make sure that the SQL Servers in their charge are rattling along in &lt;A title="maximum Maytag mode" href="http://everything2.com/index.pl?node_id=460136" target=_blank&gt;maximum Maytag mode&lt;/A&gt; without killing anyone and without too many bits falling off in the process. And this, usually, "at the same time" as they are performing their other duties, which already consume a full working day's worth of effort.&lt;/P&gt;
&lt;P&gt;SQL Server itself isn't much help. It's quite happy to answer questions as to how well it's doing in, say, executing overnight jobs which were backing up important databases. But, for preference, it would rather play 20 questions with you ("Did this job go well?", "No", "Was it a backup?", "Yes", "Animal, vegetable or mineral?") than give you a quick summary of what went well, or more importantly what went badly, in the last N hours. And should you be faced with a small room of these things, let alone an enterprise, then your chances of extracting some sense from the ensemble each morning, just to make sure that no servers caught fire over night and that all your backups actually took place, are drastically reduced.&lt;/P&gt;
&lt;P&gt;Enter SQL Response. A new tool from Red Gate, and a continued foray into the area of tools focused towards DBAs, SQL Response is part watchman, part messenger and part coroner; part missile warning system and part CSI investigation team.&lt;/P&gt;
&lt;P&gt;Installed in a convenient nook from whence it can monitor nearby SQL Servers, SQL Response keeps a watchful eye on your SQL Servers looking for problems. Problems, as far as SQL Response is concerned, are things which are really likely to spoil your day as a DBA. A SQL Server which appears to be down is one obvious candidate. Jobs which have failed are another. Yet another is a disk drive looking very full. A blocking or deadlocked process. A job which worked, but took longer than expected. And so on.&lt;/P&gt;
&lt;P&gt;Being a Red Gate tool, SQL Response doesn't just cover the obvious cases. How about a job which missed its run because the SQL Agent is disabled? Or which should have run, but couldn't because it's scheduled to run every half hour and the previous run took 31 minutes? If a disk drive is full, then is it the system (Windows) drive, or a drive used by a SQ Server database? Is that SQL Server down, or did the login just fail? And is the Windows box itself down? &lt;/P&gt;
&lt;P&gt;More than just alerting you to the fact that the problem has occurred, SQL Response tries to help you find out why the problem occurred by looking at the "scene of the crime". So whilst it's firing off an e-mail to you telling you about the problem, it's simutaneously gathering up information on SQL performance, SQL connections,&amp;nbsp; running Windows processes. Even, if requested, the SQL code running around the time the problem occurred. SQL Response captures as much of this information as seems appropriate, together with as much information as possible on the problem itself, and files this bundle of fun as an "incident report". These incidents are available from within the SQL Response client application. If you have the client and your e-mail application installed on the same machine, you can even click the link inside all SQL Response e-mail alerts to jump straight into the application and view the incident report.&lt;/P&gt;
&lt;P&gt;Once inside SQL Response you can set about diagnostic the problem. Some problems have a simple remedy inside SQL Response itself - a failed job is easily restarted. Most problems, of course, require the seasoned eye of a DBA or other professional to diagnose. SQL Response provides all the information it can, like any good investigator. Leaving the professional to work their Holmsian powers of deduction with data at their fingertips.&amp;nbsp;&lt;/P&gt;
&lt;P&gt;SQL Response can also make recommendations. If your databases are in need of backing up, reindexing, or otherwise maintaining in key ways, SQL Response will do its best to point this out. &lt;/P&gt;
&lt;P&gt;Of course, it's all very well having a sturdy watchman nearby who will sound alarm bells when things go wrong. But there is such a thing as crying wolf. Computers being what they are, things go wrong all the time, and one doesn't necessarily want someone town crier throwback heckling you constantly about every little niggle. If you have a job which runs every 5 minutes, and which starts failing at 2am whilst you're happily convalescing, do you really want 72 "Job failed" e-mails sitting primly in your inbox when you arrive in the morning? And continuing to arrive, bing-bing-bing-bing, every 5 minutes until you fix the problem? &lt;/P&gt;
&lt;P&gt;So, naturally SQL Response will allow you to ignore future incidents of a particular type, either on a particular server or just generally. But more importantly, even when its monitoring is full steam ahead, it doesn't spam e-mails at you constantly. In any 24 hour period, SQL Response will send one e-mail when a particular problem first arises (say when job A on server B goes wrong), and another if it happens again. Since deathly silence isn't necessarily a vast improvement over crying wolf, SQL Response will continue to raise incidents in its own client UI, for every failure, collapsed down into a single row to avoid visual clutter. It just stops heckling you about them. Should you care about the 56th job failure more than the first or last, you can easily drill down and inspect the gritty details.&lt;/P&gt;
&lt;P&gt;However, much as I could easily sit here in a continuing pretence that this unspeakable waffle is a reasonable justification for my paycheck, I suspect the world would be better served through my cessation in doing so. As a closing thought, therefore, if you're a DBA or other professional who has to worry about the health and general well being of SQL Servers, why not &lt;A title="SQL Response 1.0 Beta" href="http://www.red-gate.com/products/SQL_Response/index.htm?utm_source=simpletalk&amp;amp;utm_medium=blog&amp;amp;utm_campaign=sqlresponsebeta"&gt;check out our beta&lt;/A&gt; at the below link. We'd love to hear &lt;A title="SQL Response Beta forum" href="http://www.red-gate.com/MessageBoard/viewforum.php?f=77"&gt;what you have to say&lt;/A&gt;.&lt;/P&gt;
&lt;P&gt;And perhaps you can avoid having to change the bulb.&amp;nbsp;&lt;/P&gt;
&lt;P&gt;&lt;A title="SQL Response 1.0 Beta" href="http://www.red-gate.com/products/SQL_Response/index.htm?utm_source=simpletalk&amp;amp;utm_medium=blog&amp;amp;utm_campaign=sqlresponsebeta"&gt;SQL Response 1.0 Beta&lt;/A&gt;&lt;BR&gt;&lt;/P&gt;&lt;img src="http://www.simple-talk.com/community/aggbug.aspx?PostID=43648" width="1" height="1"&gt;</content><author><name>Dan J Archer</name><uri>http://www.simple-talk.com/community/user/Profile.aspx?UserID=2114</uri></author></entry><entry><title>Strong Naming + Remoting = Noth'g but ye liveliest Awfulness</title><link rel="alternate" type="text/html" href="http://www.simple-talk.com/community/blogs/dana/archive/2007/08/30/35818.aspx" /><id>http://www.simple-talk.com/community/blogs/dana/archive/2007/08/30/35818.aspx</id><published>2007-08-30T10:51:00Z</published><updated>2007-08-30T10:51:00Z</updated><content type="html">&lt;P&gt;&lt;FONT size=2&gt;&lt;EM&gt;"What you sente, did not Worke, whether because of Any Thing miss'g, or because ye Wordes were not Righte from my Speak'g or yr Copy'g. I alone am at a Loss...Certainely, there was Noth'g but ye liveliest Awfulness in that which H. rais'd upp from What he cou'd gather onlie a part of."&lt;/EM&gt;&amp;nbsp;&amp;nbsp; -- &amp;nbsp;H P Lovecraft&lt;/FONT&gt;&lt;/P&gt;
&lt;P&gt;Strong naming assemblies has many advantages. My favourites:&lt;/P&gt;
&lt;UL&gt;
&lt;LI&gt;Trust. Users of the assembly can trust that it came from the signee, such as Red Gate Software, thanks to the public key cryptography involved.&lt;/LI&gt;
&lt;LI&gt;Tamper prevention. Users of the assembly can trust that nobody has tampered with it since the signee released it. The .NET runtime will not load assemblies whose signed hash doesn't match their current hash.&lt;/LI&gt;
&lt;LI&gt;Compatibility with the GAC. The global application cache only accepts strong named assemblies.&lt;/LI&gt;&lt;/UL&gt;
&lt;P&gt;It also has disadvantages. My favourites:&lt;/P&gt;
&lt;UL&gt;
&lt;LI&gt;"The const problem", or "One naming policy to rule them all". Strongly named assemblies cannot use assemblies which aren't strongly named. Like a virus, strong naming must spread throughout an application, or perish. This causes difficulties interacting with open source or other unsigned, third party components.&lt;/LI&gt;
&lt;LI&gt;Version coupling. Since an assembly's strong name includes its version, the .NET framework generally requires that, if assembly A is using assembly B, the exact version of assembly B against which A was linked must be available at runtime.&lt;/LI&gt;
&lt;LI&gt;Compatibility with the GAC. The global application cache only accepts strong named assemblies.&lt;/LI&gt;&lt;/UL&gt;
&lt;P&gt;Thanks to the advantages of strong naming, Red Gate tends to strongly name its assemblies.&lt;/P&gt;
&lt;P&gt;To tangent slightly before weaving this story together into a coherent whole, .NET Remoting is in many ways another Good Thing. A simple, distributed .NET application is far simpler to set up and work with than some other approaches: I come from a background involving distributed DCOM applications, so I know whereof I speak.&lt;/P&gt;
&lt;P&gt;The ability to choose whether objects passed back between tiers are executed remotely via a proxy, or are transmitted across the network and executed locally, is simple and logical. Derive from MarshalByRefObject or add the Serializable attribute to your class.&lt;/P&gt;
&lt;P&gt;You still have to contend with one basic problem: in a client/server architecture based on Remoting, the client and server have to have a common set of types. There are several ways to achieve this:&lt;/P&gt;
&lt;UL&gt;
&lt;LI&gt;Ensure that each client has a reference to the server assembly, and that the server assembly is installed locally on each client purely for access to its metadata. When the client needs types, it pulls them from the server assembly.&lt;/LI&gt;
&lt;LI&gt;Since in the case of well known (ie. server activated) MarshalByRefObjects the client only needs metadata about the remote object in order to synthesize a proxy, a utility such as Soapsuds can be used to generate a metadata-only assembly for the client. This does require that HTTP/SOAP is your channel of choice.&lt;/LI&gt;
&lt;LI&gt;Put all common types into a separate, common assembly. Provided interfaces are used, no server-side code needs to exist in this assembly: the common assembly simply defines the interface which the server implements, and provides enough metadata for clients to use it.&lt;/LI&gt;&lt;/UL&gt;
&lt;P&gt;Let's consider a realistic deployment scenario. We're writing a client/server application using .NET Remoting. Both parts, client and server, are to be shipped out to users. They install the server in one place and the clients in several places.&lt;/P&gt;
&lt;P&gt;Now let's introduce one more realistic factor to spice things up: we're going to need to roll out new versions of both client and server at some point. And let's say that our build system rolls a new assembly version number each time it builds any assembly.&lt;/P&gt;
&lt;P&gt;With that in mind, let's review the above options. The first one is ugly, even if it were likely to fly. The second one is even grubbier, requiring additional build process in order to achieve a notional separation, and places techincal limitations on our communication protocol. The third one has to be the way to go.&lt;/P&gt;
&lt;P&gt;It should also fit with our stated scenario. Provided none of the types in our common assembly change, we should be free to issue updates to the client and the server independently. (If the common assembly needs to change, then we need to issue updates to both client and server, and this will be a breaking change for existing customers unless they update both client and server. But this should be a rather rarer occurrence than, say, a patch containing purely UI-related client fixes, or a couple of server fixes, or both.)&lt;/P&gt;
&lt;P&gt;The problem we encounter is this: by default, given the constraints I've outlined, IT DOESN'T WORK.&lt;/P&gt;
&lt;P&gt;Sorry, I went a bit forum troll esque there with my caps key. But this topic has been vexing me all day, and I've only now managed to find a solution that has any chance in hell of working. &lt;/P&gt;
&lt;P&gt;The problem one encounters is this: as soon as there is a difference between the strong name of the common type library on the client, and the strong name of the common type library on the server, everything breaks. Remoting throws exceptions as soon as any notable client/server communication starts.&lt;/P&gt;
&lt;P&gt;And given that the strong name of an assembly includes its version number, and that we revise our assembly version numbers each build, this is a bit of a showstopper. (Even if we only rolled a new assembly version number when new features came in, it would still be a showstopper. An old client should be able to connect to a new server, provided interfaces are backwardly compatible.)&lt;/P&gt;
&lt;P&gt;One cause of this applies only to Serializable types. By default, serialization for remoting always includes the version number of the type in question, which corresponds to the version number of its containing assembly. Since the build numbers differ between client and server, we hit exceptions upon deserialization. The fix for this is to instruct .NET remoting not to include version numbers when serializing. This is easily done by supplying includeVersions = false to the formatter sink provider associated with the channel being used for client/server communication.&lt;/P&gt;
&lt;P&gt;The other, and more irritating cause of this occurs when a type comes across the wire by other means: for example, when an argument or return value supplied from/to a well known object is of a custom type belonging to the common assembly. (Oddly it doesn't seem to occur when instantiating a well known, server-activated object; just when calling its properties and methods with common types.) It also occurs if you pass an instance of the Type class across, and that type came from eg. typeof(ICommonInterface).&lt;/P&gt;
&lt;P&gt;This is less easy to resolve. .NET is, in some ways, legitimately flagging a potential problem: the client and the server "do not agree" on the set of types used for communication. I enquote "do not agree" because .NET's idea of "agreeing" is: "types' strong names exactly match", which is clearly extraordinarily unlikely in any realistic situation. But by buying into strong naming and remoting, we buy into .NET's over-simplistic world view.&lt;/P&gt;
&lt;P&gt;The way to resolve this is to take the situation out of .NET's hands entirely, just as per the serialization versioning issue, but more so.&amp;nbsp;At the lowest level, these issues come down to assembly load resolution. When a remoting client receives a type over the wire from the server which belongs to strongly named assembly, .NET is wired up to attempt to load that specific assembly version to work with that type. Likewise when the server receives a type from the client.&lt;/P&gt;
&lt;P&gt;Let's get concrete here. Say we have Client.exe version 1.0.0.1 using Common.dll version 1.0.0.1. Remotely we have Server.exe version 1.0.0.20 using Common.dll version 1.0.0.20. We need the Client to work with types from Common.dll version 1.0.0.20, and the Server to work with types from Common.dll version 1.0.0.1. This is because we know, behind the scenes, that the types are actually identical in both versions of Common.dll. No breaking changes were made to the client/server interfaces.&lt;/P&gt;
&lt;P&gt;One thing we can do is to set up "binding redirects". We can explictly tell .NET to replace references to certain versions of an assembly with a specific version. So in the client we could have a Client.exe.config which looked like this:&lt;/P&gt;
&lt;P&gt;&amp;lt;configuration&amp;gt;&lt;BR&gt;&amp;nbsp;&amp;nbsp; &amp;lt;runtime&amp;gt;&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1"&amp;gt;&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;dependentAssembly&amp;gt;&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;assemblyIdentity name="Common"&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; publicKeyToken="deadbeefdeadbeef"&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; culture="neutral" /&amp;gt;&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;bindingRedirect oldVersion="0.0.0.0-65535.65535.65535.65535"&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; newVersion="1.0.0.1"/&amp;gt;&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;/dependentAssembly&amp;gt;&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;/assemblyBinding&amp;gt;&lt;BR&gt;&amp;nbsp;&amp;nbsp; &amp;lt;/runtime&amp;gt;&lt;BR&gt;&amp;lt;/configuration&amp;gt;&lt;/P&gt;
&lt;P&gt;And similarly on the server, except with newVersion="1.0.0.20".&lt;/P&gt;
&lt;P&gt;This works, but the problem with it is that every time the version of Common.dll on the client changes, we have to roll a new Client.exe.config file with the correct "newVersion" number in it. There's no way to say newVersion="any" or similar.&lt;/P&gt;
&lt;P&gt;A rather funkier solution I came across today is to handle this in code, rather than with configuration files. Ben Hall points out in his blog, &lt;A href="http://blog.benhall.me.uk/2006/08/appdomaincurrentdomainassemblyresolve.html"&gt;http://blog.benhall.me.uk/2006/08/appdomaincurrentdomainassemblyresolve.html&lt;/A&gt;, that it's possible for Client.exe to handle the AppDomain.AssemblyResolve event, and manually resolve which assembly gets loaded. So we can say to .NET "ah, you want version 1.0.0.20 of the Common assembly? Well here's the version of that assembly I've got in my local directory, ignore the version number, take it and shut up, it's good for you."&lt;/P&gt;
&lt;P&gt;Since types are loaded when they're first referenced, if you register for this event as the very first thing inside your Main function, you can handle all assembly versioning issues this way:&lt;/P&gt;
&lt;P&gt;&lt;FONT face="Courier New"&gt;&lt;FONT size=2&gt;&lt;FONT color=#0000ff&gt;static&lt;/FONT&gt; &lt;FONT color=#0000ff&gt;void&lt;/FONT&gt;&lt;/FONT&gt;&lt;/FONT&gt;&lt;FONT face="Courier New"&gt;&lt;FONT size=2&gt; Main()&lt;BR&gt;{&lt;BR&gt;&lt;FONT color=#008080&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; AppDomain&lt;/FONT&gt;.CurrentDomain.AssemblyResolve += &lt;FONT color=#0000ff&gt;new&lt;/FONT&gt; &lt;FONT color=#008080&gt;ResolveEventHandler&lt;/FONT&gt;&lt;/FONT&gt;&lt;/FONT&gt;&lt;FONT face="Courier New" size=2&gt;( CurrentDomain_AssemblyResolve );&lt;BR&gt;&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; Startup();&lt;/FONT&gt;&lt;/P&gt;
&lt;P&gt;&lt;FONT size=2&gt;&lt;FONT face="Courier New"&gt;&lt;FONT color=#008080&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; Application&lt;/FONT&gt;.Run( &lt;FONT color=#0000ff&gt;new&lt;/FONT&gt; &lt;FONT color=#008080&gt;MainForm&lt;/FONT&gt;&lt;/FONT&gt;&lt;FONT face="Courier New"&gt;() );&lt;/FONT&gt;&lt;/FONT&gt;&lt;/P&gt;
&lt;P&gt;&lt;FONT face="Courier New" size=2&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; Shutdown();&lt;BR&gt;&lt;/FONT&gt;&lt;FONT face="Courier New" size=2&gt;}&lt;/FONT&gt;&lt;/P&gt;
&lt;P&gt;&lt;FONT size=2&gt;&lt;FONT face="Courier New"&gt;&lt;FONT color=#0000ff&gt;static&lt;/FONT&gt; System.Reflection.&lt;FONT color=#008080&gt;Assembly&lt;/FONT&gt; CurrentDomain_AssemblyResolve(&lt;FONT color=#0000ff&gt;object&lt;/FONT&gt; sender, &lt;FONT color=#008080&gt;ResolveEventArgs&lt;/FONT&gt;&lt;/FONT&gt;&lt;FONT face="Courier New"&gt; e)&lt;BR&gt;&lt;/FONT&gt;&lt;/FONT&gt;&lt;FONT face="Courier New" size=2&gt;{&lt;BR&gt;&lt;/FONT&gt;&lt;FONT color=#0000ff&gt;&lt;FONT face="Courier New" size=2&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; try&lt;BR&gt;&lt;/FONT&gt;&lt;/FONT&gt;&lt;FONT face="Courier New" size=2&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;BR&gt;&lt;/FONT&gt;&lt;FONT size=2&gt;&lt;FONT face="Courier New"&gt;&lt;FONT color=#008080&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; MessageBox&lt;/FONT&gt;.Show( &lt;FONT color=#800000&gt;"Asked for:\n"&lt;/FONT&gt; + e.Name, &lt;FONT color=#800000&gt;"Client"&lt;/FONT&gt;&lt;/FONT&gt;&lt;FONT face="Courier New"&gt; );&lt;BR&gt;&lt;/FONT&gt;&lt;/FONT&gt;&lt;FONT size=2&gt;&lt;FONT face="Courier New"&gt;&lt;FONT color=#0000ff&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; string&lt;/FONT&gt;[] assemblyDetail = e.Name.Split(&lt;FONT color=#800000&gt;','&lt;/FONT&gt;&lt;/FONT&gt;&lt;FONT face="Courier New"&gt;);&lt;BR&gt;&lt;/FONT&gt;&lt;/FONT&gt;&lt;FONT size=2&gt;&lt;FONT face="Courier New"&gt;&lt;FONT color=#0000ff&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; string&lt;/FONT&gt; assemblyBasePath = System.IO.&lt;FONT color=#008080&gt;Path&lt;/FONT&gt;.GetDirectoryName(&lt;FONT color=#008080&gt;Assembly&lt;/FONT&gt;&lt;/FONT&gt;&lt;FONT face="Courier New"&gt;.GetExecutingAssembly().Location);&lt;BR&gt;&lt;/FONT&gt;&lt;/FONT&gt;&lt;FONT size=2&gt;&lt;FONT face="Courier New"&gt;&lt;FONT color=#008080&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; Assembly&lt;/FONT&gt; assembly = &lt;FONT color=#008080&gt;Assembly&lt;/FONT&gt;.LoadFrom( &lt;FONT color=#008080&gt;Path&lt;/FONT&gt;.Combine( assemblyBasePath, assemblyDetail[0] + &lt;FONT color=#800000&gt;".dll"&lt;/FONT&gt;&lt;/FONT&gt;&lt;FONT face="Courier New"&gt; ) );&lt;BR&gt;&lt;/FONT&gt;&lt;/FONT&gt;&lt;FONT size=2&gt;&lt;FONT face="Courier New"&gt;&lt;FONT color=#008080&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; MessageBox&lt;/FONT&gt;.Show( &lt;FONT color=#800000&gt;"Supplying:\n"&lt;/FONT&gt; + assembly.GetName(), &lt;FONT color=#800000&gt;"Client"&lt;/FONT&gt;&lt;/FONT&gt;&lt;FONT face="Courier New"&gt; );&lt;BR&gt;&lt;/FONT&gt;&lt;/FONT&gt;&lt;FONT size=2&gt;&lt;FONT face="Courier New" color=#0000ff&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; return&lt;/FONT&gt;&lt;FONT face="Courier New"&gt; assembly;&lt;BR&gt;&lt;/FONT&gt;&lt;/FONT&gt;&lt;FONT face="Courier New" size=2&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;BR&gt;&lt;/FONT&gt;&lt;FONT size=2&gt;&lt;FONT face="Courier New"&gt;&lt;FONT color=#0000ff&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; catch&lt;/FONT&gt; (&lt;FONT color=#008080&gt;Exception&lt;/FONT&gt;&lt;/FONT&gt;&lt;FONT face="Courier New"&gt;)&lt;BR&gt;&lt;/FONT&gt;&lt;/FONT&gt;&lt;FONT face="Courier New" size=2&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; {&lt;BR&gt;&lt;/FONT&gt;&lt;FONT face="Courier New"&gt;&lt;FONT size=2&gt;&lt;FONT color=#0000ff&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; return&lt;/FONT&gt; &lt;FONT color=#0000ff&gt;null&lt;/FONT&gt;; &lt;FONT color=#008000&gt;// fail&lt;BR&gt;&lt;/FONT&gt;&lt;/FONT&gt;&lt;/FONT&gt;&lt;FONT face="Courier New" size=2&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;BR&gt;&lt;/FONT&gt;&lt;FONT face="Courier New" size=2&gt;}&lt;/FONT&gt;&lt;/P&gt;
&lt;P&gt;Which is very splendid. And admittedly, terribly terribly evil. We just bypassed the simple-minded sanity which .NET is trying to inflict on our remoting of types from strongly named assemblies, and introduced much more flexible chaos. Now it's up to us to roll enough version control of our own that the whole shebang doesn't explode and shower our users with unseemly goup. But this way we at least have a chance of updating our client and server independently, without forcing clients to patch them in lock step.&lt;/P&gt;
&lt;P&gt;There is another solution to this whole mess of course: Use a proper&amp;nbsp;language. I mean, reroll a simpler in house remoting mechanism that works. Damn, two Freudian slips, I mean: stop using strongly named assemblies. Depending on your situation, as Craig points out in his blog, &lt;A href="http://www.pluralsight.com/blogs/craig/archive/2005/03/11/6653.aspx"&gt;http://www.pluralsight.com/blogs/craig/archive/2005/03/11/6653.aspx&lt;/A&gt;, this may be a far simpler solution to your woes.&lt;/P&gt;
&lt;P&gt;-&lt;/P&gt;
&lt;P&gt;&lt;FONT size=2&gt;&lt;EM&gt;"&lt;/EM&gt;&lt;/FONT&gt;&lt;FONT size=2&gt;&lt;EM&gt;...I wou'd have you Observe what was told to us aboute tak'g Care whom to calle upp, for you are Sensible...and can judge how truely that Horrendous thing is reported. &lt;/EM&gt;&lt;/FONT&gt;&lt;/P&gt;
&lt;P&gt;&lt;FONT size=2&gt;&lt;EM&gt;I say to you againe, doe not call up Any that you can not put downe; by the Which I meane, Any that can in Turne call up Somewhat against you, whereby your Powerfullest Devices may not be of use. Ask of the Lesser, lest the Greater shal not wish to Answer, and shal commande more than you."&amp;nbsp; --&amp;nbsp; &lt;/EM&gt;H P Lovecraft&lt;/FONT&gt;&lt;/P&gt;&lt;img src="http://www.simple-talk.com/community/aggbug.aspx?PostID=35818" width="1" height="1"&gt;</content><author><name>Dan J Archer</name><uri>http://www.simple-talk.com/community/user/Profile.aspx?UserID=2114</uri></author></entry><entry><title>The timeout period elapsed prior to obtaining a connection from the pool.</title><link rel="alternate" type="text/html" href="http://www.simple-talk.com/community/blogs/dana/archive/2007/03/15/20819.aspx" /><id>http://www.simple-talk.com/community/blogs/dana/archive/2007/03/15/20819.aspx</id><published>2007-03-15T09:27:00Z</published><updated>2007-03-15T09:27:00Z</updated><content type="html">As I believe I have already opined, dealing with databases is not one of my all time favourite activities. It's not right up there with drinking, skiing and World of Warcraft, for example. For someone in my position, dealing with databases is naturally an occupational hazard; but I do attempt to minimise the amount of time I spend doing it.&lt;BR&gt;&lt;BR&gt;Obscure errors connecting to databases can suck up a lot of time. There are so many things can can go wrong. Fortunately, .NET makes this rather easy to handle in many cases (catch SqlException, do something sensible), but there are occasions when this all falls down.&lt;BR&gt;&lt;BR&gt;One exception in particular has been something of a major niggle over the last few weeks, as from time to time my colleague James Moore and&amp;nbsp; I tried to establish why the application we're working on was suffering from it:&lt;BR&gt;&lt;BR&gt;&lt;FONT size=2&gt;&lt;FONT face="Courier New" color=#ff0000 size=2&gt;InvalidOperationException&lt;/FONT&gt;&lt;BR&gt;&lt;BR&gt;&lt;FONT face="Courier New" color=#990000 size=2&gt;Timeout expired.&amp;nbsp; The timeout period elapsed prior to obtaining a connection from the pool.&amp;nbsp; This may have occurred because all pooled connections were in use and max pool size was reached.&lt;/FONT&gt;&lt;/FONT&gt;&lt;BR&gt;&lt;BR&gt;It looks so straightforward. Googling suggests that the error message is telling the truth: this error occurs when I, niave fool that I am, open dastardly numbers of pooled connections to a server in a cavalier fashion, cackling as I disregard all sensible cleanup procedures, spilling objects out all over the place and generally jumping up and down on System.Data and grinning.&lt;BR&gt;&lt;BR&gt;Unfortunately, this is not the case. The error message is entirely misleading. You can get this exception when not doing anything dangerous or complicated, or opening hoards of connections. In fact, as we discovered when trying to reproduce the problem outside of our application, you can get this exception when hardly doing anything at all.&lt;BR&gt;&lt;BR&gt;Here's some sample .NET 2 code. To achieve the exception, simply &lt;I&gt;stop your SQL Server&lt;/I&gt; (2000 or 2005, dealer's choice), and run this code to attempt to connect to it.&lt;BR&gt;&lt;BR&gt;&lt;FONT face="Courier New" size=2&gt;&lt;FONT color=#0000ff&gt;using &lt;/FONT&gt;System;&lt;BR&gt;&lt;FONT color=#0000ff&gt;using &lt;/FONT&gt;System.Collections.Generic;&lt;BR&gt;&lt;FONT color=#0000ff&gt;using &lt;/FONT&gt;System.Windows.Forms;&lt;BR&gt;&lt;FONT color=#0000ff&gt;using &lt;/FONT&gt;System.Data.SqlClient;&lt;BR&gt;&lt;FONT color=#0000ff&gt;using &lt;/FONT&gt;System.Diagnostics;&lt;BR&gt;&lt;FONT color=#0000ff&gt;using &lt;/FONT&gt;System.Threading;&lt;BR&gt;&lt;BR&gt;&lt;FONT color=#0000ff&gt;namespace &lt;/FONT&gt;&lt;FONT color=#800080&gt;CrashTest&lt;/FONT&gt;&lt;BR&gt;{&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;FONT color=#0000ff&gt;static class&lt;/FONT&gt;&lt;/FONT&gt;&lt;FONT face="Courier New" size=2&gt; Program&lt;/FONT&gt;&lt;FONT face="Courier New" size=2&gt;&lt;FONT color=#000000&gt;&lt;/FONT&gt;&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; {&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; [&lt;FONT color=#800080&gt;STAThread&lt;/FONT&gt;]&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;FONT color=#0000ff&gt;static void&lt;/FONT&gt; Main()&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; {&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;FONT color=#800080&gt;ThreadPool&lt;/FONT&gt;.QueueUserWorkItem(&lt;FONT color=#0000ff&gt;new &lt;/FONT&gt;&lt;FONT color=#800080&gt;WaitCallback&lt;/FONT&gt;(GetData));&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; GetData(&lt;FONT color=#0000ff&gt;null&lt;/FONT&gt;);&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;FONT color=#0000ff&gt;static void &lt;/FONT&gt;GetData(&lt;FONT color=#0000ff&gt;object &lt;/FONT&gt;state)&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; {&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;FONT color=#0000ff&gt;using &lt;/FONT&gt;(&lt;FONT color=#800080&gt;SqlConnection &lt;/FONT&gt;connection = &lt;FONT color=#0000ff&gt;new &lt;/FONT&gt;&lt;FONT color=#800080&gt;SqlConnection&lt;/FONT&gt;(&lt;FONT color=#a52a2a&gt;"Data Source=DAN"&lt;/FONT&gt;))&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; {&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; connection.Open();&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;BR&gt;}&lt;/FONT&gt;&lt;BR&gt;&lt;BR&gt;As you can see, we simply attempt to open two simultaneous connections to a non-existent server. Not a hundred, or any other arbitrary limit, but two. 100% of the time that I run this, I get the InvalidOperationException.&lt;BR&gt;&lt;BR&gt;Having flicked through the underlying System.Data code with Reflector, the best theory we have come up with is that there's some kind of race condition in System.Data which is causing the problem. If a connection cannot be obtained from the pool for more or less any reason, System.Data throws this particular exception. It's not necessarily limited to timeouts of any variety; hence the juicily choice, "this may have occurred because..." in the exception message. An excellent get-out clause for providing exceedingly unhelpful information, in my view.&lt;BR&gt;&lt;BR&gt;To preserve our sanity, we haven't drilled down further into exactly what the offending code is doing that causes this issue. It turns out we don't need to. All we need to do is ensure that our application never attempts to open two connections to the same server at the same time. We may do something clever, but certainly one easy way to do this is to replace the line:&lt;BR&gt;&lt;BR&gt;&lt;FONT face="Courier New" size=2&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; connection.Open();&lt;BR&gt;&lt;/FONT&gt;&lt;BR&gt;with a simple monitor lock:&lt;BR&gt;&lt;FONT face="Courier New" size=2&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;FONT color=#0000ff&gt;lock &lt;/FONT&gt;(m_Lock)&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; { &amp;nbsp; &lt;BR&gt;&lt;/FONT&gt;&lt;FONT face="Courier New" size=2&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; connection.Open();&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;/FONT&gt;&lt;FONT face="Courier New" size=2&gt;&lt;BR&gt;&lt;/FONT&gt;&lt;BR&gt;on an appropriate object, which for the purposes of this example we can just declare elsewhere in the class:&lt;BR&gt;&lt;BR&gt;&lt;FONT face="Courier New" size=2&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &lt;FONT color=#0000ff&gt;private static readonly object &lt;/FONT&gt;m_Lock = &lt;FONT color=#0000ff&gt;new object&lt;/FONT&gt;();&lt;BR&gt;&lt;/FONT&gt;&lt;BR&gt;And bingo, checkmate, ding, and other gameplay-oriented culminatory expostulations; Bob is a close relative.&lt;BR&gt;&lt;img src="http://www.simple-talk.com/community/aggbug.aspx?PostID=20819" width="1" height="1"&gt;</content><author><name>Dan J Archer</name><uri>http://www.simple-talk.com/community/user/Profile.aspx?UserID=2114</uri></author></entry><entry><title>Transforming parrots</title><link rel="alternate" type="text/html" href="http://www.simple-talk.com/community/blogs/dana/archive/2006/10/12/2641.aspx" /><id>http://www.simple-talk.com/community/blogs/dana/archive/2006/10/12/2641.aspx</id><published>2006-10-12T11:11:00Z</published><updated>2006-10-12T11:11:00Z</updated><content type="html">Whilst I agree with luminaries such as Paul Graham on a reasonable number of points he makes about our industry and profession, I generally disagree with him on the subject of typing. He doesn't believe in compiler-enforced strong types, whereas I do. &lt;br&gt;&lt;br&gt;On this occasion though, I find myself leaning towards a slightly more permissive stance, with regard to templating, types and interfaces in C#.&lt;br&gt;&lt;br&gt;If you're thinking that this blog entry is uncharacteristically light on conversational detritus so far, then you're right. Surprisingly, this may continue to the end of the blog entry, mainly because I'm in rather a hurry developing the piece of software which the compiler is complaining about. But I had to pause long enough to whine, have a coffee, and get my breath back.&lt;br&gt;&lt;br&gt;So, to be more explictly explict, my gripe is as follows.&lt;br&gt;&lt;br&gt;Generics in C# permit me to have a List&amp;lt;Class&amp;gt;, that is a list whose members may only be of the given class. Splendid and good. List implements various useful interfaces, notably (for the purposes of this discussion) IEnumerable&amp;lt;Class&amp;gt;, which can be used to iterate over (one might even say enumerate) the members of the list, but not to change them. So far, so splendid.&lt;br&gt;&lt;br&gt;But classes can implement interfaces. My class, let's call it File, may just be the concrete realisation of the abstract interface IFile. IFile may be a specialisation of a less flexible interface called IFileDescription, which permits only certain simple properties to be read, but not written. All well and good.&lt;br&gt;&lt;br&gt;I may then have a class called Directory, which contains files. I may create interfaces, IDirectory and IDirectoryDescription; again, the former permits read/write access to most properties, whereas the latter permits only certain simple properties to be read but not written. Good show.&lt;br&gt;&lt;br&gt;Since a directory contains files, it seems wise for IDirectoryDescription to allow enumeration of the IFileDescriptions it contains; and for IDirectory to allow enumeration of the IFiles it contains.&lt;br&gt;&lt;br&gt;Why would I want all this? Well, let's say for the sake of this example that I have one part of my application in which I create Directory and File objects from the local file system, where it's very likely that the user can read and write to that file system, and should be able to within the application. But perhaps the application retains a log of files and directories it created in previous sessions, even if those files and directories have long since been deleted. These can certainly be read, and I can use my File and Directory objects to store that data in memory, even load it to and from disk. But I shouldn't supply that history to other parts of the application as IFile and IDirectory, since those files and directories may not exist, and in this context I only wish to view simple information, not permit it to be edited.&lt;br&gt;&lt;br&gt;So, back to the interfaces. Here's a recap in code.&lt;br&gt;&lt;br&gt;&lt;font face="Courier New" size="2"&gt;interface IDirectoryDescription&lt;br&gt;{&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; string Name { get; }&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; IEnumerable&amp;lt;IFileDescription&amp;gt; FileDescriptions { get; }&lt;br&gt;}&lt;br&gt;&lt;br&gt;interface IDirectory : IDirectoryDescription&lt;br&gt;{&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; string Name{ get; set; }&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; IEnumerable&amp;lt;IFile&amp;gt; Files { get; }&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; ...&lt;br&gt;}&lt;/font&gt;&lt;font face="Courier New" size="2"&gt;&lt;br&gt;&lt;br&gt;interface IFileDescription&lt;br&gt;{&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; string Name { get; }&lt;br&gt;}&lt;br&gt;&lt;br&gt;interface IFile : IFileDescription&lt;br&gt;{&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; string Name { get; set; }&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; ...&lt;br&gt;}&lt;/font&gt;&lt;br&gt;&lt;br&gt;So that works nicely. To implementation.&lt;br&gt;&lt;br&gt;&lt;font size="2"&gt;c&lt;/font&gt;&lt;font face="Courier New" size="2"&gt;lass File : IFile /* and implicitly also IFileDescription */&lt;br&gt;{&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; ...&lt;br&gt;}&lt;br&gt;&lt;br&gt;class Directory : IDirectory /* and implicitly also IDirectoryDescription */&lt;br&gt;{&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; private List&amp;lt;File&amp;gt; m_Files;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; ...&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; public IEnumerable&amp;lt;IFileDescription&amp;gt; FileDescriptions&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; {&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; get { return m_Files; }&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; public IEnumerable&amp;lt;IFile&amp;gt; Files&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; {&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; get { return m_Files; }&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br&gt;}&lt;/font&gt;&lt;br&gt;&lt;br&gt;So that's all good. Oh, except that it doesn't compile.&lt;br&gt;&lt;br&gt;C# bitches, whines and generally crows about the fact that it cannot convert m_Files (which is a List&amp;lt;File&amp;gt;) into either an IEnumerable&amp;lt;IFileDescription&amp;gt; or an IEnumerable&amp;lt;IFile&amp;gt;. This, it claims, is my problem.&lt;br&gt;&lt;br&gt;To which I say, yes, but I happen to know, as should you, that File implements IFile and IFileDescription. So, make it happen, make it so, engage, and chop chop.&lt;br&gt;&lt;br&gt;Sadly it has a point, according to the letter of the language. Strong typing tells us that IEnumerable&amp;lt;File&amp;gt; is entirely distinct from IEnumerable&amp;lt;IFile&amp;gt;, even if File isa IFile.&lt;br&gt;&lt;br&gt;So, a crufty adaptor class (or more specifically, classes) have to be created. Here's mine.&lt;br&gt;&lt;br&gt;&lt;font face="Courier New" size="2"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; internal class EnumerableAdaptor&amp;lt;Interface, Class&amp;gt; : IEnumerable&amp;lt;Interface&amp;gt; where Class : Interface&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; {&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; private IEnumerable&amp;lt;Class&amp;gt; m_List;&lt;br&gt;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; public EnumerableAdaptor(IEnumerable&amp;lt;Class&amp;gt; list)&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; {&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; m_List = list;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br&gt;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; public IEnumerator&amp;lt;Interface&amp;gt; GetEnumerator()&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; {&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; return new EnumeratorAdaptor&amp;lt;Interface, Class&amp;gt;(m_List);&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br&gt;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; {&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; return new EnumeratorAdaptor&amp;lt;Interface, Class&amp;gt;(m_List);&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br&gt;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; internal class EnumeratorAdaptor&amp;lt;Interface, Class&amp;gt; : IEnumerator&amp;lt;Interface&amp;gt; where Class : Interface&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; {&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; private IEnumerator&amp;lt;Class&amp;gt; m_Inner;&lt;br&gt;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; public EnumeratorAdaptor(IEnumerable&amp;lt;Class&amp;gt; list)&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; {&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; m_Inner = list.GetEnumerator();&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br&gt;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; public Interface Current&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; {&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; get { return m_Inner.Current; }&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br&gt;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; public void Dispose()&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; {&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; m_Inner.Dispose();&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br&gt;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; object System.Collections.IEnumerator.Current&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; {&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; get { return m_Inner.Current; }&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br&gt;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; public bool MoveNext()&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; {&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; return m_Inner.MoveNext();&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br&gt;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; public void Reset()&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; {&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; m_Inner.Reset();&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br&gt;&lt;/font&gt;&lt;br&gt;Now I can finish my implementation of Directory:&lt;br&gt;&lt;br&gt;&lt;font face="Courier New" size="2"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; public IEnumerable&amp;lt;IFile&amp;gt; Files&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; {&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; get { return new EnumerableAdaptor&amp;lt;IFile, File&amp;gt;(m_Files);&amp;nbsp; }&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br&gt;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; public IEnumerable&amp;lt;IFileDescription&amp;gt; FileDescriptions&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; {&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; get { return new EnumerableAdaptor&amp;lt;IFileDescription, File&amp;gt;(m_Files); }&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;/font&gt;&lt;br&gt;&lt;br&gt;I find this sort of thing exceptionally irritating when all I'm trying to do is get a simple job done. I'm one of the world's biggest fans of object orientation and strong typing, but a bit of me believes strongly that it should be up to the compiler to work this out. Admittedly that would require some reinterpretation of generics. But I find C# generics overly restrictive compared to C++ anyway...why can't I have class S&amp;lt;T&amp;gt; : T where T is a class, not an interface? Sigh.&lt;br&gt;&lt;br&gt;Anyhoo. Back to the code for me...&lt;br&gt;&lt;img src="http://www.simple-talk.com/community/aggbug.aspx?PostID=2641" width="1" height="1"&gt;</content><author><name>Dan J Archer</name><uri>http://www.simple-talk.com/community/user/Profile.aspx?UserID=2114</uri></author></entry><entry><title>Exceptional service</title><link rel="alternate" type="text/html" href="http://www.simple-talk.com/community/blogs/dana/archive/2006/07/28/1484.aspx" /><id>http://www.simple-talk.com/community/blogs/dana/archive/2006/07/28/1484.aspx</id><published>2006-07-28T09:54:00Z</published><updated>2006-07-28T09:54:00Z</updated><content type="html">A constant niggle in the arena of computing is the way in which nobody takes responsibility. If your computer crashes whilst you're, say, playing an online game, and you call the manufacturer, they'll explain it's probably a software problem, perhaps a driver you, the user, stupidly installed. If you call the driver vendor, they'll explain that it's probably some other driver you, the user, stupidly installed; or perhaps some other hardware (not theirs, of course) is causing you problems; or perhaps your game has a bug in it. If you call the game publisher, they'll explain that it's clearly a hardware problem, or you need to upgrade your drivers. Nobody will hold their hand up and say, "Sorry, my bad, we screwed up. Our product is inferior, and we will rectify that for you, right now."&lt;br&gt;&lt;br&gt;On a microscopic level, the same situation occurs when a contract breaks down between the caller and callee of an interface. The case which I wish to briefly whine about is that of web services.&lt;br&gt;&lt;br&gt;I'm working on a .NET 2.0 desktop application which uses a third party web service. Now it generally works nicely. Much to my surprise, however, I periodically notice the application cough, gasp for air, and finally collapse spreadeagled on the floor. By the time I can attend it, I observe that it has written me a final message with one trembling finger, using its own vital fluids as ink:&lt;br&gt;&lt;br&gt;&lt;font size="2"&gt;&lt;span&gt;System.Net.WebException : The underlying connection was closed: A connection that was expected to be kept alive was closed by the server. &lt;/span&gt;&lt;/font&gt;&lt;br&gt;&lt;br&gt;How nice of it.&lt;br&gt;&lt;br&gt;Googling around revealed a few more facts. It appears that .NET and the web service implementation are having a disagreement over the lifetime of the HTTP connection which they're using to talk. .NET asked for the connection to be kept up, if you don't mind, so that it can avoid the overhead of reopening it all the time. The remote server agreed, but at some stage this became impossible. This may be because the remote server is a lying son of a VAX; or because there are inconvenient firewalls between my client and the server, interfering with the keepalive; or because the server encountered a minor problem which .NET didn't understand, and this issue got blown out of proportion until it finally manifested as a dropped connection. Opinion on the internet seems divided; all of these are cited as plausible reasons. Meantime, .NET, for its part, doesn't like the internet failing to work flawlessly. In its idealistic view, this simply should not happen! What is the world coming to? And, it reasons, if this is occurring, surely someone should be alerted so they can a) fix the internet; and/or b) rewrite the server code? Overall, the best thing to do from .NET's point of view is not to, say, reopening the offending connection and try again; no no, the best plan is to throw an exception.&lt;br&gt;&lt;br&gt;Now the thing that vexes me is not that .NET likes throwing exceptions whenever anything even vaguely unconventional occurs; such as, for example, if I were to come to work wearing a hat. I've grown accustomed to its idiosyncracies in this regard. What vexes me is that .NET has deliberately set itself up to work under a theoretical and idealistic optimal case (where keepalive connections are always kept alive), and then complains bitterly when its lofty ideal is not upheld by the cruel world.&lt;br&gt;&lt;br&gt;In any case, there is a solution to this problem. One simply has to find the web service's generated code within the client application, which derives from WebClientProtocol or one of its subclasses. One then overrides the GetWebRequest method as follows, to tell .NET not to bother trying to keep connections alive:&lt;br&gt;&lt;br&gt;&lt;font size="2"&gt;&lt;span&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; protected override System.Net.WebRequest GetWebRequest(Uri uri)&lt;/span&gt;&lt;br&gt;&lt;span&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; {&lt;/span&gt;&lt;br&gt;&lt;span&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; WebRequest request = base.GetWebRequest(uri);&lt;/span&gt;&lt;br&gt;&lt;span&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; (request as HttpWebRequest).KeepAlive = false;&lt;/span&gt;&lt;br&gt;&lt;span&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; return request;&lt;/span&gt;&lt;br&gt;&lt;span&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;/span&gt;&lt;/font&gt;&lt;br&gt;&lt;br&gt;Simple stuff. But there are a few little niggles; the .cs file in which one is supposed to override this method is generated code. So, if the web service's documented interface changes (which has been known to occur rather regularly on the internet), the code needs to be regenerated. Now I personally object to trawling through generated code each time I regenerate it, adding in hacks here and there. It's just wrong.&lt;br&gt;&lt;br&gt;So, a slightly more tasteful solution was required. Instead, I derive a class from the generated code for the web service, called eg. SafeWebService. Inside this class, I override the GetWebRequest method. I then be sure to make use of SafeWebService rather than the original generated code. This way, I can regenerate the code and not have to make any changes, since my modifications are not contained therein.&lt;br&gt;&lt;img src="http://www.simple-talk.com/community/aggbug.aspx?PostID=1484" width="1" height="1"&gt;</content><author><name>Dan J Archer</name><uri>http://www.simple-talk.com/community/user/Profile.aspx?UserID=2114</uri></author></entry><entry><title>Hoop jumping</title><link rel="alternate" type="text/html" href="http://www.simple-talk.com/community/blogs/dana/archive/2006/07/14/1215.aspx" /><id>http://www.simple-talk.com/community/blogs/dana/archive/2006/07/14/1215.aspx</id><published>2006-07-14T08:56:00Z</published><updated>2006-07-14T08:56:00Z</updated><content type="html">Although this may come as a surprise to some, as I work at a company which happens to produce simple tools for database professionals, I am not the world's biggest fan of databases. "Don't get me wrong", as the poet said: they are extraordinarily powerful and highly necessary. Many splendid online games, in which I have been known to occasionally indulge, would not function without a rather spiffy database behind the scenes, doing the grunt work of serving up interesting data concurrently to millions around the globe.&lt;br&gt;&lt;br&gt;However, I usually find my more direct encounters with databases in the day to day course of developing desktop software to be, if not vexing, then at least very much a case of rolling up the sleeves, getting a cup of coffee, and bending my brain to that database's particular way of working. Or more specifically, the way of working with the particular API de jour.&lt;br&gt;&lt;br&gt;Currently that happens to be .NET's SqlClient namespace, and I have one question. Why is it so difficult to call a stored procedure from .NET code?&lt;br&gt;&lt;br&gt;Here's an example. I'll assume for brevity that I already have a nice SqlConnection set up. I'm going to call a hypothetical "hello world" procedure that will display the name with which it is supplied.&lt;br&gt;&lt;br&gt;I should mention that I don't intend to exhaustively explain the workings of the SqlClient classes for the purposes of this &lt;br&gt;&lt;br&gt;&lt;font face="Courier New" size="2"&gt;internal void sayHello(string name)&lt;br&gt;{&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; using ( SqlCommand command = new SqlCommand( "EXECUTE sp_HelloWorld @Name", m_Connection )&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; {&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; command.Parameters["Name"] =&amp;nbsp; name;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; command.ExecuteNonQuery();&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br&gt;}&lt;/font&gt;&lt;br&gt;&lt;br&gt;So here we're being fairly nice. We're using SqlParameter objects rather than munging the command string together by hand: this protects against SQL injection attacks, and should avoid us having to manually massage .NET types into the correct format for a SQL query. We're taking advantage of the type-agnostic approach to using the Parameters collection, where it accepts an object and will automatically figure out the correct SQL type based on the underlying .NET type. Shiny.&lt;br&gt;&lt;br&gt;What's so hard about that, you may ask? Well, not a lot, in the simple case. Provided whenever I call a stored procedure I want to have this nice little block of code, rather than having a generalised abstraction. Which, incidentally, I don't. And provided I can trust the SqlParameters collection to correctly marshal my data types into their nice SQL equivalents. Which, it appears, I can't.&lt;br&gt;&lt;br&gt;All I'd like to do is have a function as follows:&lt;br&gt;&lt;br&gt;&lt;font face="Courier New" size="2"&gt;&lt;span&gt;internal void ExecuteStoredProcedure(string name, params object[] args);&lt;/span&gt;&lt;br&gt;&lt;/font&gt;&lt;br&gt;This works like string.Format(): you can pass a variable number of arguments after the stored procedure name. So I can say:&lt;br&gt;&lt;br&gt;&lt;font face="Courier New" size="2"&gt;ExecuteStoredProcedure("sp_HelloWorld", name);&lt;/font&gt;&lt;br&gt;&lt;br&gt;Well, this turns out to be easy enough. Here's some code for you. It's rather rough, ready and unoptimised, to make the example simpler. But to make things interesting, it does take two slightly different but equivalent approaches to creating parameter names. And it has some hard coded limits. I know, I need slapping for this, but it's easily fixable, and it's Friday afternoon.&lt;br&gt;&lt;br&gt;&lt;font face="Courier New" size="2"&gt;internal int ExecuteNonQueryStoredProcedure(string procedureName, params object[] args)&lt;br&gt;{&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; return ExecuteNonQueryCommand(GetExecuteCommand(procedureName, args), args);&lt;br&gt;}&lt;/font&gt;&lt;br&gt;&lt;br&gt;&lt;font face="Courier New" size="2"&gt;internal string GetExecuteCommand(string procedureName, object[] args)&lt;br&gt;{&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; string command;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; StringBuilder sb = new StringBuilder();&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; sb.AppendFormat("EXECUTE {0}", procedureName);&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; int nArgsSoFar = 0;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; if (args != null)&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; {&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; // longhand&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; //&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; for (int iArg = 0; iArg &amp;lt; args.Length; iArg++)&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; {&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; if (nArgsSoFar != 0)&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; sb.AppendFormat(", @P{0}", iArg);&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; else&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; sb.AppendFormat(" @P{0}", iArg);&lt;br&gt;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; nArgsSoFar++;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; command = sb.ToString();&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; return command;&lt;br&gt;}&lt;/font&gt;&lt;br&gt;&lt;br&gt;&lt;font face="Courier New" size="2"&gt;internal int ExecuteNonQueryCommand(string template, params object[] args)&lt;br&gt;{&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; using (SqlCommand command = CreateSqlCommand(template, args))&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; {&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; return command.ExecuteNonQuery();&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br&gt;}&lt;/font&gt;&lt;br&gt;&lt;br&gt;&lt;font face="Courier New" size="2"&gt;internal SqlCommand CreateSqlCommand(string query, params object[] sqlParameters)&lt;br&gt;{&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; SqlCommand command = new SqlCommand(query,m_Connection);&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; if (sqlParameters != null)&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; {&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; int iParam = 0;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; string[] paramNames = new string[] { "P0", "P1", "P2", "P3", "P4", "P5", "P6", "P7", "P8", "P9", "P10", "P11", "P12", "P13", "P14", "P15", "P16", "P17", "P18", "P19" };&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; if ( sqlParameters.Length &amp;gt; 20 )&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; throw new ArgumentException(Localize.Me("Too many arguments supplied to SQL command creator; maximum is 20."));&lt;br&gt;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; foreach ( object arg in sqlParameters )&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; {&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; object o = arg;&lt;br&gt;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; SqlParameter param = new SqlParameter(paramNames[iParam],o);&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; iParam++;&lt;br&gt;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; command.Parameters.Add(param);&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; return command;&lt;br&gt;}&lt;/font&gt;&lt;br&gt;&lt;br&gt;So, that's all fine and splendid.&lt;br&gt;&lt;br&gt;Or so I thought, until I tried to use it.&lt;br&gt;&lt;br&gt;Now mostly this works very nicely, as it should since it's not being particularly clever. The problem occurred when I tried to pass a &lt;span&gt;NULL&lt;/span&gt; value to a stored procedure in this fashion:&lt;br&gt;&lt;br&gt;&lt;font size="2"&gt;&lt;span&gt;ExecuteStoredProcedure("sp_HelloWorld",null);&lt;/span&gt;&lt;/font&gt;&lt;br&gt;&lt;br&gt;Now naturally if my stored procedure isn't expecting this, it should complain. However, we didn't get that far. .NET threw a tantrum way before then:&lt;br&gt;&lt;br&gt;&lt;font size="2"&gt;&lt;span&gt;RedGate.HelloWorld.HelloTest.Test1 : System.Data.SqlClient.SqlException : Prepared statement '...' expects parameter @P0, which was not supplied.&lt;/span&gt;&lt;/font&gt;&lt;br&gt;&lt;br&gt;Having discovered the source of this, I'm more than a teensy bit vexed by it.&lt;br&gt;&lt;br&gt;It seems that the SqlParameter class, whilst able to freely handle int, DateTime, string, wildebeast, diplodocus and assorted other standard goodies, has a bit of a problem with null. It doesn't realise that a null string should be passed in as DBNull.Value. Which seems a fraction catastrophically dim of it.&lt;br&gt;&lt;br&gt;The solution is easy enough. We can intercept parameters before they hit SqlParameter, and massage nulls into DBNull.Value ourselves:&lt;br&gt;&lt;br&gt;&lt;font face="Courier New" size="2"&gt;internal SqlCommand CreateSqlCommand(string query, params object[] sqlParameters)&lt;br&gt;
{&lt;br&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; SqlCommand command = new SqlCommand(query,m_Connection);&lt;br&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; if (sqlParameters != null)&lt;br&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; {&lt;br&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; int iParam = 0;&lt;br&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; string[] paramNames = new string[] { "P0", "P1", "P2", "P3",
"P4", "P5", "P6", "P7", "P8", "P9", "P10", "P11", "P12", "P13", "P14",
"P15", "P16", "P17", "P18", "P19" };&lt;br&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; if ( sqlParameters.Length &amp;gt; 20 )&lt;br&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; throw new ArgumentException(Localize.Me("Too many arguments supplied to SQL command creator; maximum is 20."));&lt;br&gt;
&lt;br&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; foreach ( object arg in sqlParameters )&lt;br&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; {&lt;br&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; object o = arg;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;  &lt;br&gt;&lt;span&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;  &amp;nbsp;&amp;nbsp;  &amp;nbsp; if ( o == null )&lt;/span&gt;&lt;br&gt;&lt;span&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;  &amp;nbsp;&amp;nbsp;  &amp;nbsp; &amp;nbsp;&amp;nbsp;  o = DBNull.Value;&lt;/span&gt;&lt;br&gt;
&lt;br&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; SqlParameter param = new SqlParameter(paramNames[iParam],o);&lt;br&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; iParam++;&lt;br&gt;
&lt;br&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; command.Parameters.Add(param);&lt;br&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; return command;&lt;br&gt;
}&lt;br&gt;&lt;br&gt;&lt;/font&gt;And Bob is a close relative.&lt;br&gt;&lt;br&gt;As usual though, I'm left beating my head against a table, wondering why, why, and even, why, this occurs? Am I missing something intrinsically difficult about this? Now I'm sure someone's going proffer the explanation that database NULL is distinct from, say, a C++ NULL, which is in fact just a pointer to memory location zero. Even assuming I agree that such a distiction is worth making, it doesn't apply here. a C++ null is a genuine null reference. Now at a purely semantic level, this is may be arguably not the same as a valid reference to a nullable type with a value of null. But, forgive me, I really don't care in the slightest. All I wish to express, clearly and succinctly, is the absence of a value, and I expect to be able to do that in the standard fashion for my language of choice. After all, if I don't have to convert between DateTime and SqlDateTime, which on earth should I care which flavour of null this API happens to prefer?&lt;br&gt;&lt;br&gt;I think the phrase is, Do What I Mean.&lt;br&gt;&lt;img src="http://www.simple-talk.com/community/aggbug.aspx?PostID=1215" width="1" height="1"&gt;</content><author><name>Dan J Archer</name><uri>http://www.simple-talk.com/community/user/Profile.aspx?UserID=2114</uri></author></entry><entry><title>1mL Adrenaline, stat</title><link rel="alternate" type="text/html" href="http://www.simple-talk.com/community/blogs/dana/archive/2006/06/12/870.aspx" /><id>http://www.simple-talk.com/community/blogs/dana/archive/2006/06/12/870.aspx</id><published>2006-06-12T06:40:00Z</published><updated>2006-06-12T06:40:00Z</updated><content type="html">It is a balmy summer evening in a suburb of Cambridge, England. Young gentlemen play cricket on the village green. Young ladies walk the country paths, shielded from the sun's rays with fashionable parasols. An iced cream vendor perambulates the streets with a cheery smile. And a young couple sit before their computers, beating the living daylights out of some wayward troll cultists in the World of Warcraft.&lt;BR&gt;&lt;BR&gt;All perfectly idyllic. A typical British summer evening. But, unpointful as it may seem to draw down such a melancholic and improbable path, what could spoil such a serene setting? Martians landing on the green, disrupting the cricket until destroyed by bacteria from inappropriately stored iced cream? An influx of crazed barely humanoid loons wearing brightly striped clothing, screeching barely polysyllabic mantras relating to the relative merits of two loosely geopolitically based groups forming the basis of some sort of banale, fruitless orchestrated confrontation of skill?&lt;BR&gt;&lt;BR&gt;No, but worse. On the two brightly lit internet-facing screens, heroes and villians froze mid swing as the ADSL router coughed, spluttered and gave up.&lt;BR&gt;&lt;BR&gt;It had done this before. It was a device of evil disposition, or rather it had been crafted by experts who themselves were sufficiently machiavellian as to contrive and shape designs into raw plastic with such painstaking malice that their work itself took on the dark aspect of its creators. Its very nature was transformed by the housing which contained it: transformed from a device whose purpose was to transmit the odd datum or two from one locale to another into a device eptly and precisely contrived to stop working whenever its operating temperature reached the meagre heights of a typical British summer.&lt;BR&gt;&lt;BR&gt;Fortunately, even the most rigerous of master criminals always make one fatal mistake. In this case, the anglo saxon masons of telecommunications (whose company shall remain relatively nameless) who assembled the dread device erred in permitting it to be opened by a skilled God-fearing ecclesiast with the aid of a small, consecrated screwdriver. Naturally the device would hiss and writhe beneath his grasp, but their faith would be a worthy shield against the demoniac, and against the tiny, maliciously sticky footpads which had to be removed to gain access to the aforementioned screws. Once open, the vile humours which the device's crafters had masterfully sought to enclose were freed, and set to disperse in the great outdoors, lost and winnowing amongst the cricketers and cucumber sandwiches.&lt;BR&gt;&lt;BR&gt;And so would remain only the task of preventing the same from happening again. Fortunately, mundane but effective means were at hand. With the aid of the perforated aluminium bin which normally housed the hamster-bedding output from a paper shredder, a more ventilated housing for the router was fashioned. Next, from the bowels of one of the sturdy Warcraft-ready difference engines, a surplus fan was retrieved, without disconnecting it from the umbilical cord which allowed its mother board to sustain it. This, placed atop the perforated bin with its blowing-inclined side downwards, proved exquisitely capable of maintaining the surface temperature of the Dread Device at something more akin to a drab, English wintery morning.&lt;BR&gt;&lt;BR&gt;And so the trollectomy was permitted to continue. Fortunately, the cricket, the iced cream, and more distantly the mantra-chanting, stripe-apparelled supporters of geopolitics were never disrupted. And, even more fortunately, the chances of anything coming from Mars were a million to one.&lt;BR&gt;&lt;img src="http://www.simple-talk.com/community/aggbug.aspx?PostID=870" width="1" height="1"&gt;</content><author><name>Dan J Archer</name><uri>http://www.simple-talk.com/community/user/Profile.aspx?UserID=2114</uri></author></entry><entry><title>The game's afoot</title><link rel="alternate" type="text/html" href="http://www.simple-talk.com/community/blogs/dana/archive/2006/05/10/784.aspx" /><id>http://www.simple-talk.com/community/blogs/dana/archive/2006/05/10/784.aspx</id><published>2006-05-10T13:58:00Z</published><updated>2006-05-10T13:58:00Z</updated><content type="html">I'd like to squeeze in another reference to the Killing Game Show, or
other Amiga classics, at this point. I really must wind up the emulator
and dig out the ADFs I made of my old games, at some point. Sadly I
lost the original machine when I sold a house, neglecting to clear out
lots of ancient goodies from the roof. Sigh. The beige box with the
chicken-head on the outside and B52 ROCK LOBSTER on the inside is no
more.&lt;br&gt;
&lt;br&gt;
What's he drivelling about? Well folks, that was a cunningly disguised
reference to my previous blog entry, Making Tracks, which referred to
the beta release of SQL Dependency Tracker. Why should I bring that up,
at this point? And why doesn't this blog make much sense, really, when
you look at it closely? The latter question I'm just going to have to
/shrug to. However, as to the former, I have an answer for you.&lt;br&gt;
&lt;br&gt;
We've recently released the full, 2.0 release version of SQL Dependency
Tracker, and it's really quite spiffy. Even if I do say so myself,
being project manager and co-developer. As I've been telling some
journalistic types over the last few days, it's an excellent way to
analyse the impact of potential changes to your database before you
make them, as a number of our beta testers and Friends of Red Gate have
discovered. It's also great for exploring your database. Even though we
didn't have time to put printing features into the app, all credit to
one of our beta users who cunningly used Dependency Tracker's export
features to paste a diagram into Excel, and printed up a wall-sized
block diagram of his database courtesy of Red Gate. The man has
determination and style.&lt;br&gt;
&lt;br&gt;Anyhoo. If you fancy getting your hands dirty, then why not go download
a free 14 day trial from:&lt;br&gt;
&lt;br&gt;
&lt;a href="http://www.red-gate.com/products/SQL_Dependency_Tracker/index.htm"&gt;http://www.red-gate.com/products/SQL_Dependency_Tracker/index.htm&lt;/a&gt;&lt;br /&gt;
&lt;br&gt;
Meantime, I'll stroll away reminiscing about 16-bit machines.&lt;br&gt;
&lt;br&gt;&lt;img src="http://www.simple-talk.com/community/aggbug.aspx?PostID=784" width="1" height="1"&gt;</content><author><name>Dan J Archer</name><uri>http://www.simple-talk.com/community/user/Profile.aspx?UserID=2114</uri></author></entry><entry><title>Making Tracks</title><link rel="alternate" type="text/html" href="http://www.simple-talk.com/community/blogs/dana/archive/2006/03/28/745.aspx" /><id>http://www.simple-talk.com/community/blogs/dana/archive/2006/03/28/745.aspx</id><published>2006-03-28T11:33:00Z</published><updated>2006-03-28T11:33:00Z</updated><content type="html">Feeling as we did that it was time to take longer strides (ah, the
Killing Game Show. A fine example of early 90's computer gaming) Red
Gate has just released the beta version of our shiny new SQL Dependency
Tracker.&lt;br&gt;
&lt;br&gt;
This is, in many ways, a long-planned revamp of last year's SQL
Dependency Viewer beta. We put that product out there to see what kind
of reaction it would get, and our feedback was very positive. Turns out
rather a lot of database developers and their managers could make good
use of such a tool. Doing a spot of research, we discovered that a
primary motivation for this for database developers was in order to do
impact analysis for a change about to be made to a database; and then
to document that change in diagrammatic form for use in the sort of
internal company documentation that's part of the outdated Waterfall
model process that so many of us know, hate, and still find ourselves
doing. (I must confess that I am no longer subject to such outmoded
blitherings since I've been at Red Gate Software, but I have in the past been found
desparately trying to fight my way out from 'neath piles of such paper).&lt;br&gt;
&lt;br&gt;
Digressions aside, you should find this up-and-coming new version
should be much tastier than the old. You can add dependencies
selectively to the diagram, and related dependencies will make their
way in automatically; so if you're about to make a change to a table
and want to see what references it, just drop the table onto the
diagram and all its "used by" dependencies will shuffle on obediently.
If objects on the diagram have references that aren't also on the
diagram, they'll display a little leftward stalk, and tooltippage gives
you more detail. Just right click the object to bring in its
dependencies. You can copy your diagram to the clipboard and paste it
into Word; or get an XML report or export a full or partial image of
your diagram.&lt;br&gt;
&lt;br&gt;
Of course you can move objects around, change the colours, pan and
zoom, load and save diagrams without reconnecting to the original
databases,
all that sort of jazz. More interestingly, we also (as I may have
hinted above) have the ability to track dependencies across databases,
and to show you unresolved references within databases (to eg. missing
stored procedures). In the final version you'll likely be able to
select an unresolved external dependency, frob the mouse, and shout
"make it so, engage" at the screen whilst SQL Dependency Tracker goes
and finds the database in question and pulls in your object.&lt;br&gt;
&lt;br&gt;
Under the hood, we're of course not relying on the evil that is the
sysdepends table; we make good use of the same technology that powers
the battleship SQL Compare to get down, if not dirty, with the database
schema directly.&lt;br&gt;
&lt;br&gt;
Anyhoo. If you find yourself with a spare moment and have
even a passing interest in a) tracking dependencies within and between
databases in SQL Server, or b) pretty diagrams, then do check out our
beta version. You can find it at
&lt;a href="http://www.red-gate.com/products/SQL_Dependency_Tracker/beta.htm"&gt;http://www.red-gate.com/products/SQL_Dependency_Tracker/beta.htm&lt;/a&gt;, and
there's a forum to post your questions, comments and problems at
&lt;a href="http://www.red-gate.com/MessageBoard/viewforum.php?f=40"&gt;http://www.red-gate.com/MessageBoard/viewforum.php?f=40&lt;/a&gt;. Seeing as how
I've been shepharding this product for the last couple of months, I'll
even reply to most postings there personally. Of course my colleagues
are also as able in forum penmanship as they are in cutting code, and
may take their turn in turn.&lt;br&gt;
&lt;br&gt;
&lt;br&gt;&lt;img src="http://www.simple-talk.com/community/aggbug.aspx?PostID=745" width="1" height="1"&gt;</content><author><name>Dan J Archer</name><uri>http://www.simple-talk.com/community/user/Profile.aspx?UserID=2114</uri></author></entry><entry><title>Wheels within wheels</title><link rel="alternate" type="text/html" href="http://www.simple-talk.com/community/blogs/dana/archive/2006/02/28/391.aspx" /><id>http://www.simple-talk.com/community/blogs/dana/archive/2006/02/28/391.aspx</id><published>2006-02-28T16:36:00Z</published><updated>2006-02-28T16:36:00Z</updated><content type="html">I hate to depart from my usual ranting and raving and actually post some useful code, but the below is too lovely to ignore.&lt;br&gt;
&lt;br&gt;
Before I proceed to take credit, I have to thank Google and the
following poster(s):
http://forums.microsoft.com/MSDN/ShowPost.aspx?PostID=228499&amp;amp;SiteID=1.
The below is a rather elementary explanation of the code posted therein, with a generic adaptation at the end.&lt;br&gt;
&lt;br&gt;
&lt;b&gt;Subject:&lt;/b&gt; The mouse wheel.&lt;br&gt;
&lt;br&gt;
The mouse wheel is an excellent widget for the scrolling with. One
simply waves it over a window and, without needing to give input focus
to that window, a few deft flicks of the wheel will scroll any
eminently scrollable window, moving the dull, tiresome and mundane
contents out of view and revealing the delicious, exciting, wonderous
contents to be found just enticingly off screen. If one were
extraordinarily closeted or on some serious drugs, it could even be a
fulfilling experience.&lt;br&gt;
&lt;br&gt;
In any case, this is the experience a lot of users are looking for (if
you remove the parts of the above description which relate to
excitement or interest in any way). Now in previous applications I'd
written, this proved an issue. Mouse wheel messages are only routed by
Windows to the window with input focus. Extraordinarily dumb? Why, yes,
I should coco. The immediate and obvious hack is to ensure that the
window under the mouse has input focus. As most users don't have
PowerToys for Windows installed (and, even if they do, rarely check the
"activate the window under the mouse cursor" option), this has to be
done by the application. Cheap, hideous hacks follow:&lt;br&gt;
&lt;br&gt;
&lt;font face="Courier New" size="2"&gt;&lt;font color="#0000ff"&gt;internal&lt;/font&gt; &lt;font color="#0000ff"&gt;class &lt;/font&gt;MyControl : UserControl&lt;br&gt;
{&lt;br&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; ...&lt;br&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; MyControl()&lt;br&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; {&lt;br&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; ....&lt;br&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;font color="#0000ff"&gt;this&lt;/font&gt;.MouseMove += &lt;font color="#0000ff"&gt;new &lt;/font&gt;MouseEventHandler(MyControl_MouseMove);&lt;br&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br&gt;
&lt;br&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;font color="#0000ff"&gt;private void&lt;/font&gt; MyControl_MouseMove(&lt;font color="#0000ff"&gt;object &lt;/font&gt;sender, MouseEventArgs e)&lt;br&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; {&lt;br&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;font color="#0000ff"&gt;this&lt;/font&gt;.Focus();&lt;br&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br&gt;
}&lt;br&gt;
&lt;/font&gt;&lt;br&gt;
Disgusting indeed. One problem with this is that every window which
wants mouse wheel input has to include this deviant hack. It also has
the disadvantage of vexing behaviour
in various cases, such as if you add dialogs containing text boxes into
the equation. Position such a dialog over your focus-stealing control,
put some text in the dialog's text box, and ask a user to select that
text. See the user drag select the text from right to left, ending up
with the mouse cursor outside the dialog and over your focus-stealing
control...which grabs the focus, thus deselecting the text and
preventing keyboard input to the dialog until the user clicks the text
field again. Better hope at that stage they keep the mouse within the
dialog...&lt;br&gt;
&lt;br&gt;
However, there is a much nicer solution, in the form of message
filters. Like the filter tips on cigarettes, message filters can
prevent carcinogenic messages from reaching the lungs of your controls.
Unlike filter tips, they can also do more or less anything with the
messages they filter out.&lt;br&gt;
&lt;br&gt;
Behind the scenes in a Windows Forms application, each user interface
thread of an application is running a message loop which resembles the
following:&lt;br&gt;
&lt;br&gt;
&lt;font face="Courier New" size="2"&gt;while ( GetMessage(&amp;amp;msg,0,0,0) )&lt;br&gt;
{&lt;br&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; TranslateMessage(&amp;amp;msg);&lt;br&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; DispatchMessage(&amp;amp;msg);&lt;br&gt;
}&lt;/font&gt;&lt;br&gt;
&lt;br&gt;
This should be familiar to any seasoned Win32 programmer. So messages
arrive in the message queue; take a quick slash and burn through
keyboard accelerator translation; and then are dispatched to the
destination window's window procedure (WndProc). I missed out a
pertinent detail, however...&lt;br&gt;
&lt;br&gt;
&lt;font face="Courier New" size="2"&gt;while ( GetMessage(&amp;amp;msg,0,0,0) )&lt;br&gt;
{&lt;br&gt;&lt;/font&gt;&lt;font face="Courier New" size="2"&gt;&lt;i&gt;&lt;font color="#ff0000"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; if ( Application.FilterMessage(&amp;amp;msg) ) // allow the application to filter out messages of its choosing&lt;br&gt;

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; continue;&lt;/font&gt;&lt;/i&gt;&lt;br&gt;
&lt;/font&gt;&lt;font face="Courier New" size="2"&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; TranslateMessage(&amp;amp;msg);&lt;br&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; DispatchMessage(&amp;amp;msg);&lt;br&gt;
}&lt;/font&gt;&lt;br&gt;
&lt;br&gt;
So, .NET is being very obliging here. The Application class is giving
us a window in which we can globally (per thread) filter out messages
before they hit their destination window. How incredibly evil! And,
useful.&lt;br&gt;
&lt;br&gt;
We can use this little loophole to detect when the mouse wheel is
scrolled when the focus is with any window. Then before the window gets
the message, we'll whisk it away under cover of darkness, and send it
to a more deserving candidate: viz. the window actually under the mouse
cursor at the time. Best of all, this requires no code whatsoever in
the recipient window. It just gets mouse wheel messages, and gratefully
processes them as normal.&lt;br&gt;
&lt;br&gt;
So, we take the following steps.&lt;br&gt;
&lt;br&gt;
1. Derive a class from IMessageFilter.&lt;br&gt;
2. Do what we want with messages therein.&lt;br&gt;
3. At an early stage in our application's lifetime, call Application.AddMessageFilter( new MyMessageFilterClass() ).&lt;br&gt;
4. Bingo.&lt;br&gt;
&lt;br&gt;
To make things really easy for you, here's some sample code in full.&lt;br&gt;




































































































































&lt;p class="MsoNormal"&gt;&lt;font color="#000000" face="Courier New"&gt;&lt;span&gt;using&lt;/span&gt;&lt;span&gt; System;&lt;o:p&gt;&lt;/o:p&gt;&lt;br&gt;
using&lt;/span&gt;&lt;span&gt; System.Windows.Forms;&lt;o:p&gt;&lt;/o:p&gt;&lt;br&gt;
using&lt;/span&gt;&lt;span&gt; System.Runtime.InteropServices;&lt;o:p&gt;&lt;br&gt;
&amp;nbsp;&lt;/o:p&gt;&lt;br&gt;
namespace&lt;/span&gt;&lt;/font&gt;&lt;span&gt;&lt;font color="#000000" face="Courier New"&gt; MyCompany.MyApplication.UI&lt;o:p&gt;&lt;/o:p&gt;&lt;br&gt;

{&lt;o:p&gt;&lt;/o:p&gt;&lt;br&gt;
&lt;/font&gt;
&lt;font color="#000000" face="Courier New"&gt;&lt;span&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;&lt;span&gt;///&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&amp;lt;summary&amp;gt;&lt;o:p&gt;&lt;/o:p&gt;&lt;br&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;&lt;span&gt;///&lt;/span&gt;&lt;span&gt; Ensures that all mouse
wheel messages are dispatched to the&lt;o:p&gt;&lt;/o:p&gt;&lt;br&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;&lt;span&gt;///&lt;/span&gt;&lt;span&gt; window under the
cursor, rather than the window with input&lt;o:p&gt;&lt;/o:p&gt;&lt;br&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;&lt;span&gt;///&lt;/span&gt;&lt;span&gt; focus.&lt;o:p&gt;&lt;/o:p&gt;&lt;br&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;&lt;span&gt;///&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/font&gt;&lt;font color="#000000" face="Courier New"&gt;&lt;span&gt;&amp;lt;/summary&amp;gt;&lt;o:p&gt;&lt;/o:p&gt;&lt;br&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;&lt;span&gt;public&lt;/span&gt; &lt;span&gt;class&lt;/span&gt;
MouseWheelMessageFilter : IMessageFilter&lt;o:p&gt;&lt;/o:p&gt;&lt;br&gt;
&lt;span&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;{&lt;o:p&gt;&lt;/o:p&gt;&lt;br&gt;
&lt;span&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;[StructLayout(LayoutKind.Sequential)]&lt;o:p&gt;&lt;/o:p&gt;&lt;br&gt;
&lt;span&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;&lt;span&gt;private&lt;/span&gt; &lt;span&gt;struct&lt;/span&gt; POINT&lt;o:p&gt;&lt;/o:p&gt;&lt;br&gt;
&lt;span&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;{&lt;o:p&gt;&lt;/o:p&gt;&lt;br&gt;
&lt;span&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;&lt;span&gt;public&lt;/span&gt; &lt;span&gt;int&lt;/span&gt; x;&lt;o:p&gt;&lt;/o:p&gt;&lt;br&gt;
&lt;span&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;&lt;span&gt;public&lt;/span&gt; &lt;span&gt;int&lt;/span&gt; y;&lt;o:p&gt;&lt;/o:p&gt;&lt;br&gt;
&lt;span&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;}&lt;o:p&gt;&lt;br&gt;
&amp;nbsp;&lt;/o:p&gt;&lt;br&gt;
&lt;span&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;[DllImport("user32.dll")]&lt;o:p&gt;&lt;/o:p&gt;&lt;br&gt;
&lt;span&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;&lt;span&gt;private&lt;/span&gt; &lt;span&gt;static&lt;/span&gt; &lt;span&gt;extern&lt;/span&gt; IntPtr WindowFromPoint([In]
MouseWheelMessageFilter.POINT point);&lt;o:p&gt;&lt;br&gt;
&amp;nbsp;&lt;/o:p&gt;&lt;br&gt;
&lt;span&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;[DllImport("user32.dll")]&lt;o:p&gt;&lt;/o:p&gt;&lt;br&gt;
&lt;span&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;&lt;span&gt;public&lt;/span&gt; &lt;span&gt;static&lt;/span&gt; &lt;span&gt;extern&lt;/span&gt; &lt;span&gt;int&lt;/span&gt;
SendMessage(IntPtr hWnd, &lt;span&gt;uint&lt;/span&gt; msg, IntPtr
wParam, IntPtr lParam);&lt;o:p&gt;&lt;br&gt;
&amp;nbsp;&lt;/o:p&gt;&lt;br&gt;
&lt;span&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;&lt;span&gt;private&lt;/span&gt; &lt;span&gt;const&lt;/span&gt; &lt;span&gt;int&lt;/span&gt; WM_MOUSEWHEEL = 0x020A;&lt;o:p&gt;&lt;br&gt;
&amp;nbsp;&lt;/o:p&gt;&lt;br&gt;
&lt;span&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;&lt;span&gt;private&lt;/span&gt; &lt;span&gt;uint&lt;/span&gt;
LOWORD(IntPtr &lt;span&gt;value&lt;/span&gt;)&lt;o:p&gt;&lt;/o:p&gt;&lt;br&gt;
&lt;span&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;{&lt;o:p&gt;&lt;/o:p&gt;&lt;br&gt;
&lt;span&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;&lt;span&gt;return&lt;/span&gt; (&lt;span&gt;uint&lt;/span&gt;)( ( ((&lt;span&gt;ulong&lt;/span&gt;)&lt;span&gt;value&lt;/span&gt;.ToInt64())
&amp;amp; 0xffff ) );&lt;o:p&gt;&lt;/o:p&gt;&lt;br&gt;
&lt;span&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;}&lt;o:p&gt;&lt;br&gt;
&amp;nbsp;&lt;/o:p&gt;&lt;br&gt;
&lt;span&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;&lt;span&gt;private&lt;/span&gt; &lt;span&gt;uint&lt;/span&gt;
HIWORD(IntPtr &lt;span&gt;value&lt;/span&gt;)&lt;o:p&gt;&lt;/o:p&gt;&lt;br&gt;
&lt;span&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;{&lt;o:p&gt;&lt;/o:p&gt;&lt;br&gt;
&lt;span&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;&lt;span&gt;return&lt;/span&gt; (&lt;span&gt;uint&lt;/span&gt;)( ( ((&lt;span&gt;ulong&lt;/span&gt;)&lt;span&gt;value&lt;/span&gt;.ToInt64())
&amp;amp; 0xffff0000 ) &amp;gt;&amp;gt; 16 );&lt;o:p&gt;&lt;/o:p&gt;&lt;br&gt;
&lt;span&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;}&lt;o:p&gt;&lt;br&gt;
&amp;nbsp;&lt;/o:p&gt;&lt;br&gt;
&lt;span&gt;&lt;/span&gt;&lt;span&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;&lt;span&gt;public&lt;/span&gt; &lt;span&gt;bool&lt;/span&gt;
PreFilterMessage(&lt;span&gt;ref&lt;/span&gt; Message m)&lt;o:p&gt;&lt;/o:p&gt;&lt;br&gt;
&lt;span&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;{&lt;o:p&gt;&lt;/o:p&gt;&lt;br&gt;
&lt;span&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;&lt;span&gt;if&lt;/span&gt; ( m.Msg == WM_MOUSEWHEEL )&lt;o:p&gt;&lt;/o:p&gt;&lt;br&gt;
&lt;span&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;{&lt;o:p&gt;&lt;/o:p&gt;&lt;br&gt;
&lt;span&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;
&lt;/span&gt;&lt;span&gt;uint&lt;/span&gt; screenX = LOWORD(m.LParam);&lt;o:p&gt;&lt;/o:p&gt;&lt;br&gt;
&lt;span&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;
&lt;/span&gt;&lt;span&gt;uint&lt;/span&gt; screenY = HIWORD(m.LParam);&lt;o:p&gt;&lt;br&gt;
&amp;nbsp;&lt;/o:p&gt;&lt;br&gt;
&lt;span&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;
&lt;/span&gt;MouseWheelMessageFilter.POINT
point = &lt;span&gt;new&lt;/span&gt; MouseWheelMessageFilter.POINT();&lt;o:p&gt;&lt;/o:p&gt;&lt;br&gt;
&lt;span&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;
&lt;/span&gt;point.x
= (&lt;span&gt;int&lt;/span&gt;)screenX;&lt;o:p&gt;&lt;/o:p&gt;&lt;br&gt;
&lt;span&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;
&lt;/span&gt;point.y
= (&lt;span&gt;int&lt;/span&gt;)screenY;&lt;o:p&gt;&lt;br&gt;
&amp;nbsp;&lt;/o:p&gt;&lt;br&gt;
&lt;span&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;
&lt;/span&gt;IntPtr
hWnd = MouseWheelMessageFilter.WindowFromPoint( point );&lt;o:p&gt;&lt;/o:p&gt;&lt;br&gt;
&lt;span&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;
&lt;/span&gt;&lt;span&gt;if&lt;/span&gt; ( hWnd != IntPtr.Zero )&lt;o:p&gt;&lt;/o:p&gt;&lt;br&gt;
&lt;span&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;
&lt;/span&gt;{&lt;o:p&gt;&lt;/o:p&gt;&lt;br&gt;
&lt;span&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;
&lt;/span&gt;MouseWheelMessageFilter.SendMessage(hWnd,(&lt;span&gt;uint&lt;/span&gt;)m.Msg, m.WParam, m.LParam);&lt;o:p&gt;&lt;/o:p&gt;&lt;br&gt;
&lt;span&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;
&lt;/span&gt;}&lt;o:p&gt;&lt;br&gt;
&amp;nbsp;&lt;/o:p&gt;&lt;br&gt;
&lt;span&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;
&lt;/span&gt;&lt;span&gt;return&lt;/span&gt; &lt;span&gt;true&lt;/span&gt;; &lt;span&gt;// stop this message being dispatched&lt;o:p&gt;&lt;/o:p&gt;&lt;br&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;}&lt;o:p&gt;&lt;br&gt;
&amp;nbsp;&lt;/o:p&gt;&lt;br&gt;
&lt;span&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;&lt;span&gt;return&lt;/span&gt; &lt;span&gt;false&lt;/span&gt;;&lt;o:p&gt;&lt;/o:p&gt;&lt;br&gt;
&lt;span&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;}&lt;o:p&gt;&lt;/o:p&gt;&lt;br&gt;
&lt;span&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;}&lt;o:p&gt;&lt;/o:p&gt;&lt;br&gt;

}&lt;/font&gt;





































&lt;o:p&gt;&lt;/o:p&gt;&lt;/span&gt;&lt;/p&gt;

Let's take a quick look through this code.&lt;br&gt;
&lt;br&gt;
Firstly, we declare some helpful Win32 glue. We need a
binarily-compatible .NET equivalent of the Win32 POINT structure. We
also need to make native calls to the WindowFromPoint() and
SendMessage() APIs from user32.dll, so we pull them in.&lt;br&gt;
&lt;br&gt;
Then the meat of the message filter. We implement
IMessageFilter.PreFilterMessage(). If the message is a mouse wheel
message, we extract the cursor position (in screen coordinates) from
the message parameters, and see which of our windows is under those
coordinates. (Nowdays this function only tells you about windows owned
by your thread of your process, so no need to worry about accidentally
enabling this functionality across all the user's applications.) Then
we immediately route the message directly to that window's WndProc. We
call SendMessage() rather than PostMessage(), since the latter would
add the message to the message queue, where it would be retrieved by
.NET's GetMessage() call and we'd go round and round to infinite.
SendMessage() dispatches the message directly to the window procedure,
bypassing the message queue. It's also faster.&lt;br&gt;
&lt;br&gt;
So there we go. Now don't say I never give you anything.&lt;br&gt;&lt;img src="http://www.simple-talk.com/community/aggbug.aspx?PostID=391" width="1" height="1"&gt;</content><author><name>Dan J Archer</name><uri>http://www.simple-talk.com/community/user/Profile.aspx?UserID=2114</uri></author></entry><entry><title>Trivial Persuits</title><link rel="alternate" type="text/html" href="http://www.simple-talk.com/community/blogs/dana/archive/2006/02/07/245.aspx" /><id>http://www.simple-talk.com/community/blogs/dana/archive/2006/02/07/245.aspx</id><published>2006-02-07T12:43:00Z</published><updated>2006-02-07T12:43:00Z</updated><content type="html">Once again I find myself penning a missive on trivia, rather than matters of import. Still, now we're here...&lt;br&gt;

&lt;br&gt;

Regular expressions. To a large extent, I hate them. To begin with,
they are a complete misnominer. They are neither regular, nor
expressions. Don't believe me? To demonstrate this I'll consult an
authority on such topics. The Oxford English Dictionary would be handy,
but being incredibly lax I don't seem to have a copy lying around.
However, dictionary.com will do...&lt;br&gt;

&lt;br&gt;

&lt;blockquote&gt;&lt;b&gt;reg·u·lar&lt;/b&gt;&lt;br&gt;
  &lt;i&gt;adj.&lt;/i&gt;&lt;br&gt;
  &lt;br&gt;
&amp;nbsp;&amp;nbsp; 1. Customary, usual, or normal: the train's regular schedule.&lt;br&gt;
&amp;nbsp;&amp;nbsp; 2. Orderly, even, or symmetrical: regular teeth.&lt;br&gt;
&amp;nbsp;&amp;nbsp; 3. In conformity with a fixed procedure, principle, or discipline.&lt;br&gt;
&amp;nbsp;&amp;nbsp; 4. Well-ordered; methodical: regular habits.&lt;br&gt;
&amp;nbsp;&amp;nbsp; 5. Occurring at fixed intervals; periodic: regular payments.&lt;br&gt;
&amp;nbsp;&amp;nbsp; 6.&lt;br&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 1. Occurring with normal or healthy frequency.&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;
2. Having bowel movements or menstrual periods with normal or healthy
frequency.&lt;br&gt;
&amp;nbsp;&amp;nbsp; 7. Not varying; constant.&lt;br&gt;
&amp;nbsp;&amp;nbsp; 8. Formally correct; proper.&lt;br&gt;
&amp;nbsp;&amp;nbsp; 9. Having the required qualifications for an occupation: not a regular lawyer.&lt;br&gt;
&amp;nbsp; 10. Informal. Complete; thorough: a regular scoundrel.&lt;br&gt;
&amp;nbsp; 11. Informal. Good; nice: a regular guy.&lt;br&gt;
&amp;nbsp; 12. Botany. Having symmetrically arranged parts of similar size and shape: regular flowers.&lt;br&gt;
&amp;nbsp; 13. Grammar. Conforming to the usual pattern of inflection, derivation, or word formation.&lt;br&gt;
&amp;nbsp; 14. Ecclesiastical. Belonging to a religious order and bound by its rules: the regular clergy.&lt;br&gt;
&amp;nbsp; 15. Mathematics.&lt;br&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 1. Having equal sides and equal angles. Used of polygons.&lt;br&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 2. Having faces that
are congruent regular polygons and congruent polyhedral angles. Used of
polyhedrons.&lt;br&gt;
&amp;nbsp; 16. Belonging to or constituting the permanent army of a nation.&lt;br&gt;
  &lt;br&gt;
  &lt;br&gt;
  &lt;b&gt;ex·pres·sion&lt;/b&gt;&lt;br&gt;
  &lt;i&gt;n.&lt;/i&gt;&lt;br&gt;
  &lt;br&gt;
&amp;nbsp;&amp;nbsp; 1. The act of expressing, conveying, or representing in words, art,
music, or movement; a manifestation: an expression of rural values.&lt;br&gt;
&amp;nbsp;&amp;nbsp; 2. Something that expresses or communicates: Let this plaque serve as an expression of our esteem.&lt;br&gt;
&amp;nbsp;&amp;nbsp; 3. Mathematics. A symbol or combination of symbols that represents a quantity or a relationship between quantities.&lt;br&gt;
&amp;nbsp;&amp;nbsp; 4. The manner in which one expresses oneself, especially in speaking, depicting, or performing.&lt;br&gt;
&amp;nbsp;&amp;nbsp; 5. A particular word or phrase: “an old Yankee expression... ‘Stand up and be counted’” (Charles Kuralt).&lt;br&gt;
&amp;nbsp;&amp;nbsp; 6. The outward manifestation of a mood or a disposition: My tears are an expression of my grief.&lt;br&gt;
&amp;nbsp;&amp;nbsp; 7. A facial aspect or a look that conveys a special feeling: an expression of scorn.&lt;br&gt;
&amp;nbsp;&amp;nbsp; 8. The act of pressing or squeezing out.&lt;br&gt;
&amp;nbsp;&amp;nbsp; 9. Genetics. The act or process of expressing a gene.&lt;br&gt;
&lt;/blockquote&gt;

Now, I know someone's going to tell me that if you look up "regular
expression" on dictionary.com it gives you the "proper" definition. But
to that I say, bah. There's far too much rather pants terminology in
the world, particularly in the computing arena. Next you'll be telling
me that "non-deterministic finite state automaton" is easier to say
than "state machine". And put those 5-tuples away, you don't know where
they've been. It's not big and it's not clever.&lt;br&gt;

&lt;br&gt;

Without getting overly semantic (though I may deviate from the
canonical definition of "overly" here) regular expressions are not
regular. There are at least three different flavours of which I'm
aware, all of which have their own magical incantations:
Perl-compatible (as seen in Perl - shocking I know - PHP, and the open
source PCRE engine, http://pcre.org); Microsoft (as seen in Visual
Studio and .NET's regular expression library); and Java (as seen in,
well, Java). "Expressions" expressed in one dialect are not necessarily
freely transferable to the other. This doesn't meet any sensible
definition of regular that I'm aware of. "Self consistent" would
perhaps be more appropriate.&lt;br&gt;

&lt;br&gt;

Nor are they expressions. Expressions tend, by definition, to express
something to somebody. I blame Perl entirely for this. Any "high level"
language in which software can be expressed entirely without reference
to any alphabet used by a civilisation, past or present, cannot claim
to adequately express anything. If I wanted unreadable gumf, I'd write
software in pure machine code. With a decent lookup table and a few
cups of coffee it wouldn't be much less straightforward.&lt;br&gt;

&lt;br&gt;
However, despite their inadequate definition and manifest unreadability, they do have some extraordinary merits.&lt;br&gt;
&lt;br&gt;
Today, for example, 10 minutes of faffing around trying to remember
which bits of the Visual Studio RE (see, even handy, descriptive
phrases like "regular expression" are too long to type occasionally)
syntax are Microsoft-specific, and which are generally applicable, in
which time I eventually resorted to the help...proved to be exceedingly
handy.&lt;br&gt;
&lt;br&gt;
Our software tends to be sold in many international markets, and thus
internationalisation (I18N; which isn't a regular expression, but
another bludgeoningly stupid acronym bordering on haxx0r lingo) and
localisation (L10N, if I recall...but please don't tell me if I'm
wrong, I'd like to keep it as a surprise) is an issue for us. All the
strings in our source code need to be pulled out of resources, rather
than hard-coded. This is sensible practice anyway, but means you end up
not being able to express text literally but dereferencing it.&lt;br&gt;
&lt;br&gt;
Previously I was doing as follows. Rather than expressing, eg.&lt;br&gt;
&lt;br&gt;
&lt;font face="Courier New" size="2"&gt;&lt;font color="#0000ff"&gt;namespace &lt;/font&gt;MyCompany.MyApp&lt;br&gt;
{&lt;br&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;font color="#0000ff"&gt;class &lt;/font&gt;HelloWorld()&lt;br&gt;
&lt;/font&gt;&lt;font face="Courier New" size="2"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/font&gt;&lt;font face="Courier New" size="2"&gt;{&lt;br&gt;
&lt;/font&gt;&lt;font face="Courier New" size="2"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/font&gt;&lt;font face="Courier New" size="2"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; HelloWorld()&lt;br&gt;
&lt;/font&gt;&lt;font face="Courier New" size="2"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/font&gt;&lt;font face="Courier New" size="2"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; {&lt;br&gt;
&lt;/font&gt;&lt;font face="Courier New" size="2"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/font&gt;&lt;font face="Courier New" size="2"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;font color="#0000ff"&gt;string &lt;/font&gt;name = &lt;font color="#800080"&gt;"world"&lt;/font&gt;;&lt;br&gt;
&lt;/font&gt;&lt;font face="Courier New" size="2"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/font&gt;&lt;font face="Courier New" size="2"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;font color="#0000ff"&gt;string &lt;/font&gt;message = &lt;font color="#0000ff"&gt;string&lt;/font&gt;.Format(&lt;font color="#800080"&gt;"Hello, {0}"&lt;/font&gt;,name);&lt;br&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; Debug.WriteLine(message);&lt;br&gt;
&lt;/font&gt;&lt;font face="Courier New" size="2"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/font&gt;&lt;font face="Courier New" size="2"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br&gt;
&lt;/font&gt;&lt;font face="Courier New" size="2"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/font&gt;&lt;font face="Courier New" size="2"&gt;}&lt;br&gt;
}&lt;br&gt;
&lt;br&gt;
&lt;/font&gt;One might write:&lt;br&gt;
&lt;br&gt;
&lt;font face="Courier New" size="2"&gt;&lt;font color="#0000ff"&gt;namespace &lt;/font&gt;MyCompany.MyApp&lt;br&gt;
{&lt;br&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;font color="#0000ff"&gt;class &lt;/font&gt;HelloWorld&lt;br&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; {&lt;br&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;font color="#0000ff"&gt;private static &lt;/font&gt;ResourceManager m_Resources = &lt;font color="#0000ff"&gt;new &lt;/font&gt;ResourceManager(&lt;font color="#800080"&gt;"MyCompany.MyApp.HelloWorldClass_Resources"&lt;/font&gt;);&lt;br&gt;
&lt;br&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; HelloWorld()&lt;br&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; {&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;  &lt;br&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;font color="#0000ff"&gt;string &lt;/font&gt;name = m_Resources.GetString(&lt;font color="#800080"&gt;"Name.Text"&lt;/font&gt;);&lt;br&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;font color="#0000ff"&gt;string &lt;/font&gt;message = &lt;font color="#0000ff"&gt;string&lt;/font&gt;.Format( m_Resources.GetString(&lt;font color="#800080"&gt;"HelloWorldMessage.Text"&lt;/font&gt;), name );&lt;br&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; Debug.WriteLine(message);&lt;br&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br&gt;
}&lt;br&gt;
&lt;/font&gt;&lt;br&gt;
Which is not overlong, but is a bit less expressive. Note how I decided
at some point to adopt a "dotted text" convention for my resource
string names at some point. Not a particularly clever idea, in
retrospect, since most of them end in ".Text", except when I decide
they should end in ".Caption" as they represent eg. a button caption on
a form. Of course then you discover that the button's property is also
called Text rather than Caption, but there'd be retyping involved in
changing it...and so on.&lt;br&gt;
&lt;br&gt;
Nowadays I've adopted a rather simpler syntax. Assuming the existence
of a handy UI class with some helpful static methods, I tend to write
as follows:&lt;br&gt;
&lt;br&gt;
&lt;font face="Courier New" size="2"&gt;&lt;font color="#0000ff"&gt;namespace &lt;/font&gt;MyCompany.MyApp&lt;br&gt;
{&lt;br&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;font color="#0000ff"&gt;class &lt;/font&gt;HelloWorld&lt;br&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; {&lt;br&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;font color="#0000ff"&gt;private static &lt;/font&gt;ResourceStrings S = UI.GetResourceStrings(&lt;font color="#0000ff"&gt;typeof&lt;/font&gt;(HelloWorld));&lt;br&gt;
&lt;br&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; HelloWorld()&lt;br&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; {&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;  &lt;br&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; Debug.WriteLine( &lt;font color="#0000ff"&gt;string&lt;/font&gt;.Format( S[&lt;font color="#800080"&gt;"HelloX"&lt;/font&gt;], S[&lt;font color="#800080"&gt;"World"&lt;/font&gt;] ) );&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br&gt;
}&lt;br&gt;
&lt;/font&gt;&lt;br&gt;

Which I think is remarkably tidier. Probably because it reminds me of the Visual C++&lt;font face="Courier New"&gt; &lt;font color="#008000" size="2"&gt;_T()&lt;/font&gt;&lt;/font&gt; macro to encapsulate text which, at compile time, may or may not turn out to be Unicode.&lt;br&gt;
&lt;br&gt;
Anyhoo. The point was, that having adopted this new convention I was
faced with having to change a number of source files to use the new
style rather than the old.&lt;br&gt;
&lt;br&gt;
Conventional search-and-replace is of little value here. Replacing &lt;font color="#008000" face="Courier New" size="2"&gt;m_Resources.GetString("SomeText")&lt;/font&gt; with &lt;font color="#008000" face="Courier New" size="2"&gt;S["SomeText"]&lt;/font&gt; can't quite be done. You could try it in two parts, replacing &lt;font color="#008000" face="Courier New" size="2"&gt;m_Resources.GetString("&lt;/font&gt; with &lt;font color="#008000" face="Courier New" size="2"&gt;S["&lt;/font&gt;, and replacing &lt;font color="#008000" face="Courier New" size="2"&gt;")&lt;/font&gt; with &lt;font color="#008000" face="Courier New" size="2"&gt;"]&lt;/font&gt;, but you're bound to find a number of &lt;font color="#008000" face="Courier New" size="2"&gt;")&lt;/font&gt;'s littered all over the shop which you'll have to fix manually, that belong to other things than a &lt;font color="#008000" face="Courier New" size="2"&gt;GetString()&lt;/font&gt; call.&lt;br&gt;
&lt;br&gt;
With regular expressions, however, you can make the hougan work his
vodun for you. Since Visual Studio's Find/Replace widget supports
regular expressions, one can incant the following:&lt;br&gt;
&lt;br&gt;
Find all&amp;nbsp;&amp;nbsp; &lt;font color="#008000" face="Courier New" size="2"&gt;m_Resources.GetString\({[^\)]#}\)&lt;/font&gt;&lt;br&gt;
And replace with&amp;nbsp;&amp;nbsp; &lt;font color="#008000" face="Courier New" size="2"&gt;S[\(0,1)]&lt;/font&gt;&lt;br&gt;
&lt;br&gt;
Once the sparks have settled down and the broom has finished sweeping
the magician's workshop, one finds cheerfully that tedium has been
averted and it's time for lunch.&lt;br&gt;
&lt;br&gt;
For those who are relentlessly interested in regular expressions, I've
also just found the aptly named http://www.regular-expressions.info/,
which does exactly what it says on the tin, if URL's were to come in
tins. URL. Don't get me started on that little moniker...&lt;br&gt;
&lt;br&gt;&lt;img src="http://www.simple-talk.com/community/aggbug.aspx?PostID=245" width="1" height="1"&gt;</content><author><name>Dan J Archer</name><uri>http://www.simple-talk.com/community/user/Profile.aspx?UserID=2114</uri></author></entry><entry><title>Super size me</title><link rel="alternate" type="text/html" href="http://www.simple-talk.com/community/blogs/dana/archive/2005/12/12/64.aspx" /><id>http://www.simple-talk.com/community/blogs/dana/archive/2005/12/12/64.aspx</id><published>2005-12-12T15:12:00Z</published><updated>2005-12-12T15:12:00Z</updated><content type="html">Having fixed all the outstanding "Oh my [deity/dictator of choice], we have to fix this &lt;B&gt;now&lt;/B&gt; or people will be really rather unhappy" issues (as they're referred to under our bug tracking system. Or is that my imagination? You know, I think it probably is) against DTS Package Compare 2.0, it was time to turn attention to some more of those niggling little issues which don't really make much of a difference to the masses, but you know are really going to irritate somebody at some point.&lt;BR&gt;&lt;BR&gt;&lt;FONT face=Arial&gt;&lt;B&gt;Remember, Remember...&lt;/B&gt;&lt;/FONT&gt;&lt;BR&gt;&lt;BR&gt;Applications should remember the state they were in when you last ran them, goes an unwritten tenet. The old version of DTS Compare 1.5 didn't do this (very naughty of it), and so we'd duly carried this bug over to the new DTS Package Compare 2.0 to make sure we fixed it. For some time, including our private beta, the application had gotten away with simply maximising itself to the primary monitor on the user's system, and this was generally splendid, if extraordinarily cheap. But since lots of other applications do recall where they were when you were last using them, we shouldn't really be the exception.&lt;BR&gt;&lt;BR&gt;Now I don't like working in this little simple looking area of user interface programming, as it rarely gets done properly. Take well known spreadsheet and word processing applications, for example: I've on several occasions had to help out colleagues or family members who started the application only to have it appear on the Windows taskbar but not exhibit any notable window in any way. Now those of us In The Know can often fix these problems by activating the (rather non-visible) window, pressing Alt-Space then M (for the system menu's "Move" option on that window, which you could of course pick with the mouse...if you could see the window in question), and pressing cursor keys for a while until the window appears. In these cases the application just got confused (or you've dropped your screen resolution rather substantially since last running the app) and positioned its window offscreen. So moving it back on screen tends to help. Occasionally of course some well known applications get themselves into such a pickle that you have to start mumbling mantras like "Aich kee current user, Software, Vendorname, ApplicationName, ah let's just delete everything" in order to fix the problem. And should you invoke such voodoo at your workstation, expect your computer to notice if you have a microphone attached, and a booming voice to warn you that messing with the registry can cause infertility, global warming, and seriously damage the economy of small countries. Well, not really. But in any case it's not a fun way to spend a morning when you haven't had your second coffee.&lt;BR&gt;&lt;BR&gt;I also have to mention, as an aside, a well known brand of developer tool which can occasionally encounter fatal problems loading projects. It also has a setting to load the last project when you open the tool. Windows of course now has a setting whereby if it encounters a fatal problem it will automatically restart the troubled application. These three work together in an amusing fashion: sepuku.mpg on a loop.&lt;BR&gt;&lt;BR&gt;&lt;B&gt;&lt;FONT face=Arial&gt;The Registry&lt;/FONT&gt;&lt;/B&gt;&lt;BR&gt;&lt;BR&gt;The point being that simply dumping out window positions as reported by Windows and stuffing such dumps into the registry is not generally clever. This situation is not helped if you're using third party user interface libraries, which helpfully provide mechanisms to do exactly this, thus saving you the bother of considering whether it's a good idea since you can simply call them, rebuild, and close any bugs you might have in this area, before cheerfully strolling down to the boozer.&lt;BR&gt;&lt;BR&gt;There are two principal reasons why this approach is, IMHO, not a wise plan. The first is that stuffing user-related data into the registry is now frowned upon in many circles. Let's be honest, it wasn't a great place to put things in anyway. The registry was a wonderful new way of crossing interstellar distances without all that tedious messing about with INI files, but INI files were handy as you could usually just delete them without breaking applications. Deleting things from the registry is not an activity for the faint hearted, at least before they've had a few drinks, and they're likely to regret it in the morning anyway. (At least one headache will result. For some reason I have a fleeting image of a PC booting, looking in its registry and discovering an unexpected traffic cone planted in HKEY_LOCAL_MACHINE, and then promptly BSOD'ing with an 0x80040200 NoIdeaWhatHappenedLastNightError. But then I may just need a holiday.) INI files were accessible and user-visible; the registry is neither nor. Luckily, Windows nowadays provides applications with an Application Data folder for each user (and a choice whether this should accompany roaming profiles, or stay doggedly attached to the current workstation) in which they can place their own settings files. And, hopefully, react well if a user comes along and gleefully trashes them all.&lt;BR&gt;&lt;BR&gt;&lt;B&gt;&lt;FONT face=Arial&gt;Lies, Damned Lies, and Window Positions&lt;/FONT&gt;&lt;/B&gt;&lt;BR&gt;&lt;BR&gt;The second reason, which was originally by way of being the subject of this article, is that Windows lies about window positions. This is hard for non-programmers to see (but probably easier to believe).&lt;BR&gt;&lt;BR&gt;If you minimise a window, for instance, and then ask Windows where it is (the Win32 API GetWindowRect), it will tell you that it's top left corner is at coordinates (-32000, -32000). Skeptics, realising that the top left of the screen is (0,0), and the bottom right usually say (1024,768) or similar, may doubt the verisimilitude of this report. And with good reason. What Windows means is that the Window is minimised, and so it isn't going to tell you where the Window actually is. That's no longer any of your concern. If you wanted to know where the Window was when it wasn't minimised, you should have asked before the user went and minimised it. Silly application programmer.&lt;BR&gt;&lt;BR&gt;And indeed if you maximise a window, and then ask Windows where it is, it will tell you that its top left corner is at coordinates are of the order of (0,-4).&amp;nbsp; Skeptics might be willing to let that pass, as they might deduce that Windows has, in order to make the window take up the whole screen, moved the top of it slightly off the screen.&lt;BR&gt;&lt;BR&gt;Now one could ignore these little foibles. But of course, just because a minimised window has coordinates (-32000,-32000) doesn't mean that setting a window's coordinates to (-32000,-32000) will make it minimised. That would be silly. No, windows have a fairly sensible state that can be detected, amongst other things, via the Win32 API GetWindowPlacement. This window state (also referred to as its ShowWindow flags or Show Command) is either SW_SHOW, SW_HIDE, SW_MINIMIZE, SW_MAXIMIZE, or other variations on the theme. This, not the window rectangle, is your true indicator of whether a window is minimised, maximised or regular. And in order to super size (or sub size) your window, you'll have to set this window state directly.&lt;BR&gt;&lt;BR&gt;So. One could save this state with the application; save the window coordinates as well; and restore them both when the application is next started. Correct? Er, no. Because as previously mentioned, the coordinates returned when a window is minimsed or maximised are not the coordinates you should be saving. Those are the coordinates imposed on the window due to its minimised/maximised state, which do not reflect the actual window position prior to this occurring. A window might be 100 by 100 pixels in the bottom right of the screen, for example, but be maximised to take up the full screen. If you save its position after it's been maximised, and then when the app is run set the maximised state and position, then when the user un-maximises (restores) the window it will not return to its diminutive location in the bottom right, but stay taking up the full screen.&lt;BR&gt;&lt;BR&gt;So in terms of remembering window position to save on application exit, one has to watch when window sizes and positions change. One then has to record the true position of the window before the user minimises or maximises it, and ignore the window position (not record it) if that window is currently minimised or maximised. Then, on exit, you save the last recorded position, along with whether the window is minimised or maximised. On startup, you restore this information, and Bob is a close relative.&lt;BR&gt;&lt;BR&gt;In C#, you do this via the Form.WindowState property (which accepts Maximized, Minimized or Normal) and the Form.Location and Form.Size properties. This maps to calling the Win32 APIs GetWindowRect() (for coordinates) and GetWindowPlacement() (for window state).&lt;BR&gt;&lt;BR&gt;&lt;B&gt;&lt;FONT face=Arial&gt;Screening for Problems&lt;BR&gt;&lt;/FONT&gt;&lt;/B&gt;&lt;BR&gt;Of course it's not as easy as that. This is software. Just because you're trying to do something so trivial that it should be possible whilst drinking coffee and eating doughnuts, doesn't mean that you've got a hope of getting it done swiftly.&lt;BR&gt;&lt;BR&gt;The above works pretty well, but then what if the user has multiple monitors? Under .NET, saving the position works fine, but restoring it doesn't. Specifically, if the window is maximised on the &lt;I&gt;second &lt;/I&gt;monitor when you quit the application, when you restore it it will be maximised on the &lt;I&gt;first&lt;/I&gt; monitor. However, if you then restore (de-maximise) it, it will return to its non-maximised position on the second monitor. Clear? Jolly good.&lt;BR&gt;&lt;BR&gt;The reason is, it turns out, that .NET sets the window rectangle through the Win32 SetWindowPos API(). This should be fine, as it accepts all the relevant parameters, and in most cases works well. Sadly, this API doesn't deal well with muliple monitors: the above bizarrety occurs.&lt;BR&gt;&lt;BR&gt;Of course there is a solution: the GetWindowPlacement() API, previously referred to, can retrieve window coordinates as well as window state. And there is a corresponding SetWindowPlacement() API which accepts both pieces of information, and behaves correctly. (Indeed, coordinates returned from GetWindowPlacement() can only be provided to SetWindowPlacement(), and not eg. SetWindowPos(): they're special in some irritating fashion.) The only minor downside (apart from having spent valuable beer time figuring this out) is that from .NET one has to invoke native code, and DllImport GetWindowPlacement and SetWindowPlacement from user32.dll. No big deal. DTS Package Compare has to use a whole slew of native functions for cases such as this, so a couple more is not, as it were, a biggy.&lt;BR&gt;&lt;BR&gt;So that's another completed tour through the mundane and slightly vexing. At some stage I may blog about something that was actually difficult to solve...but there seems to be so much more milage in life's little irritations.&lt;BR&gt;&lt;img src="http://www.simple-talk.com/community/aggbug.aspx?PostID=64" width="1" height="1"&gt;</content><author><name>Dan J Archer</name><uri>http://www.simple-talk.com/community/user/Profile.aspx?UserID=2114</uri></author></entry><entry><title>Cowboy filenames</title><link rel="alternate" type="text/html" href="http://www.simple-talk.com/community/blogs/dana/archive/2005/12/02/55.aspx" /><id>http://www.simple-talk.com/community/blogs/dana/archive/2005/12/02/55.aspx</id><published>2005-12-02T16:53:00Z</published><updated>2005-12-02T16:53:00Z</updated><content type="html">If you ask me, and I take it as implicit by your visit that you do
(sorry about that, but it's Friday afternoon and I need a weekend),
Windows is far too facist about filenames. Most notably in terms of the
characters one can put into a filename, and the obstreperous way it will cough and whine at you if you dare to break the rules.&lt;br&gt;
&lt;br&gt;
For the application I'm working on, we automatically generate filenames
based on certain criteria. These filenames can contain descriptive text
which we don't necessarily want to restrict to the set of
filename-legal characters (I'll return to this point momentarily). So
we have to filter the descriptive text and strip out illegal characters.&lt;br&gt;
&lt;br&gt;
So what is the set of legal characters? I consulted my trusty internet, and after the usual few minutes throwing seaweed over it
and sticking pins in effigies, it yielded the following rather useful
bit of wisdom, from the Windows Platform SDK, under &lt;i&gt;Win32
and COM Development, System Services, Files and I/O, SDK Documentation,
Storage, Storage Overview, File Management, Creating, Deleting and
Maintaining Files, Naming a File&lt;/i&gt;.&lt;br&gt;
&lt;br&gt;
&lt;div&gt;&lt;font color="#000070" face="Tahoma" size="2"&gt;
...&lt;br&gt;&lt;/font&gt;&lt;font color="#007000" face="Tahoma" size="2"&gt;
Use any character in the current code page for a name, except
characters in the range of 0 through 31, or any character that the file
system does not allow. A name can contain characters in the extended
character set (128–255). However, it cannot contain the following
reserved characters: &lt;br&gt;
&amp;lt; &amp;gt; : " / \ |&lt;br&gt;
&lt;br&gt;
The following reserved device names cannot be used as the name of a
file: CON, PRN, AUX, NUL, COM1, COM2, COM3, COM4, COM5, COM6, COM7,
COM8, COM9, LPT1, LPT2, LPT3, LPT4, LPT5, LPT6, LPT7, LPT8, and LPT9.
Also avoid these names followed by an extension, for example, NUL.tx7.&lt;/font&gt;&lt;br&gt;
      &lt;font color="#000070" face="Tahoma" size="2"&gt;
...&lt;br&gt;
      &lt;/font&gt;&lt;/div&gt;
&lt;br&gt;
Now at first, as I was in a hurry, I read this as "everything except &amp;lt; &amp;gt; : " / \ | is ok".&lt;br&gt;
&lt;br&gt;
But I forgot that characters below 32 are explicitly disallowed.&lt;br&gt;
&lt;br&gt;
And then I noticed that (quite naturally) applications don't let you
save filenames containing the "*" key, as this is used as a wildcard.&lt;br&gt;
&lt;br&gt;
And also filenames containing the "?" key are disallowed. This is also
a wildcard character, and used to escape long paths by prepending
"\\?\" (I always liked that little hack. Such style.)&lt;br&gt;
&lt;br&gt;
But with a firm conviction that MSDN Does Not Lie, I checked my ASCII
table to make sure I wasn't hallucinating. Sure enough, "*" and "?" are
ASCII 42 and 63 (please don't anyone tell me they knew that from
memory, or I may cry).&lt;br&gt;
&lt;br&gt;
But, and aha, there's another clause I hadn't read properly: "...or any
character that the file system does not allow". So, time to work out
what characters are considered illegal by FAT, FAT32 and NTFS,
methought.&lt;br&gt;
&lt;br&gt;
Around this point I came across the system structure BIGFATBOOTFSINFO,
which I only mention as it's an excellent name. But I digress.&lt;br&gt;
&lt;br&gt;
So, I wandered towards the Platform SDK again, by way of &lt;i&gt;Win32 and COM Development, Development
Guides, Windows 95/98/Me Programming, Long File Names, Long File Names
and the Protected-Mode FAT System&lt;/i&gt;. It was a long walk, but I sustained myself on the way with a coffee. Herein I found more wisdom.&lt;br&gt;
&lt;br&gt;
&lt;div&gt;&lt;font color="#007000" face="Tahoma" size="2"&gt;
...&lt;br&gt;When
an application creates a file or directory that has a long file name,
the system automatically generates a corresponding alias for that file
or directory using the standard 8.3 format. The characters used in the
alias are the same characters that are available for use in MS-DOS file
and directory names. Valid characters for the alias are any combination
of letters, digits, or characters with ASCII codes greater than 127,
the space character (ASCII 20h), as well as any of the following
special characters.&lt;br&gt;
      &lt;br&gt;
$ % ' - _ @ ~ ` ! ( ) { } ^ # &amp;amp;&lt;br&gt;
      &lt;br&gt;
The space character has been available to applications for file names
and directory names through the functions in current and earlier
versions of MS-DOS. However, many applications do not recognize the
space character as a valid character, and the system does not use the
space character when it generates an alias for a long file name. MS-DOS
does not distinguish between uppercase and lowercase letters in file
names and directory names, and this is also true for aliases.&lt;br&gt;
      &lt;br&gt;
The set of valid characters for long file names includes all the
characters that are valid for an alias as well as the following
additional characters.&lt;br&gt;
      &lt;br&gt;
+ , ; = [ ]&lt;br&gt;
...&lt;br&gt;
&lt;/font&gt;&lt;/div&gt;
&lt;br&gt;So, we're getting a little closer. At least FAT16 fesses up to not allowing "*" and "?".&lt;br&gt;
&lt;br&gt;
As to exactly which characters are disallowed by NTFS, this remains a
mystery to me. It doesn't fess up to disallowing "*" and "?", and
indeed googling suggests that all Unicode characters are allowed by
NTFS as filenames. So it is presumably the OS level Windows, quite
possibly not much deeper than the Win32 API, which disallows "*" and
"?" as well as the characters listed in the first quoted section above.
Could it disallow others? Since "*" and "?" are not documented
explicitly, one has to presume so. &lt;br&gt;
&lt;br&gt;
If it wasn't for the fact that our application had to support Unicode,
I would write a simple filter based on ASCII character codes, and be
rather facist about it. In our case we can simply replace offending
characters with underscores "_" and have done with it. Once, of course,
we've determined what those offending characters are.&lt;br&gt;
&lt;br&gt;
I'm sure I've encountered this issue before, and I seem to recall
wishing then just for a simple table, for both Unicode and ASCII
characters, showing which were disallowed and by what level of the
system (shell, file system API, file system itself). My wish is still
pending, but since I have some pressing work to do I should Alt-Tab and
get on with it...&lt;br&gt;&lt;img src="http://www.simple-talk.com/community/aggbug.aspx?PostID=55" width="1" height="1"&gt;</content><author><name>Dan J Archer</name><uri>http://www.simple-talk.com/community/user/Profile.aspx?UserID=2114</uri></author></entry></feed>
