(EDIT) The plot thickens: The same code (with no need for impersonation!) runs successfully from a Windows 7 client, but NOT from a Windows 2008 R2 client! Here's the code in question. (Original message follows code below.)
var entry = new DirectoryEntry("IIS://" + tbHost.Text + "/W3SVC", tbUsername.Text, tbPassword.Password);
foreach (DirectoryEntry site in entry.Children)
{
Console.Write("Site {0}\n", site.Name);
foreach (PropertyValueCollection prop in site.Properties)
Console.Write("{0}={1}\n", prop.PropertyName, prop.Value);
}
I read here that, for the IIS provider, you can't pass credentials when creating the DirectoryEntry
object. You have to do impersonation. So I tried the following code, but I still get a COMException with a text of "Unknown error (0x80005000)" when I try to read a property, just as I did when I previously tried to pass username and password for the DirectoryEntry
constructor. Here's the rundown:
LogonUser()
succeeds, credentials are OK. I had banged by head a little before I found out I had to use LOGON32_LOGON_NEW_CREDENTIALS
instead of LOGON32_LOGON_INTERACTIVE
.hosts
file so I could get to it by name..
const int LOGON32_LOGON_INTERACTIVE = 2;
const int LOGON32_LOGON_NETWORK = 3;
const int LOGON32_LOGON_NEW_CREDENTIALS = 9;
const int LOGON32_PROVIDER_DEFAULT = 0;
const int LOGON32_PROVIDER_WINNT50 = 3;
const int LOGON32_PROVIDER_WINNT40 = 2;
const int LOGON32_PROVIDER_WINNT35 = 1;
[DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
public static extern int LogonUser(String lpszUserName, string lpszDomain, string lpszPassword, int dwLogonType, int dwLogonProvider, ref IntPtr phToken);
[DllImport("kernel32.dll", CharSet = CharSet.Auto)]
public extern static bool CloseHandle(IntPtr handle);
[DllImport("kernel32.dll")]
extern static int GetLastError();
(...)
IntPtr myToken = IntPtr.Zero;
if (LogonUser(tbUsername.Text, tbHost.Text, tbPassword.Password, LOGON32_LOGON_NEW_CREDENTIALS, LOGON32_PROVIDER_DEFAULT, ref myToken) == 0)
{
int causingError = GetLastError();
throw new System.ComponentModel.Win32Exception(causingError);
}
WindowsImpersonationContext myMission = WindowsIdentity.Impersonate(myToken);
string mbUri = "IIS://" + tbHost.Text + "/MimeMap";
DirectoryEntry myDirEntry = new DirectoryEntry(mbUri);
Console.Write("{0}\n", myDirEntry.Properties["KeyType"]);
myDirEntry.Close();
myMission.Undo();
if (myToken != IntPtr.Zero)
CloseHandle(myToken);
Got it. All I had to do was go to Server Management → Roles → Web Server (IIS) and enable the service Management Tools → IIS 6 Management Compatibility → IIS 6 Metabase Compatibility. See pic below.
It's in the client machine, so you don't really have to have anything else from IIS installed.
I wonder how that will work for a non-server Windows (XP, Vista, 7).