To get the hosted web sites names from IIS - c#

I have created a method to get hosted web sites from IIS server as following code snippet.
ServerManager serverManager = new ServerManager();
try
{
foreach (Site site in serverManager.Sites)
{
Console.WriteLine(site);
}
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
Console.ReadLine();
}
When i run it in my local machine it's working perfectly(Windows 7 /IIS 7 with 32bits).but when i run it in server machine(Windows server 2003 R2 with IIS 6) it's not working.It gives following Error
Retrieving the COM class factory for component with CLSID {2B>
52-803546CE3344} failed due to the following error: 80040154>
d (Exception from HRESULT: 0x80040154 (REGDB_E_CLASSNOTREG)).
Any help will be great full ?

I have used System.DirectoryServices instead of using Microsoft.Web.Administration and it solved my problem.It will work with the IIS6 and IIS7 as well.
public class IisManager
{
public static int IisVersion
{
get
{
int iisVersion;
using (RegistryKey iisKey = Registry.LocalMachine.OpenSubKey(#"Software\Microsoft\InetStp"))
{
if (iisKey == null)
throw new Exception("IIS is not installed.");
iisVersion = (int)iisKey.GetValue("MajorVersion");
}
return iisVersion;
}
}
public static IList<IisWebSite> GetIisSites()
{
List<IisWebSite> sites = new List<IisWebSite>();
using (DirectoryEntry iisRoot = new DirectoryEntry("IIS://localhost/W3SVC"))
{
iisRoot.RefreshCache();
sites.AddRange(iisRoot.Children.Cast<DirectoryEntry>().Where(w => w.SchemaClassName.ToLower(CultureInfo.InvariantCulture) == "iiswebserver").Select(w => new IisWebSite(w.Name, w.Properties["ServerComment"].Value.ToString())));
}
return sites;
}
public static IList<string> GetIisAppPools()
{
List<string> pools = new List<string>();
using (DirectoryEntry poolRoot = new DirectoryEntry("IIS://localhost/W3SVC/AppPools"))
{
poolRoot.RefreshCache(); pools.AddRange(poolRoot.Children.Cast<DirectoryEntry>().Select(p => p.Name));
}
return pools;
}
}

Check out this blog post, most specifically, the last paragraph. It's very possible that it's a 32bit vs 64bit DLL compilation conflict
Both the customer and I were creating 32-bit .NET applications, and
the COM interface for the FTP runtime state is implemented in a
64-bit-only DLL. Once we both changed our projects to compile for
64-bit platforms, we were both able to get the code to run.
(Coincidentally, all I had was a 32-bit system when I wrote my
original blog, so I probably would have run into this sooner if I had
owned a 64-bit system way back then. ;-])
http://blogs.iis.net/robert_mcmurray/archive/2012/06/29/error-class-not-registered-0x80040154-when-querying-ftp-runtime-state.aspx

Related

Problem with building C# code in Windows Service App

I have some server app. This app run, read some files, create C# classes, build this and load assembly. This app can work in two modes - one mode is window desktop application, and other mode - as windows service but core in dll is common.
Sometimes when this app work long time as service, and machine server has long timeup, they can't build anything. I attach to debugger, and debug. I debug .NET source (CompileAssemblyFromSource), and I see, that .NET classes just run csc.exe process with some params (CSharpCodeProvider class), but csc.exe run, return no errors or exceptions, output is blank and nothing is happend. No assembly is build.
I wrote some dump test service to compile code:
namespace CompilerService
{
public class Compiler
{
private Task _compilerTask;
public Compiler()
{
_compilerTask = Task.Run(() => CompileHalloWorld());
}
private const string _workingDir = #"C:\tmp";
private void CompileHalloWorld()
{
System.Threading.Thread.Sleep((30000));
if (!Directory.Exists(_workingDir))
{
Directory.CreateDirectory(_workingDir);
}
Directory.SetCurrentDirectory(_workingDir);
var csc = new CSharpCodeProvider(new Dictionary<string, string>() { { "CompilerVersion", "v4.0" } });
var parameters = new CompilerParameters(new[] { "mscorlib.dll", "System.Core.dll" }, "foo.exe", true);
parameters.GenerateExecutable = true;
CompilerResults results = null;
try
{
results = csc.CompileAssemblyFromSource(parameters,
#"using System;
class Program {
public static void Main(string[] args) {
Console.WriteLine(""Hallo World!"");
}
}");
}
catch (Exception e)
{
int a = 2;
}
results.Errors.Cast<CompilerError>().ToList().ForEach(error => Console.WriteLine(error.ErrorText));
}
}
}
This dump service is fail too with build hallo world in this state of machine.
After restart machine, all work again ok, compile and load assembly all the time. After few weeks, problem come back, and we must reset server. This problem is on only one machine. On otger machines this service and csc.exe work perfect from years.
If machine is in this wird state, csc.exe dont build in windows service app, but when We run this app as Windows Desktop App all work fine, and csc.exe build normal...
Can you tell me, this is some known issue, oraz is some solution of don't compile csc.exe without machine restart?

How to list / check existance of Managed .NET DLL's in dllhost.exe

I have some components that I made available for COM+.
In this case it will be loaded in dllhost.exe (COM Surrogate) when it is used.
For maintenance reasons I want to create a .EXE file that stops all instances of the dllhost.exe to stop usage of the components.
So I made this:
foreach (var process in Process.GetProcesses().Where(pr => pr.ProcessName.ToLower() == "dllhost"))
{
var modules = process.Modules;
foreach (ProcessModule module in modules)
{
//Console.WriteLine(module.ModuleName);
if (!module.ModuleName.ToLower().Contains("tqsoft")) continue;
process.Kill();
}
}
Unfortunately process.Modules do only list unmanaged code of .dll and .exe files.
I did not find any solution so far in MSDN, SO, etc.
Hans Passant mentioned here MDbg - Debugger's Protocol Is Incompatible With The Debuggee about a solution.
I checked out the Version 4 sample and referenced mdbgeng.dll and corapi.dll in my project.
The following code should give me the assemblies of the process, but it fails with a exception.
MDbgEngine mDbgEngine = new MDbgEngine();
var dbgProcess = mDbgEngine.Attach(process.Id);
foreach (CorAppDomain appDomain in dbgProcess.AppDomains)
{
foreach (CorAssembly assembly in appDomain.Assemblies)
{
Console.WriteLine(assembly.Name);
//get assembly information
}
}
Exception:
System.Runtime.InteropServices.COMException (0x8007012B): Only part of a ReadProcessMemory or WriteProcessMemory request
was completed. (Exception from HRESULT: 0x8007012B)
at Microsoft.Samples.Debugging.CorDebug.ICLRMetaHost.EnumerateLoadedRuntimes(ProcessSafeHandle hndProcess)
at Microsoft.Samples.Debugging.CorDebug.CLRMetaHost.EnumerateLoadedRuntimes(Int32 processId)
at Microsoft.Samples.Debugging.MdbgEngine.MdbgVersionPolicy.GetDefaultAttachVersion(Int32 processId)
at Microsoft.Samples.Debugging.MdbgEngine.MDbgEngine.Attach(Int32 processId)
at TQsoft.Windows.Products.Sake.Kernel.StopInformer(Boolean fullstop)
at TQsoft.Windows.Products.Sake.Program.Main(String[] args)
Don't blame on me, I am only human after all :) But what is wrong here or what I miss?
UPDATE
My mistake. The exception comes from try to access 64bit dllhost.exe from a 32bit process. I fixed that I only access dllhost.exe with 32bit processes.
But still I do not get a list of assemblies of the attached process.
Solved: I dig into the MDbgEngine mentioned by Hans Passant and found out what I did wrong.
For those who run into same problems, here is my code.
// dllhost.exe com+ instances
foreach (var process in Process.GetProcesses().Where(pr => pr.ProcessName.ToLower() == "dllhost"))
{
// better check if 32 bit or 64 bit, in my test I just catch the exception
try
{
MDbgEngine mDbgEngine = new MDbgEngine();
var dbgProcess = mDbgEngine.Attach(process.Id);
dbgProcess.Go().WaitOne();
foreach (MDbgAppDomain appDomain in dbgProcess.AppDomains)
{
var corAppDomain = appDomain.CorAppDomain;
foreach (CorAssembly assembly in corAppDomain.Assemblies)
{
if (assembly.Name.ToLower().Contains("tqsoft"))
{
dbgProcess.Detach();
process.Kill();
}
}
}
}
catch
{
Console.WriteLine("64bit calls not supported from 32bit application.");
}
}

WMI: Register Assembly with WMI Repository via InstrumentationManager.RegisterAssembly

I am trying to write a WMI provider in C# (version 3.5). It'll be a dll that I can install into the GAC and it communicates with a running service via a pipe. Due to a custom updater mechanism on the service, I would like to programatically register and unregister the provider with the WMI repository instead of using installutil.exe.
To isolate my issues and solve them one at a time, I've started with the sample provider from the following link: https://msdn.microsoft.com/en-us/library/cc268228.aspx. I can build it as a DLL, insert it into the GAC (gacutil.exe), and Register it with the WMI repository via InstallUtil.exe. Queries to it via powershell work correctly and I can see its classes under its namespace when I use wbemtest.exe.
However, I've been unsuccessful in my attempts to register the assembly with the WMI repository when I use a separate app I wrote for using InstrumentationManager.RegisterAssembly.
Below is my attempt to programatically install the DLL based on the above sample. I'd appreciate any guidance or directions to samples that show how to implement a provider dll in this manner.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;
using System.IO;
using System.Management.Instrumentation;
using System.Configuration.Install;
//using System.EnterpriseServices.Internal;
namespace wmi_register_tool
{
class Program
{
static void Main(string[] args)
{
bool installing = true;
string asmPath = "";
if (!ParseInput(args, ref installing, ref asmPath))
{
PrintUsage();
return;
}
Assembly myAssem = Assembly.Load(File.ReadAllBytes(asmPath));
Console.WriteLine(myAssem.FullName);
Console.WriteLine("Types contained in " + asmPath + " assembly");
foreach (Type oType in myAssem.GetTypes())
{
Console.WriteLine("\t" + oType.Name);
}
//Publish p = new Publish();
//p.RegisterAssembly(myAssem);
AssemblyInstaller wmi_installer = new AssemblyInstaller(myAssem, null);
Console.WriteLine("--------------> Starting AssemblyInstaller.Install()");
wmi_installer.Install(null);
Console.WriteLine("--------------> Done with AssemblyInstaller.Install()");
Console.WriteLine("--------------> Starting AssemblyInstaller.Commit()");
wmi_installer.Commit(null);
Console.WriteLine("--------------> Done with AssemblyInstaller.Commit()");
try
{
Console.WriteLine("--------------> Starting Instrumentation.RegisterAssembly()");
//Instrumentation.RegisterAssembly(myAssem);
InstrumentationManager.RegisterAssembly(myAssem);
Console.WriteLine("--------------> Done with Instrumentation.RegisterAssembly()");
}
catch (Exception e)
{
Console.WriteLine("Exception Caught: {0}", e);
Console.WriteLine("Exception Message: " + e.Message);
}
}
static bool ParseInput(string[] args, ref bool installing, ref string asmPath)
{
if (args.Length < 2 || args[0].Equals("/h") || args[0].Equals("-h"))
{
return false;
}
asmPath = args[1];
if (args[0].Equals("-i"))
{
installing = true;
return true;
}
else if(args.Equals("-u"))
{
installing = false;
return true;
}
return false;
}
static void PrintUsage()
{
System.Console.WriteLine("wmi_register_tool.exe \n\t-i <filename>\n\t-u <filename>");
}
}
}
I get the following output where it appears to succeed, but then I don't find anything in the WMI repository, so RegisterAssembly must not have successfully inserted it.
C:\wmi_testing>"C:\Program Files (x86)\Microsoft SDKs\Windows\v7.0A\Bin\gacutil.exe" /i WmiServic
eHost.dll
Microsoft (R) .NET Global Assembly Cache Utility. Version 3.5.30729.1
Copyright (c) Microsoft Corporation. All rights reserved.
Assembly successfully added to the cache
C:\wmi_testing>wmi_register_tool.exe -i WmiServiceHost.dll
WmiServiceHost, Version=1.0.0.3, Culture=neutral, PublicKeyToken=3cd934646523c30c
Types contained in WmiServiceHost.dll assembly
SERVICE_STATUS_PROCESS
SCM_ACCESS
SERVICE_ACCESS
SC_STATUS_TYPE
ServiceHandle
SCM
MyInstall
Win32_Process
WIN32ServiceHost
<EnumerateServiceHosts>d__0
--------------> Starting AssemblyInstaller.Install()
Installing assembly 'C:\Windows\assembly\GAC_MSIL\WmiServiceHost\1.0.0.3__3cd934646523c30c\WmiServiceHost.dll'.
Affected parameters are:
assemblypath = C:\Windows\assembly\GAC_MSIL\WmiServiceHost\1.0.0.3__3cd934646523c30c\WmiServiceHost.dll
logfile = C:\Windows\assembly\GAC_MSIL\WmiServiceHost\1.0.0.3__3cd934646523c30c\WmiServiceHost.InstallLog
Installing WMI Schema: Started
Registering assembly: WmiServiceHost_SN_3cd934646523c30c_Version_1.0.0.3
Ensuring that namespace exists: root\MicrosoftWmiNet
Ensuring that class exists: root\MicrosoftWmiNet:WMINET_Instrumentation
Ensuring that class exists: CREATING root\MicrosoftWmiNet:WMINET_Instrumentation
Ensuring that class exists: root\MicrosoftWmiNet:WMINET_InstrumentedNamespaces
Ensuring that class exists: CREATING root\MicrosoftWmiNet:WMINET_InstrumentedNamespaces
Ensuring that class exists: root\MicrosoftWmiNet:WMINET_Naming
Ensuring that class exists: CREATING root\MicrosoftWmiNet:WMINET_Naming
Ensuring that namespace exists: root\default
Ensuring that class exists: root\default:WMINET_Instrumentation
Ensuring that class exists: CREATING root\default:WMINET_Instrumentation
Ensuring that class exists: root\default:WMINET_InstrumentedAssembly
Ensuring that class exists: CREATING root\default:WMINET_InstrumentedAssembly
Ensuring that class exists: root\default:MSFT_DecoupledProvider
Ensuring that class exists: CREATING root\default:MSFT_DecoupledProvider
Ensuring that class exists: root\default:WMINET_ManagedAssemblyProvider
Ensuring that class exists: CREATING root\default:WMINET_ManagedAssemblyProvider
Installing WMI Schema: Finished
--------------> Done with AssemblyInstaller.Install()
--------------> Starting AssemblyInstaller.Commit()
See the contents of the log file for the C:\Windows\assembly\GAC_MSIL\WmiServiceHost\1.0.0.3__3cd934646523c30c\WmiServic
eHost.dll assembly's progress.
The file is located at C:\Windows\assembly\GAC_MSIL\WmiServiceHost\1.0.0.3__3cd934646523c30c\WmiServiceHost.InstallLog.
Committing assembly 'C:\Windows\assembly\GAC_MSIL\WmiServiceHost\1.0.0.3__3cd934646523c30c\WmiServiceHost.dll'.
Affected parameters are:
assemblypath = C:\Windows\assembly\GAC_MSIL\WmiServiceHost\1.0.0.3__3cd934646523c30c\WmiServiceHost.dll
logfile = C:\Windows\assembly\GAC_MSIL\WmiServiceHost\1.0.0.3__3cd934646523c30c\WmiServiceHost.InstallLog
logtoconsole =
--------------> Done with AssemblyInstaller.Commit()
--------------> Starting Instrumentation.RegisterAssembly()
--------------> Done with Instrumentation.RegisterAssembly()
C:\wmi_testing>
EDIT: I tried replacing Instrumentation.RegisterAssembly with the following code and again it ran to completion with no exception, but it printed out more more information to the console indicating that things were going smoothly. In the end the results were the same and I couldn't find my namespace in the WMI repository using wbemtest.exe.
string[] installArgs = new String[] {
"//logfile=",
"//LogToConsole=false",
"//ShowCallStack",
myAssem.Location,
};
System.Configuration.Install.
ManagedInstallerClass.InstallHelper(installArgs);
In my attempt to get the RegisterAssembly function to do the work, I missed the fact that I had achieved my goal while searching for potential dependencies. The AssemblyInstaller.Install and AssemblyInstaller.Commit functions were successfully inserting my provider into the WMI repository. I'm not sure whether RegisterAssembly was removing it and/or messing up the entry or if I had just messed up my WMI repository through continual testing, but when I reset my test VM to a clean state and ran the code with some new lines that cause an exception after AssemblyInstaller.Commit and before RegisterAssembly, I noticed that my provider was correctly inserted into the WMI repository and that I could query it through powershell.
Below is the main that successfully installed it into the WMI repository after I had manually placed the DLL into the GAC.
static void Main(string[] args)
{
bool installing = true;
string asmPath = "";
if (!ParseInput(args, ref installing, ref asmPath))
{
PrintUsage();
return;
}
Assembly myAssem = Assembly.Load(File.ReadAllBytes(asmPath));
Console.WriteLine(myAssem.FullName);
AssemblyInstaller wmi_installer = new AssemblyInstaller(myAssem, null);
wmi_installer.Install(null);
wmi_installer.Commit(null);
}
And just for completeness, I'll include another version that uninstalls and checks if the assembly can be installed prior to trying.
static void Main(string[] args)
{
bool installing = true;
bool installable = false;
string asmPath = "";
if (!ParseInput(args, ref installing, ref asmPath))
{
PrintUsage();
return;
}
IDictionary mySavedState = new Hashtable();
Assembly myAssem = Assembly.Load(File.ReadAllBytes(asmPath));
Console.WriteLine(myAssem.FullName);
AssemblyInstaller wmi_installer = new AssemblyInstaller(myAssem, null);
if (installing)
{
try
{
AssemblyInstaller.CheckIfInstallable(asmPath);
installable = true;
}
catch (Exception)
{
installable = false;
}
if (installable)
{
mySavedState.Clear();
try
{
wmi_installer.Install(mySavedState);
wmi_installer.Commit(mySavedState);
}
catch (Exception)
{
wmi_installer.Rollback(mySavedState);
}
}
}
else
{
try
{
wmi_installer.Uninstall(null);
}
catch (Exception e)
{
Console.WriteLine("Uninstall failed due to: " + e.Message);
}
}
}

How to connect to windows phone 7 from a 64-bit application

I have a 32-bit program (written in C++) that can connect to some different devices and as long as it is 32-bit everything works fine. However, now I need to build it as a 64-bit program but then I came across some problems with Windows Phone 7.
I found out that a dll (written in C#) that I rebuilt as 64-bit throws exception at this line:
MultiTargetingConnectivity connectivity = new MultiTargetingConnectivity(CultureInfo.CurrentUICulture.LCID);
The exception is:
An unhandled exception of type 'Microsoft.SmartDevice.Connectivity.DatastoreException' occurred in Microsoft.SmartDevice.Connectivity.dll
Additional information: Retrieving the COM class factory for component with CLSID {349AB2E8-71B6-4069-AD9C-1170849DA64C} failed due to the following error: 80040154 Class not registered (Exception from HRESULT: 0x80040154 (REGDB_E_CLASSNOTREG)).
(For example, if I try to run this example program it works in 32-bit but throws that exception in 64-bit at the same line)
When I searched for that CLSID in the registry I found a path to to "C:\Program Files (x86)\Common Files\Microsoft Shared\Phone Tools\CoreCon\11.0\Bin\ConMan2.dll" so I registered that dll using regsvr32 but I still get the same exception.
UPDATE:
Since I might need to create a workaround instead of finding a 64bit version of ConMan2.dll, I post a bit of my current dll here if anybody can show me a possible workaround so that it will work in both 32 and 64 bit.
namespace WP7DLL
{
// Interface declaration.
[Guid("11111111-1111-1111-1111-111111111111")]
public interface IWP7DLL
{
int GetStatus();
};
[ClassInterface(ClassInterfaceType.None)]
[Guid("22222222-2222-2222-2222-222222222222")]
public class WP7DLL : IWP7DLL
{
public WP7DLL() { }
public int GetStatus()
{
//Line that gives an exception in 64 bit
MultiTargetingConnectivity connectivity = new MultiTargetingConnectivity(CultureInfo.CurrentUICulture.LCID);
...
...
}
}
}
The COM server with CLSID = {349AB2E8-71B6-4069-AD9C-1170849DA64C} is implemented in C:\Program Files (x86)\Common Files\Microsoft Shared\Phone Tools\CoreCon\11.0\Bin\ConMan2.dll
There’s no 64-bit version of that DLL.
And you can’t use a 32-bit DLLs directly from a 64 bit process.
There’s a workaround. You can create another project, 32-bit EXE, that will call that 32-bit DLL however you want, and implement any IPC to interact with your main 64-bit application.
For the specific IPC mechanism, if you only need to invoke a single relatively long task and wait for it to complete, command-like app + command-line arguments + exit code may me enough for you.
If you need to issue many calls, I’d choose WCF over named pipe transport. If you’ll choose this way, below is some sample code implementing that .EXE.
/// <summary>The class from the shared assembly that defines WCF endpoint, and named events</summary>
public static class InteropShared
{
// Host signals it's ready and listening. Replace the zero GUID with a new one
public static readonly EventWaitHandle eventHostReady = new EventWaitHandle( false, EventResetMode.AutoReset, #"{00000000-0000-0000-0000-000000000000}" );
// Client asks the host to quit. Replace the zero GUID with a new one
public static readonly EventWaitHandle eventHostShouldStop = new EventWaitHandle( false, EventResetMode.AutoReset, #"{00000000-0000-0000-0000-000000000000}" );
const string pipeBaseAddress = #"net.pipe://localhost";
/// <summary>Pipe name</summary>
// Replace the zero GUID with a new one.
public const string pipeName = #"00000000-0000-0000-0000-000000000000";
/// <summary>Base addresses for the hosted service.</summary>
public static Uri baseAddress { get { return new Uri( pipeBaseAddress ); } }
/// <summary>Complete address of the named pipe endpoint.</summary>
public static Uri endpointAddress { get { return new Uri( pipeBaseAddress + '/' + pipeName ); } }
}
static class Program
{
/// <summary>The main entry point for the application.</summary>
[STAThread]
static void Main()
{
// The class implementing iYourService interface that calls that 32-bit DLL
YourService singletoneInstance = new YourService();
using( ServiceHost host = new ServiceHost( singletoneInstance, InteropShared.baseAddress ) )
{
// iYourService = [ServiceContract]-marked interface from the shared assembly
host.AddServiceEndpoint( typeof( iYourService ), new NetNamedPipeBinding(), InteropShared.pipeName );
host.Open();
InteropShared.eventHostReady.Set();
// Wait for quit request
InteropShared.eventHostShouldStop.WaitOne();
host.Close();
}
}
}

IIS: wwwroot directories of an IIS with C#?

How can I get all (physical) wwwroot directories (home directories) of an IIS with C# from the IIS metabase?
If you're using IIS6:
You need to add a reference to System.DirectoryServices in Visual Studio in the Add References .NET assemblies tab:
using System;
using System.DirectoryServices;
namespace ListRootAppPathsIIS6
{
class Program
{
static void Main(string[] args)
{
using (DirectoryEntry de = new DirectoryEntry("IIS://Localhost/W3SVC"))
{
foreach (DirectoryEntry w3svc in de.Children)
{
if (w3svc.SchemaClassName == "IIsWebServer")
{
string rootPath =
String.Format("IIS://Localhost/W3SVC/{0}/root", w3svc.Name);
using (DirectoryEntry root = new DirectoryEntry(rootPath))
{
string info = String.Format("{0} - {1} - {2}",
w3svc.Name,
w3svc.Properties["ServerComment"].Value,
root.Properties["Path"].Value);
Console.WriteLine(info);
}
}
}
}
Console.ReadLine();
}
}
}
If you're using IIS7:
Add a reference to C:\Windows\System32\inetsrv\Microsoft.Web.Administration.dll
using System;
using System.Linq;
using Microsoft.Web.Administration;
namespace ListRootAppPathsIIS7
{
class Program
{
static void Main(string[] args)
{
using(ServerManager serverManager = new ServerManager())
{
foreach (var site in serverManager.Sites)
{
var app = site.Applications.Where(a => a.Path == "/").First();
var vdir = app.VirtualDirectories.Where(v => v.Path == "/").First();
string info = String.Format("{0} - {1} - {2}",
site.Id,
site.Name,
Environment.ExpandEnvironmentVariables(vdir.PhysicalPath));
Console.WriteLine(info);
}
}
Console.ReadLine();
}
}
}
The first method (using System.Directoryservices) will work with IIS7 provided you've installed the IIS6 management compatibilities bits.
In both cases you need to be running as an Administrator.
You could call out to
C:\Windows\system32\inetsrv\appcmd.exe list vdir
(using the API to get the right folder of course). And filter for "site\" (for the default site "Default Web Site/". I assume there is some API to do this (but, certainly for older IIS versions, this was very script orientated, so would require dealing with IDispatch to invoke methods and property-gets).
This does require an elevated process on IIS7 and 7.5 (unless sufficient rights to IIS have been delegated).

Categories

Resources