davidg
Level 4

Website Not Removed On Uninstall Due To SubKey

I've created a Website in a Basic MSI project, and when I uninstall the website is not removed. I've read the log that this because a sub key is present in the web app.

I did place a key when setting different access to a specific folder to win authentication through a custom action.

Is there any way in the IIs configuration screen to set one folder to win authentication? Or can I remove the website through InstallScript? Example?

Thanks,
David G
Labels (1)
0 Kudos
3 Replies
phill_mn
Level 7

In an InstallScript project, I do it this way:

#define _IIS_INVALID_SITEID_NUM -1
#define _IIS_MAX_SCAN 20
//returned if can't find any ID
#define _IIS_DEF_SITEID "2"
//used to seed search, unrealted to _IIS_DEF_SITEID
#define _IIS_SITEID_SCAN_SEED 0
//#define _IIS51_DEFAULT_SITE "Default Web Site"
#define _IIS51_DEFAULT_SITEID "1"
#define _IIS_DEFAULT_BINDING ":80:"

#define _IIS_STOP_SITE 1


/*///////////////////////////////////////////////////////////////////////////
// prototype BOOL IISGetSiteID(string, BYREF string, BYREF BOOL);
//
// If site with szName is located then the site ID is returned in svSiteID
// and bFound is set to TRUE. The Function also returns TRUE.
// If site with szName is not located, then save site ID of last valid site,
// and after reach max retries, return one more than the last valid site ID. The
// function returns TRUE if returning an incremented SiteID or FALSE if
// the SiteID is assumed to be a default (due to some error).
// svSiteID can be intialized to seed the scan, otherwise a default is assumed.
*//////////////////////////////////////////////////////////////////////////////
function BOOL IISGetSiteID(szName, svSiteID, bFound)
number nReturn, nSiteID, nBadIDretry, nLastSiteID;
string szMsg, szSiteID, szWebComment;
string szIPAddress;
BOOL bDone, bIISExists, bIDok;
BOOL bGiveUp; //true when quite scan due to maximum retries
OBJECT objW3SVC, objSite;
#if 0 //used to research ServerBindings, not sure that we will ever need this again
#define _GET_BINDING
LIST listBindings;
#else
#ifdef _GET_BINDING
#undef _GET_BINDING
#endif
#endif
begin
bFound = FALSE;
/*Make sure IIS exists*/
if (!g_bIISExists) then
svSiteID = _IIS_DEF_SITEID; //ASSUME 1
return FALSE;
endif;
bDone = FALSE;
bGiveUp = FALSE;
nBadIDretry = 0;
nLastSiteID = _IIS_INVALID_SITEID_NUM;
nReturn = StrLength(svSiteID);
if ( nReturn > 0) then
nReturn = StrToNum(nSiteID, svSiteID);
if (nReturn < 0) then
nSiteID = _IIS_SITEID_SCAN_SEED;
endif;
else
nSiteID = _IIS_SITEID_SCAN_SEED;
endif;
/*no try-catch but already checked for IIS*/
set objW3SVC = CoGetObject("IIS://LocalHost/W3SVC", "");
if (IsObject(objW3SVC)) then
while (bDone = FALSE)
NumToStr(szSiteID, nSiteID);
/*orig code used try-catch, but no point in this situation*/
set objSite = CoGetObject("IIS://LocalHost/W3SVC/"+szSiteID, "");
if (IsObject(objSite)) then
bIDok = TRUE;
nLastSiteID = nSiteID;
nBadIDretry = 0;
/*Check for website, and return ID*/
if (szName != "") then
/*IIS6 can get messed up so that it says that a Site object is valid
but then throws and unhandled exception on the call to "objSite.ServerComment"
so use try...catch*/
try
szWebComment = objSite.ServerComment;
catch
szWebComment = ""; //assume null string if an exception was thrown
endcatch;
if (szWebComment = szName) then
bFound = TRUE;
bDone = TRUE;
#ifdef _GET_BINDING
/*This code was used to research what the objSite.ServerBindings
was set to. If we ever need to pass ServerBindings 'array' to a
function, declare the function with a VARIANT arg.*/
listBindings = ListCreate(STRINGLIST);
if( listBindings != LIST_NULL) then
ListAppendFromArray ( listBindings, objSite.ServerBindings, TRUE);
SdShowInfoList("Test", "Bindings List", listBindings);
ListDestroy(listBindings);
endif;
#endif
endif;
endif;
else
bIDok = FALSE;
endif;

if (!bDone) then
if(!bIDok) then
nBadIDretry = nBadIDretry + 1;
endif;
nSiteID = nSiteID + 1;
if (nBadIDretry > _IIS_MAX_SCAN) then //ASSUME more than 20 bad IDs in a row, and we can stop scanning
bGiveUp = TRUE;
bDone = TRUE;
endif;
endif;
endwhile;
else
/*Since IIS exists, prior to IIS7 this should never happen, but it may happen
in IIS7 if IIS6ManagementObjects were disabled*/
if (SYSINFO.nWinMajor >= 6) then
MessageBox(@ERR_NO_IIS_METABASE, MB_OK);
abort;
else
MessageBox(@ERR_NO_W3SVC_SITEID, SEVERE);
endif;
endif;

if (IsObject(objW3SVC)) then
set objW3SVC = NOTHING;
endif;

if (IsObject(objSite)) then
set objSite = NOTHING;
endif;

if(bGiveUp) then
if (nLastSiteID > _IIS_INVALID_SITEID_NUM) then
nSiteID = nLastSiteID + 1;
nReturn = NumToStr(svSiteID, nSiteID);
if (nReturn < 0) then
svSiteID = _IIS_DEF_SITEID; //ASSUME 1
return FALSE;
else
return TRUE; //site was not found, but succeded in locating a SiteID to use
endif;
else //never found a site
svSiteID = _IIS_DEF_SITEID; //ASSUME that this is lowest site ID that we want to use
return FALSE;
endif;
endif; //else got here because we found the site.
svSiteID = szSiteID;
return TRUE;
end;

/*///////////////////////////////////////////////
// prototype BOOL ControlIISSite(string, number, BOOL);
//
// This procedure issues control commands to the IIS Admin object
*//////////////////////////////////////////////////
function BOOL ControlIISSite(szSite, nCommand, bIgnorCmdErr)
BOOL bSuccess;
string szSitePath, szMsg;
OBJECT objSite;
begin
/*Make sure IIS exists*/
if (!g_bIISExists) then
if (bIgnorCmdErr) then
return TRUE;
else
return FALSE;
endif;
endif;

szSitePath = "IIS://LocalHost/W3SVC/" + szSite;
bSuccess = TRUE;
try
set objSite = CoGetObject(szSitePath, "");
catch
szMsg = @ERR_NO_CONNECT;
Disable(BACKBUTTON);
SprintfBox(SEVERE, IFX_PRODUCT_NAME, szMsg, szSitePath);
Enable(BACKBUTTON);
bSuccess = FALSE;
endcatch;

if (bSuccess) then
try
switch (nCommand)
case _IIS_STOP_SITE:
szMsg = @ERR_IIS_STOP_FAILED;
objSite.Stop;
case _IIS_START_SITE:
//if(SYSINFO.WINNT.bWinXP) then
/*make sure all other web sites are stopped, or we will not be able
to start this site*/
IIsStopAllSites();
//endif;
szMsg = @ERR_IIS_START_FAILED;
objSite.Start;
default:
endswitch;
catch

if (bIgnorCmdErr) then
return TRUE;
endif;
Disable(BACKBUTTON);
SprintfBox(SEVERE, IFX_PRODUCT_NAME, szMsg, szSitePath);
Enable(BACKBUTTON);
bSuccess = FALSE;
endcatch;
endif;

if (IsObject(objSite)) then
set objSite = NOTHING;
endif;
return bSuccess;
end;

/*///////////////////////////////////////////////////////////////////////////
// prototype BOOL IISDelWebSite(string, string);
//
// This procedure is used to delete a web site. Either the name of a
// web site can be provided and the site ID will be determined by a
// search for web sites with same name. the search will start at the site
// ID provided in svSiteID. If a name is not provided, then a Site ID must
// be provided which is ASSUMED to be the targeted web site.
// string svSiteID IN/OUT if input is > 1 then becomes start of search,
// otherwise defaults to ID = 1 to start.
*//////////////////////////////////////////////////////////////////////////////
function BOOL IISDelWebSite(szName, svSiteID)
number nReturn, nSiteID;
string szMsg, szSiteID;
string szIPAddress;
BOOL bRtn, bSearch, bFound;
OBJECT objW3SVC, objSite, objDir;
begin
/*Make sure IIS exists*/
if (!g_bIISExists) then
return FALSE;
endif;
bSearch = FALSE;
if (StrLength(szName) > 1) then
bSearch = TRUE;
else
if (StrLength(svSiteID) < 1) then
/*Invalid args passed to this call*/
Sprintf(szMsg, @ERR_IIS_PARAMS, szName, svSiteID);
/*This number is arbitrary, but must be negative*/
Err.Raise(-666, "IIS.RUL_IISDelWebSite", szMsg);
return FALSE;
endif;
endif; //ASSUME that svSiteID is the site to be deleted

if (bSearch) then
szSiteID = "0";
bRtn = IISGetSiteID(szName, szSiteID, bFound);
if(bRtn && bFound)then
nReturn = StrToNum(nSiteID, szSiteID);
if (nReturn < 0) then
return FALSE;
endif;
else
return FALSE;
endif;
else
szSiteID = svSiteID;
nReturn = StrToNum(nSiteID, szSiteID);
if (nReturn < 0) then
return FALSE;
endif;
endif;
/*Now the site ID has been defined*/
/*Stop the site. Suppress error because it may already be stopped*/
bRtn = ControlIISSite(szSiteID, _IIS_STOP_SITE, FALSE);

/*Now delete the site*/
bRtn = TRUE;
set objW3SVC = CoGetObject("IIS://LocalHost/W3SVC", "");
if (IsObject(objW3SVC)) then
try
set objSite = objW3SVC.Delete("IIsWebServer", nSiteID);
catch
//MessageBox("Couldn't delete web site in: IISDelWebSite", SEVERE);
//ignore the error
bRtn = FALSE;
endcatch;
else
bRtn = FALSE;
endif;


if (IsObject(objW3SVC)) then
set objW3SVC = NOTHING;
endif;

if (IsObject(objSite)) then
set objSite = NOTHING;
endif;

return bRtn;
end;
0 Kudos
cbragg
Level 7

That's standard behaviour. Installer doesn't know it created the key for this product so it won't delete it, because it presumes something else is using it.
If you know what the subkey is called and you're just setting a value, you can add that value to the registry table and just overwrite the value with your new one from you custom action (be careful to make it deferred after the WriteRegistryValues action otherwise your regsitry will go done after your one)
Otherwise you need to create another custom action that deletes it on uninstall.
0 Kudos
davidg
Level 4

In the end I tried two methods: one was to modify the tables directly, and added a virtual directory instead of using a IIsWebFile (Which doesn't seem to be supported in the acceptable types of IIS Metabase Objects (could be wrong). It didn't seem to work, so I ended up just using installscript to delete it.

Sidenote:
I find it strange that my problem doesn't occur in IIS7, it just deletes the website. Anyways, all is well now, thanks for the help.
0 Kudos