Question

I have a C++ application that needs to retrieve an IIS 7 site's properties (such as metabase properties similar to those in IIS6 - Path, AppFriendlyName etc).

With IIS 7, my code does this:

  1. Get the AppHostWritableAdminManager and commit the path MACHINE/WEBROOT/APPHOST/Default Web Site/.
  2. Call GetAdminSection with the section name appSettings.
  3. Then look at the returned collection and look for the property (Path for example).

This works in IIS 6 but not on IIS7/7.5.

What changes do I need to make in order to make this work?

Was it helpful?

Solution

In IIS7 the configuration data is not stored in a "metabase" and also the metabase properties that we're accustomed to in IIS6 aren't the same. IIS7 stores the bulk of its configuration data in the following file:

%systemroot%\System32\InetSrv\Config\applicationHost.config

There are other files, but for the purposes of answering this question, this is the file we're interested in.

The documentation for applicationHost.config can be found here:

<system.applicationHost> - IIS.NET
configuration Element [IIS 7 Settings Schema]
system.applicationHost Section Group [IIS 7 Settings Schema]

You can find a list of IIS6 metabase -> IIS7 XML configuration mappings here:

Converting Metabase Properties to Configuration Settings [IIS 7]

For example in IIS6 the path to a site's /root is stored in the Path attribute of IIsWebVirtualDir. i.e.:

<IIsWebServer Location="/LM/W3SVC/67793744" AuthFlags="0" ServerAutoStart="TRUE" 
              ServerBindings="217.69.47.170:80:app2.dev" ServerComment="app2" /> 
<IIsWebVirtualDir Location="/LM/W3SVC/67793744/root" 
    AccessFlags="AccessRead | AccessScript" 
    AppFriendlyName="Default Application" 
    AppIsolated="2" 
    AppRoot="/LM/W3SVC/67793744/Root" 
    AuthFlags="AuthAnonymous | AuthNTLM" 
    DirBrowseFlags="DirBrowseShowDate | DirBrowseShowTime | DirBrowseShowSize |
            DirBrowseShowExtension | DirBrowseShowLongDate | EnableDefaultDoc" 
    Path="D:\websites\ssl-test\www\kerboom" 
    ScriptMaps="...">

But in IIS7 it's stored differently:

<sites>
    <site name="Default Web Site" id="1" serverAutoStart="true">
        <!-- this is the functional equivalent of the /root app in IIS6 -->
        <application path="/">
            <virtualDirectory path="/" 
                              physicalPath="%SystemDrive%\inetpub\wwwroot" />
        </application>
    </site>
<sites>

However, if your code must work with both IIS6 and IIS7 then you can install the IIS6 Management Compatibility components. This will allow you to access IIS7 site properties using traditional IIS6 metabase API's such as ADSI, System.DirectoryServices etc. The compatibility layer will map these properties to the new IIS7 schema for you.

The first part of this article explains how to install this on Vista/Windows7/Windows 2008:

How to install ASP.NET 1.1 with IIS7 on Vista and Windows 2008 - see step #1

Update:

Unfortunately C++ isn't my strong point. However I put together an example in C# using COM Interop to demonstrate using the AppHostWritableAdminManager:

IAppHostWritableAdminManager wam = new AppHostWritableAdminManager();
IAppHostElement sites = 
   wam.GetAdminSection("system.applicationHost/sites", "MACHINE/WEBROOT/APPHOST");
IAppHostElementCollection sitesCollection = sites.Collection;

long index = FindSiteIndex(sitesCollection, "MySite");
if(index == -1) throw new Exception("Site not found");

IAppHostElement site = sitesCollection[index];
IAppHostElementCollection bindings = site.ChildElements["bindings"].Collection;

for (int i = 0; i < bindings.Count; i++)
{
  IAppHostElement binding = bindings[i];
  IAppHostProperty protocolProp = binding.GetPropertyByName("protocol");
  IAppHostProperty bindingInformationProp = 
      binding.GetPropertyByName("bindingInformation");

  string protocol = protocolProp.Value;
  string bindingInformation = bindingInformationProp.Value;

  Debug.WriteLine("{0} - {1}", protocol, bindingInformation);

}

static long FindSiteIndex(IAppHostElementCollection sites, string siteName)
{
  for (int i = 0; i < sites.Count; i++)
  {
    IAppHostElement site = sites[i];
    Debug.WriteLine(site.Name);
    IAppHostProperty prop = site.GetPropertyByName("name");
    if(prop.Value == siteName)
    {
      return i;
    }
  }
  return -1;
}

The code above locates a site named "MySite" in the <sites> collection. It then retrieves the site's <bindings> collection and print's each bindings protocol and bindingInformation attributes.

Your should be able to convert this to C++ fairly easily.

To answer the question in your comment -

For example, the path system.applicationHost/Sites will get me to the list of sites. Is there an absolute way to get to my server bindings like this (for example by doing system.applicationHost/Sites/Default Web Site/Bindings

When using the AppHostWritableAdminManager there isn't a shortcut to getting directly to the site you want to inspect/modify or it's properties. In the example above, you'll see that I need to loop through the sites collection to find the site I'm interested in. The reason for this is that AppHostWritableAdminManager sees everything as elements and collections of elements. It's a fairly basic API. Even when using the managed Microsoft.Web.Administration API you find that whilst there are some nice properties such as Site.Bindings, these are thinly disguised wrappers around AppHostWritableAdminManager.

In fact if I want to find a site I still have to search the Sites collection either in a for loop or by adding some LINQ sugar if I'm using C#3.5 or later:

using(ServerManager serverManager = new ServerManager())
{
    Site x = serverManager.Sites.FirstOrDefault(s => s.Name == "MySite");
}

Site's base class is ConfigurationElement which under the bonnet wraps access to IAppHostElement.

Once you're past some basic shortcut wrapper properties much of what we do in managed code to configure IIS (for example IIS FTP) is elements, attributes and collections of elements.

Update 2:

Please bear in mind I've never written a line of C++ in my life. There's no cleanup of strings or objects:

#include "stdafx.h"
#include <windows.h>
#include <stdio.h>
#include <ahadmin.h>
#include <crtdbg.h>

static IAppHostElement* 
   FindSite(IAppHostElementCollection *pCollection, BSTR bstrSiteName);

int _tmain(int argc, _TCHAR* argv[])
{
  CoInitialize(NULL);

  IAppHostWritableAdminManager *pMgr = NULL;
  IAppHostElement *pElem = NULL;
  IAppHostElementCollection *pSitesCollection = NULL;
  IAppHostElement *pSite = NULL;
  IAppHostElement *pBindings = NULL;
  IAppHostElement *pBinding = NULL;
  IAppHostElementCollection *pBindingsCollection = NULL;
  IAppHostChildElementCollection *pChildElements = NULL;
  IAppHostProperty *pProtocol = NULL;
  IAppHostProperty *pBindingInformation = NULL;

  BSTR bstrSectionName = SysAllocString( L"system.applicationHost/sites" );
  BSTR bstrConfigCommitPath = SysAllocString( L"MACHINE/WEBROOT/APPHOST" );
  BSTR bstrSiteName = SysAllocString( L"MySite" );
  BSTR bstrBindingsConst = SysAllocString( L"bindings" );
  BSTR bstrBindingProtocol = SysAllocString( L"protocol" );
  BSTR bstrBindingInformation = SysAllocString( L"bindingInformation" );

  VARIANT vtPropertyName;
  VARIANT vtIndex;

  HRESULT hr = S_OK;

  hr = CoCreateInstance( __uuidof(AppHostWritableAdminManager), NULL, 
      CLSCTX_INPROC_SERVER, __uuidof(IAppHostWritableAdminManager), (void**) &pMgr);

  hr = pMgr->GetAdminSection(bstrSectionName, bstrConfigCommitPath, &pElem);
  hr = pElem->get_Collection(&pSitesCollection);

  pSite = FindSite(pSitesCollection, bstrSiteName);

  hr = pSite->get_ChildElements(&pChildElements);

  vtPropertyName.vt = VT_BSTR;
  vtPropertyName.bstrVal = bstrBindingsConst;

  hr = pChildElements->get_Item(vtPropertyName, &pBindings);
  hr = pBindings->get_Collection(&pBindingsCollection);

  DWORD bindingsCount;
  hr = pBindingsCollection->get_Count(&bindingsCount);

  for(int i = 0; i < bindingsCount; i++)
  {
    vtIndex.lVal = i;
    vtIndex.vt = VT_I4;
    hr = pBindingsCollection->get_Item(vtIndex, &pBinding);

    hr = pBinding->GetPropertyByName(bstrBindingProtocol, &pProtocol);
    hr = pBinding->GetPropertyByName(bstrBindingInformation, &pBindingInformation);

    BSTR bstrProtocol;
    BSTR bstrBindingInformation;

    hr = pProtocol->get_StringValue(&bstrProtocol);
    hr = pBindingInformation->get_StringValue(&bstrBindingInformation);

    _tprintf(_T("Protocol: %s, BindingInfo: %s\n"), bstrProtocol, bstrBindingInformation);
  }

  CoUninitialize();
  return 0;
}

IAppHostElement* FindSite(IAppHostElementCollection *pCollection, BSTR bstrSiteName)
{
  DWORD count = -1;
  pCollection->get_Count(&count);

  BSTR bstrPropName = SysAllocString( L"name");

  for(DWORD i = 0; i < count; i++)
  {
    IAppHostElement *site = NULL;
    IAppHostProperty *prop = NULL;
    BSTR bstrPropValue;

    HRESULT hr = S_OK;

    VARIANT vtCount;
    VariantInit(&vtCount);
    vtCount.lVal = i;
    vtCount.vt = VT_I4;

    hr = pCollection->get_Item(vtCount, &site);
    hr = site->GetPropertyByName(bstrPropName, &prop);
    hr = prop->get_StringValue(&bstrPropValue);

    if(wcscmp(bstrPropValue, bstrSiteName) == 0)
    {
      return site;
    }
  }

  return NULL;
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top