Warning: ob_start() [ref.outcontrol]: output handler 'ob_gzhandler' cannot be used after 'URL-Rewriter' in /home/.elfa/mogens/xt1.org/wp-includes/functions.php on line 821
xt1.org » ADSI and WMI -- Christian Mogensen writes software and dreams of droids

ADSI and WMI

March 13th, 2006

I’m improving the installer for a web application. It’s a windows installer. It needs to register a MIME type with the server. This is important to do, since Windows 2003 server will not serve unknown Mime types. (Neither will Windows 2000 after the Lockdown tool has been used).

WMI is the preferred mechanism for diddling IIS settings, but it turns out you can’t rely on WMI being installed. So we need to user ADSI, the underlying technology.

ADSI is a maze of little automation objects, all poorly documented. There’s plenty of documentation. It’s just that most of it is pointless drivel. Undocumentation as some have called it.

The ADSI incantations necessary have been recorded by wizened elders.

Unfortunately these incantations need to be converted to C++ in order to work inside an installer. There is a heavy bias against using script in installers. Partly because script may not be installed, or if it is installed, it’s a lower version than we require. So a hardened C++ version is needed.

In case anyone needs to know, here is how to tweak ADSI to add a mime-type to IIS from C++. It’s not pretty - being a frankenstein monster of cut-and-paste snippets from various places.

char buf[BUFLEN];
strcpy(buf, "");

_bstr_t rdfType = ".rdf";

CoInitialize(NULL);
_bstr_t bsMimePath = "IIS://localhost/MimeMap";

IADs* pContainer = NULL;
HRESULT hr = ADsGetObject( bsMimePath, IID_IADs, (void**) &pContainer);
if( SUCCEEDED(hr) && pContainer != NULL )
{
_variant_t varMimeMap;
_bstr_t nameMimeMap = "MimeMap";
hr = pContainer->GetEx( nameMimeMap, &varMimeMap);
if( SUCCEEDED(hr) && ((varMimeMap.vt & VT_ARRAY) == VT_ARRAY) )
{
bool bFoundRdfType = false;

// MIME map is an array that we can redim and add an item to the end
SAFEARRAY* pArrMimeMap = varMimeMap.parray;
LONG lIdx, lHigh;
::SafeArrayGetLBound(pArrMimeMap, 1, &lIdx);
::SafeArrayGetUBound(pArrMimeMap, 1, &lHigh);
for(LONG i = lIdx; i < = lHigh; i++)
{
_variant_t elem;
hr = ::SafeArrayGetElement(pArrMimeMap, &i, (void*) &elem);
if( SUCCEEDED(hr) && ((elem.vt & VT_DISPATCH) == VT_DISPATCH) )
{

IDispatchPtr pMimeType = elem.pdispVal;
if(pMimeType != NULL)
{
DISPID idExtension = 0;
_bstr_t propName = "Extension";
BSTR pName = (BSTR)propName;
BSTR* ppName = & pName;
hr = pMimeType->GetIDsOfNames( IID_NULL, ppName, 1, LOCALE_USER_DEFAULT, &idExtension );
if( SUCCEEDED(hr) )
{
DISPPARAMS dispparamsNoArgs;
memset( &dispparamsNoArgs, 0, sizeof(dispparamsNoArgs));
_variant_t varResult;
hr = pMimeType->Invoke( idExtension, IID_NULL, LOCALE_USER_DEFAULT, DISPATCH_PROPERTYGET, &dispparamsNoArgs, &varResult, NULL, NULL);
if( SUCCEEDED(hr) )
{
_bstr_t tmp = varResult.bstrVal;
if( tmp == rdfType )
bFoundRdfType = true;
}
}

}
}

}
// add one element
if( ! bFoundRdfType )
{
_bstr_t fileType = “.rdf”;
_bstr_t mimeType = “application/octet-stream”;
_variant_t varFileType = fileType;
_variant_t varMimeType = mimeType;

CLSID CLSID_MimeType;
hr = CLSIDFromProgID( nameMimeMap, &CLSID_MimeType );
if( SUCCEEDED(hr) )
{
IDispatchPtr pMimeType;
hr = ::CoCreateInstance( CLSID_MimeType, NULL, CLSCTX_ALL, IID_IDispatch, (void**) &pMimeType );

DISPID idExtension = -1;
DISPID idMimeType = -1;
DISPID propPut = DISPID_PROPERTYPUT;

_bstr_t propName = “Extension”;
BSTR pName = (BSTR)propName;
BSTR* ppName = & pName;
if( SUCCEEDED(hr) )
hr = pMimeType->GetIDsOfNames( IID_NULL, ppName, 1, LOCALE_USER_DEFAULT, &idExtension );
if( SUCCEEDED(hr) )
{
DISPPARAMS dispParam;
dispParam.cArgs = 1;
dispParam.cNamedArgs = 1;
dispParam.rgvarg = & varFileType;
dispParam.rgdispidNamedArgs = &propPut;
hr = pMimeType->Invoke( idExtension, IID_NULL, LOCALE_USER_DEFAULT, DISPATCH_PROPERTYPUT, &dispParam, NULL, NULL, NULL);
}
if( SUCCEEDED(hr) )
{
propName = “MimeType”;
pName = (BSTR)propName;
ppName = & pName;
hr = pMimeType->GetIDsOfNames( IID_NULL, ppName, 1, LOCALE_USER_DEFAULT, &idMimeType );
}
if( SUCCEEDED(hr) )
{
DISPPARAMS dispParam;
dispParam.cArgs = 1;
dispParam.cNamedArgs = 1;
dispParam.rgvarg = & varMimeType;
dispParam.rgdispidNamedArgs = &propPut;
hr = pMimeType->Invoke( idMimeType, IID_NULL, LOCALE_USER_DEFAULT, DISPATCH_PROPERTYPUT, &dispParam, NULL, NULL, NULL);
}
if( SUCCEEDED(hr) )
{
SAFEARRAYBOUND bounds;
bounds.lLbound = lIdx;
bounds.cElements = lHigh - lIdx + 1 + 1;
hr = ::SafeArrayRedim(pArrMimeMap, &bounds);
if( SUCCEEDED(hr) )
hr = ::SafeArrayGetUBound(pArrMimeMap, 1, &lHigh);
_variant_t varMimeObj = (IDispatch*) pMimeType;
if( SUCCEEDED(hr) )
hr = ::SafeArrayPutElement(pArrMimeMap, &lHigh, &varMimeObj);
if( SUCCEEDED(hr) )
hr = pContainer->PutEx( ADS_PROPERTY_UPDATE, nameMimeMap, varMimeMap );
if( SUCCEEDED(hr) )
hr = pContainer->SetInfo();
}
}

if( SUCCEEDED(hr) )
strcpy(buf, “Added Mime type for .RDF files to IIS”);
else
strcpy(buf, “Failed to add MIME type to IIS”);
}
else
{
strcpy(buf, “MIME type already registered in IIS”);
}

}
pContainer->Release();

}

CoUninitialize();

Entry Filed under: Software

2 Comments Add your own

  • 1. ninlar  |  June 26th, 2008 at 01:25

    You are a life saver. ADSI is very poorly documented especially when it comes to the IIS provider. I am writing an installer, and the version of InstallShield we use does not support adding MIME types. This has saved me a ton time.

    Thanks.

  • 2. ninlar  |  June 27th, 2008 at 00:16

    You are a life saver. ADSI is very poorly documented especially the IIS provider. This will help out a lot with our installer.

Leave a Comment

You must be logged in to post a comment.

Trackback this post  |  Subscribe to the comments via RSS Feed


Calendar

March 2006
M T W T F S S
« Feb   Apr »
 12345
6789101112
13141516171819
20212223242526
2728293031  

Most Recent Posts