Inno Setup IIS Installation and Configuration
we are using also IIS and thinking about to automate the IIS installation. As far as I know is it possible to do all the stuff via command line tools. So you can create your own Inno Custom Pages to retrieve the necessary data from the user. (You can use the Inno Setup Form Designer for this). Once you've collected all your stuff, you can create and then fill your prepared template batch files and in the post install the batches will be executed.
I would try it like this:
- try to do everything with a batch file concerning your IIS installation (Take a look at this archived page
- when you can do anything with batch files for the IIS Configuration, then you need to make template batch files for the Inno setup
- create custom pages for Inno to retrieve the necessary data
- in Inno, replace the template batches with the variables
- execute the batches in Inno
Hope this helps
Here is my complete code to handle IIS (Internet Information Services) with Inno Setup. It contains:
- Installation of .NET Framework 4
- Install/Uninstall of IIS+ASP.NET 4
- Registration and removal of Web-Sites/Apps
- Helper to get user-account an app pool is running on
- Helper to get physical path to a Web-Site
It has been tested with the following Windows editions: XP Pro SP3 x86, 2003 R2 x86, 2003 R2 x64, Vista Ultimate x64, 7 Home Premium x64, 2008 R2 x64, 2011 x64, 8.1 Pro x86, 10 Pro x64
Everything is done as passive as possible to not break any existing Web-Sites.
Important
This might need the Unicode-Version of Inno-Setup (I'm using 5.5.6(u)). It is also required to force 64-bit mode on x64 machines (otherwise registry and file access will redirect to 32-bit stuff):
[Setup]
ArchitecturesInstallIn64BitMode=x64
Required Helpers
- Some constants
- ExpandEnvironmentStrings => https://msdn.microsoft.com/en-us/library/windows/desktop/ms724265%28v=vs.85%29.aspx
- ExecWithResult => Reads what is written to STDOUT
- IIs7ExecAppCmd => Required for IIS installation since Vista/2008
#ifdef UNICODE
#define AW "W"
#else
#define AW "A"
#endif
const
// See https://msdn.microsoft.com/en-us/library/windows/desktop/ms681382.aspx
ERROR_SUCCESS = 0;
ERROR_INVALID_FUNCTION = 1;
ERROR_NOT_SUPPORTED = 50;
ERROR_NOT_FOUND = 1168;
ERROR_SUCCESS_REBOOT_REQUIRED = 3010;
function ExpandEnvironmentStrings(lpSrc: String; lpDst: String; nSize: DWORD): DWORD;
external 'ExpandEnvironmentStrings{#AW}@kernel32.dll stdcall';
function ExpandEnvVars(const Input: String): String;
var
Buf: String;
BufSize: DWORD;
begin
BufSize := ExpandEnvironmentStrings(Input, #0, 0);
if BufSize > 0 then
begin
SetLength(Buf, BufSize);
if ExpandEnvironmentStrings(Input, Buf, BufSize) = 0 then
RaiseException(Format('Expanding env. strings failed. %s', [SysErrorMessage(DLLGetLastError)]));
#if AW == "A"
Result := Copy(Buf, 1, BufSize - 2);
#else
Result := Copy(Buf, 1, BufSize - 1);
#endif
end
else
RaiseException(Format('Expanding env. strings failed. %s', [SysErrorMessage(DLLGetLastError)]));
end;
// Exec with output stored in result. ResultString will only be altered if True is returned.
function ExecWithResult(const Filename, Params, WorkingDir: String; const ShowCmd: Integer; const Wait: TExecWait; var ResultCode: Integer; var ResultString: String): Boolean;
var
TempFilename: String;
TempAnsiStr: AnsiString;
Command: String;
begin
TempFilename := ExpandConstant('{tmp}\~execwithresult.txt');
// Exec via cmd and redirect output to file. Must use special string-behavior to work.
Command := Format('"%s" /S /C ""%s" %s > "%s""', [ExpandConstant('{cmd}'), Filename, Params, TempFilename]);
Result := Exec(ExpandConstant('{cmd}'), Command, WorkingDir, ShowCmd, Wait, ResultCode);
if not Result then
Exit;
LoadStringFromFile(TempFilename, TempAnsiStr); // Cannot fail
ResultString := String(TempAnsiStr);
DeleteFile(TempFilename);
// Remove new-line at the end
if (Length(ResultString) >= 2) and (ResultString[Length(ResultString) - 1] = #13) and (ResultString[Length(ResultString)] = #10) then
Delete(ResultString, Length(ResultString) - 1, 2);
end;
function IIs7ExecAppCmd(Params: String; var ResultString: String; var ResultCode: Integer): Boolean;
var
AppCmdFilePath: String;
Command: String;
begin
AppCmdFilePath := ExpandConstant('{sys}\inetsrv\appcmd.exe');
Result := ExecWithResult(AppCmdFilePath, Params, '', SW_HIDE, ewWaitUntilTerminated, ResultCode, ResultString);
end;
.NET 4 Installation
Will install .NET 4.0 for Win XP/2003. .NET 4.6 for Vista/2008 and later. Will do nothing if .NET 4 is already included with the system (Win 8/2012/10). The following files are required and must be included in the setup:
- .NET 4.0 Web-Installer "dotNetFx40_Full_setup.exe" => https://www.microsoft.com/en-US/download/details.aspx?id=17851
- .NET 4.6 Web-Installer "NDP46-KB3045560-Web.exe" => https://www.microsoft.com/de-de/download/details.aspx?id=48130
- WIC x64 "wic_x64_enu.exe" => https://www.microsoft.com/de-de/download/details.aspx?id=1385
- WIC x86 "wic_x86_enu.exe" => https://www.microsoft.com/de-de/download/details.aspx?id=32
-
[Files]
Source: "dotNetFx40_Full_setup.exe"; DestDir: "{tmp}"; Flags: ignoreversion dontcopy
Source: "NDP46-KB3045560-Web.exe"; DestDir: "{tmp}"; Flags: ignoreversion dontcopy
Source: "wic_x64_enu.exe"; DestDir: "{tmp}"; Flags: ignoreversion dontcopy
Source: "wic_x86_enu.exe"; DestDir: "{tmp}"; Flags: ignoreversion dontcopy
Since I'm using the Web-Installers, an internet connection might be required during installation.
// Checks if .NET 4 "Full" is installed
function IsDotNet4FullInstalled(): Boolean;
var
Success: Boolean;
Install: Cardinal;
begin
try
ExpandConstant('{dotnet40}'); // This will throw an exception if .NET 4 is not installed at all
Success := RegQueryDWordValue(HKLM, 'SOFTWARE\Microsoft\NET Framework Setup\NDP\v4\Full', 'Install', Install); // Check for "Full" version
Result := Success and (Install = 1);
except
Result := False;
end;
end;
// Returns True if a restart is required.
function InstallDotNet4(): Boolean;
var
Version: TWindowsVersion;
ResultCode: Integer;
Success: Boolean;
begin
GetWindowsVersionEx(Version);
if (Version.Major <= 5) then // 4.0 for XP, 2003
ExtractTemporaryFile('dotNetFx40_Full_setup.exe')
else // 4.6
ExtractTemporaryFile('NDP46-KB3045560-Web.exe');
if (Version.Major <= 5) then // XP, 2003: Install .NET 4.0
begin
Success := Exec(ExpandConstant('{tmp}\dotNetFx40_Full_setup.exe'), '/passive', '', SW_HIDE, ewWaitUntilTerminated, ResultCode);
if Success and (ResultCode = 5100) then // Indicates that "Windows Imaging Component" is missing (probably 2003)
begin
if IsWin64 then
begin
ExtractTemporaryFile('wic_x64_enu.exe');
if not Exec(ExpandConstant('{tmp}\wic_x64_enu.exe'), '/passive', '', SW_HIDE, ewWaitUntilTerminated, ResultCode) or (ResultCode <> ERROR_SUCCESS) then
RaiseException('Failed to install "Windows Imaging Component": ' + SysErrorMessage(ResultCode) + ' (' + IntToStr(ResultCode) + ')');
// Retry .NET
Success := Exec(ExpandConstant('{tmp}\dotNetFx40_Full_setup.exe'), '/passive', '', SW_HIDE, ewWaitUntilTerminated, ResultCode);
end
else
begin
ExtractTemporaryFile('wic_x86_enu.exe');
if not Exec(ExpandConstant('{tmp}\wic_x86_enu.exe'), '/passive', '', SW_HIDE, ewWaitUntilTerminated, ResultCode) or (ResultCode <> ERROR_SUCCESS) then
RaiseException('Failed to install "Windows Imaging Component": ' + SysErrorMessage(ResultCode) + ' (' + IntToStr(ResultCode) + ')');
// Retry .NET
Success := Exec(ExpandConstant('{tmp}\dotNetFx40_Full_setup.exe'), '/passive', '', SW_HIDE, ewWaitUntilTerminated, ResultCode);
end;
end
end
else // Vista / 2008 or later: Install .NET 4.6
Success := Exec(ExpandConstant('{tmp}\NDP46-KB3045560-Web.exe'), '/passive', '', SW_HIDE, ewWaitUntilTerminated, ResultCode);
// Check for errors
if not Success or ((ResultCode <> ERROR_SUCCESS) and (ResultCode <> ERROR_SUCCESS_REBOOT_REQUIRED)) then
RaiseException('Failed to install .NET: ' + SysErrorMessage(ResultCode) + ' (' + IntToStr(ResultCode) + ')');
Result := ResultCode = ERROR_SUCCESS_REBOOT_REQUIRED;
end;
IIS Installation / Uninstall
Installs IIS with ASP.NET 4. Will activate the same (default-)features as if it was activated via GUI. After installation is completed, ASP.NET is (re-)registered with IIS.
Note: For Win XP/2003, the "Windows Installation CD" is required.
// Returns True if a restart is required. Throws exceptions.
function InstallIIs(): Boolean;
var
Version: TWindowsVersion;
Success: Boolean;
ResultCode: Integer;
IIS56IniFile: String;
begin
GetWindowsVersionEx(Version);
if (Version.Major <= 5) then // XP / 2003: Use "sysocmgr"
begin
IIS56IniFile := ExpandConstant('{tmp}\~iis56.ini');
SaveStringToFile(IIS56IniFile, '[Components]' + #13#10 + 'iis_common = ON' + #13#10 + 'iis_www = ON' + #13#10 + 'iis_inetmgr = ON', False);
Success := Exec('sysocmgr', ExpandConstant('/i:"{win}\inf\sysoc.inf" /u:"' + IIS56IniFile + '"'), '', SW_SHOW, ewWaitUntilTerminated, ResultCode);
DeleteFile(IIS56IniFile);
end
else if (Version.Major = 6) and (Version.Minor = 0) then // Vista / 2008: Use "pkgmgr"
begin
Success := Exec('pkgmgr', '/iu:' +
// Enables everything a fresh install would also (implicitly) enable.
// This is important in case IIS was already installed and some features manually disabled.
'WAS-WindowsActivationService;WAS-ProcessModel;WAS-NetFxEnvironment;WAS-ConfigurationAPI' +
';IIS-WebServerRole' +
';IIS-WebServerManagementTools;IIS-ManagementConsole' +
';IIS-WebServer' +
';IIS-ApplicationDevelopment;IIS-NetFxExtensibility;IIS-ASPNET;IIS-ISAPIExtensions;IIS-ISAPIFilter' +
';IIS-CommonHttpFeatures;IIS-HttpErrors;IIS-DefaultDocument;IIS-StaticContent;IIS-DirectoryBrowsing' +
';IIS-Performance;IIS-HttpCompressionStatic' +
';IIS-Security;IIS-RequestFiltering' +
';IIS-HealthAndDiagnostics;IIS-RequestMonitor;IIS-HttpLogging',
'', SW_HIDE, ewWaitUntilTerminated, ResultCode);
end
else if (Version.Major = 6) and (Version.Minor = 1) then // 7 / 2008 R2 / 2011: Use "Dism"
begin
Success := Exec('Dism', '/Online /Enable-Feature' +
// Enables everything a fresh install would also (implicitly) enable.
// This is important in case IIS was already installed and some features manually disabled.
// "Parent fetaures" are NOT automatically enabled by "Dism".
' /FeatureName:WAS-WindowsActivationService /FeatureName:WAS-ProcessModel /FeatureName:WAS-NetFxEnvironment /FeatureName:WAS-ConfigurationAPI' +
' /FeatureName:IIS-WebServerRole' +
' /FeatureName:IIS-WebServerManagementTools /FeatureName:IIS-ManagementConsole' +
' /FeatureName:IIS-WebServer' +
' /FeatureName:IIS-ApplicationDevelopment /FeatureName:IIS-NetFxExtensibility /FeatureName:IIS-ASPNET /FeatureName:IIS-ISAPIExtensions /FeatureName:IIS-ISAPIFilter' +
' /FeatureName:IIS-CommonHttpFeatures /FeatureName:IIS-HttpErrors /FeatureName:IIS-DefaultDocument /FeatureName:IIS-StaticContent /FeatureName:IIS-DirectoryBrowsing' +
' /FeatureName:IIS-Performance /FeatureName:IIS-HttpCompressionStatic' +
' /FeatureName:IIS-Security /FeatureName:IIS-RequestFiltering' +
' /FeatureName:IIS-HealthAndDiagnostics /FeatureName:IIS-RequestMonitor /FeatureName:IIS-HttpLogging',
'', SW_HIDE, ewWaitUntilTerminated, ResultCode);
end
else // 8 / 2012 and later: Use "Dism"
begin
Success := Exec('Dism', '/Online /Enable-Feature' +
// Enables everything a fresh install would also (implicitly) enable.
// This is important in case IIS was already installed and some features manually disabled.
' /FeatureName:WAS-WindowsActivationService /FeatureName:WAS-ProcessModel /FeatureName:WAS-NetFxEnvironment /FeatureName:WAS-ConfigurationAPI' +
' /FeatureName:IIS-ManagementConsole' +
' /FeatureName:IIS-HttpErrors /FeatureName:IIS-DefaultDocument /FeatureName:IIS-StaticContent /FeatureName:IIS-DirectoryBrowsing' +
' /FeatureName:IIS-ASPNET45' +
' /FeatureName:IIS-HttpCompressionStatic' +
' /FeatureName:IIS-RequestFiltering' +
' /FeatureName:IIS-HttpLogging' +
' /All', // Implicitly enables dependent features
'', SW_HIDE, ewWaitUntilTerminated, ResultCode);
end;
if not Success or ((ResultCode <> ERROR_SUCCESS) and (ResultCode <> ERROR_SUCCESS_REBOOT_REQUIRED)) then
RaiseException('Cannot install IIS: ' + SysErrorMessage(ResultCode) + ' (' + IntToStr(ResultCode) + ')');
Result := ResultCode = ERROR_SUCCESS_REBOOT_REQUIRED;
// Register ASP.NET 4 with IIS. This is required since .NET 4 was probably installed before IIS.
// This will NOT change existing web-sites (which might be using other ASP.NET versions already)
if not Exec(ExpandConstant('{dotnet40}') + '\aspnet_regiis.exe', '-iru -enable', '', SW_HIDE, ewWaitUntilTerminated, ResultCode) or
(ResultCode <> ERROR_SUCCESS) then
RaiseException('Cannot register ASP.NET: ' + SysErrorMessage(ResultCode) + ' (' + IntToStr(ResultCode) + ')');
end;
// Returns True if a restart is required. Throws exceptions.
function UninstallIIs(): Boolean;
var
Version: TWindowsVersion;
Success: Boolean;
ResultCode: Integer;
IIS56IniFile: String;
begin
GetWindowsVersionEx(Version);
if (Version.Major <= 5) then // XP / 2003: Use "sysocmgr"
begin
IIS56IniFile := ExpandConstant('{tmp}\~iis56.ini');
SaveStringToFile(IIS56IniFile, '[Components]' + #13#10 + 'iis_common = OFF' + #13#10 + 'iis_www = OFF' + #13#10 + 'iis_inetmgr = OFF', False);
Success := Exec('sysocmgr', ExpandConstant('/i:"{win}\inf\sysoc.inf" /u:"' + IIS56IniFile + '"'), '', SW_SHOW, ewWaitUntilTerminated, ResultCode);
DeleteFile(IIS56IniFile);
end
else if (Version.Major = 6) and (Version.Minor = 0) then // Vista / 2008: Use "pkgmgr"
Success := Exec('pkgmgr', '/norestart /uu:IIS-WebServerRole', '', SW_HIDE, ewWaitUntilTerminated, ResultCode)
else // 7 / 2008 R2 and later: Use "Dism"
Success := Exec('Dism', '/NoRestart /Online /Disable-Feature /FeatureName:IIS-WebServerRole', '', SW_HIDE, ewWaitUntilTerminated, ResultCode);
if not Success or ((ResultCode <> ERROR_SUCCESS) and (ResultCode <> ERROR_SUCCESS_REBOOT_REQUIRED)) then
RaiseException('Cannot uninstall IIS: ' + SysErrorMessage(ResultCode) + ' (' + IntToStr(ResultCode) + ')');
Result := ResultCode = ERROR_SUCCESS_REBOOT_REQUIRED;
end;
Web-Site/App Registration and Removal
Creates a virtual directory and an ASP.NET 4 Application Pool.
Note: IIsServerNumber is the actual Web-Site which usually defaults to 1 (=> "Default Web Site").
Usage:
RegisterAppAtIIs('MyAppName', 1, 'MyAppAppPoolName');
DeleteAppFromIIs('MyAppName', 1, 'MyAppAppPoolName');
// Throws exceptions.
procedure RegisterAppAtIIs(const IIsAppName: String; const IIsServerNumber: Integer; const IIsApplicationPoolName: String);
var
Version: TWindowsVersion;
// IIS 5.1 - 6.0
IIS, WebService, WebSite, WebRoot, vDir, AppPools, AppPool: Variant;
// IIS 7 and later
ResultCode: Integer;
ExecResult: String;
WebSiteName: String;
begin
GetWindowsVersionEx(Version);
if (Version.Major = 5) and (Version.Minor <= 2) then // XP, 2003: IIS 5.1 - 6.0
begin
try
// Create the main IIS COM Automation object
IIS := CreateOleObject('IISNamespace');
WebService := IIS.GetObject('IIsWebService', 'localhost/W3SVC');
// Get web site
WebSite := WebService.GetObject('IIsWebServer', IntToStr(IIsServerNumber));
WebRoot := WebSite.GetObject('IIsWebVirtualDir', 'Root');
except
RaiseException(Format('Web-site #%d not found: ', [IIsServerNumber]) + GetExceptionMessage());
end;
// Delete the virtual dir if it already exists
try
WebRoot.Delete('IIsWebVirtualDir', IIsAppName);
WebRoot.SetInfo();
except
end;
if (Version.Minor = 1) then // XP: IIS 5.1
begin
// Create the virtual directory
try
vDir := WebRoot.Create('IIsWebVirtualDir', IIsAppName);
vDir.AccessRead := True;
vDir.AccessScript := True;
vDir.AppFriendlyName := IIsAppName;
vDir.Path := ExpandConstant('{app}');
vDir.AppCreate(True); // Create in "InProc" mode (don't really know why though)
vDir.SetInfo();
except
RaiseException('Cannot create virtual directory: ' + GetExceptionMessage());
end;
end
else if (Version.Minor = 2) then // 2003: IIS 6.0
begin
// Application pool stuff
AppPools := WebService.GetObject('IIsApplicationPools', 'AppPools');
try
// Check if the application pool already exists
AppPool := AppPools.GetObject('IIsApplicationPool', IIsApplicationPoolName);
except
AppPool := Null;
end;
if VarIsNull(AppPool) then
begin
// Create the application pool
try
AppPool := AppPools.Create('IIsApplicationPool', IIsApplicationPoolName);
AppPool.SetInfo();
except
RaiseException('Cannot add application pool: ' + GetExceptionMessage());
end;
end;
// Create the virtual directory
try
vDir := WebRoot.Create('IIsWebVirtualDir', IIsAppName);
vDir.AccessRead := True;
vDir.AccessScript := True;
vDir.AppFriendlyName := IIsAppName;
vDir.Path := ExpandConstant('{app}');
vDir.AppCreate(True); // Create in "InProc" mode
vDir.AppPoolId := IIsApplicationPoolName;
vDir.SetInfo();
except
RaiseException('Cannot create virtual directory: ' + GetExceptionMessage());
end;
end;
// Register handlers for ASP.NET 4 (important if other ASP.NET versions are present)
if not ExecWithResult(ExpandConstant('{dotnet40}') + '\aspnet_regiis.exe',Format('-s "W3SVC/%d/ROOT/%s"', [IIsServerNumber, IIsAppName]),'',SW_HIDE,ewWaitUntilTerminated, ResultCode, ExecResult) or (ResultCode <> ERROR_SUCCESS) then
RaiseException('Cannot set ASP.NET version: ' + SysErrorMessage(ResultCode) + ' (' + IntToStr(ResultCode) + ')');
end
else if (Version.Major >= 6) then // Vista / 2008 or later : IIS 7 or later
begin
// Get name of web-site
if not IIs7ExecAppCmd(Format('list site /id:%d /text:name', [IIsServerNumber]), ExecResult, ResultCode) then
RaiseException(Format('Cannot get name of web-site #%d: ', [IIsServerNumber]) + SysErrorMessage(ResultCode) + ' (' + IntToStr(ResultCode) + ')');
if (Length(Trim(ExecResult)) = 0) then
RaiseException(Format('Web-site #%d not found.', [IIsServerNumber]));
WebSiteName := ExecResult;
// Delete the application if it already exists
if not IIs7ExecAppCmd(Format('delete app "%s/%s"', [WebSiteName, IIsAppName]), ExecResult, ResultCode) or
((ResultCode <> ERROR_SUCCESS) and (ResultCode <> ERROR_NOT_FOUND) and (ResultCode <> ERROR_NOT_SUPPORTED)) then
RaiseException('Cannot delete application: ' + SysErrorMessage(ResultCode) + ' (' + IntToStr(ResultCode) + ')');
// Check if the application pool already exists (we don't delete it, since another GVS instance might be using it too)
if not IIs7ExecAppCmd(Format('list apppool "%s" /text:name', [IIsApplicationPoolName]), ExecResult, ResultCode) or
((ResultCode <> ERROR_SUCCESS) and (ResultCode <> ERROR_INVALID_FUNCTION)) then
RaiseException('Cannot list application pools: ' + SysErrorMessage(ResultCode) + ' (' + IntToStr(ResultCode) + ')');
// Create the application pool
if (ExecResult <> IIsApplicationPoolName) then
begin
if not IIs7ExecAppCmd(Format('add apppool /name:"%s" /managedRuntimeVersion:v4.0', [IIsApplicationPoolName]), ExecResult, ResultCode) or (ResultCode <> ERROR_SUCCESS) then
RaiseException('Cannot add application pool: ' + SysErrorMessage(ResultCode) + ' (' + IntToStr(ResultCode) + ')');
end;
// Create the application
if not IIs7ExecAppCmd(Format('add app /site.name:"%s" /path:"/%s" /physicalPath:"%s" /applicationPool:"%s"', [WebSiteName, IIsAppName, ExpandConstant('{app}'), IIsApplicationPoolName]), ExecResult, ResultCode) or
(ResultCode <> ERROR_SUCCESS) then
RaiseException('Cannot add application: ' + SysErrorMessage(ResultCode) + ' (' + IntToStr(ResultCode) + ')');
end
else
RaiseException('Cannot register web-site: Unknown OS version.');
end;
// Throws exceptions.
procedure DeleteAppFromIIs(const IIsAppName: String; const IIsServerNumber: Integer; const IIsApplicationPoolName: String);
var
Version: TWindowsVersion;
// IIS 5.1 - 6.0
IIS, WebService, WebSite, WebRoot, AppPools: Variant;
// IIS 7 and later
ResultCode: Integer;
ExecResult: String;
WebSiteName: String;
begin
GetWindowsVersionEx(Version);
if (Version.Major = 5) and (Version.Minor <= 2) then // XP, 2003: IIS 5.1 - 6.0
begin
try
// Create the main IIS COM Automation object
IIS := CreateOleObject('IISNamespace');
WebService := IIS.GetObject('IIsWebService', 'localhost/W3SVC');
// Get web site
WebSite := WebService.GetObject('IIsWebServer', IntToStr(IIsServerNumber));
WebRoot := WebSite.GetObject('IIsWebVirtualDir', 'Root');
except
RaiseException(Format('Web-site #%d not found: ', [IIsServerNumber]) + GetExceptionMessage());
end;
// Delete the virtual dir
try
WebRoot.Delete('IIsWebVirtualDir', IIsAppName);
WebRoot.SetInfo();
except
end;
if (Version.Minor = 2) then // 2003: IIS 6.0
begin
// Application pool stuff
AppPools := WebService.GetObject('IIsApplicationPools', 'AppPools');
try
// Delete the application pool
AppPools.Delete('IIsApplicationPool', IIsApplicationPoolName);
except
end;
end;
end
else if (Version.Major >= 6) then // Vista / 2008 or later : IIS 7 or later
begin
// Get name of web-site
if not IIs7ExecAppCmd(Format('list site /id:%d /text:name', [IIsServerNumber]), ExecResult, ResultCode) then
RaiseException(Format('Cannot get name of web-site #%d: ', [IIsServerNumber]) + SysErrorMessage(ResultCode) + ' (' + IntToStr(ResultCode) + ')');
if (Length(Trim(ExecResult)) = 0) then
RaiseException(Format('Web-site #%d not found.', [IIsServerNumber]));
WebSiteName := ExecResult;
// Delete the application
if not IIs7ExecAppCmd(Format('delete app "%s/%s"', [WebSiteName, IIsAppName]), ExecResult, ResultCode) or
((ResultCode <> ERROR_SUCCESS) and (ResultCode <> ERROR_NOT_FOUND) and (ResultCode <> ERROR_NOT_SUPPORTED)) then
RaiseException('Cannot delete application: ' + SysErrorMessage(ResultCode) + ' (' + IntToStr(ResultCode) + ')');
// Delete the application pool
if not IIs7ExecAppCmd(Format('delete apppool "%s"', [IIsApplicationPoolName]), ExecResult, ResultCode) or
((ResultCode <> ERROR_SUCCESS) and (ResultCode <> ERROR_NOT_FOUND)) then
RaiseException('Cannot delete application pool: ' + SysErrorMessage(ResultCode) + ' (' + IntToStr(ResultCode) + ')');
end
else
RaiseException('Cannot delete web-site: Unknown OS version.');
end;
Useful Helpers
Finds the Windows user-account used by ASP.NET to read/write files in the virtual directory. Useful to set permissions for "App_Data".
// Throws exceptions.
function GetIIsAppPoolIdentity(const IIsApplicationPoolName: String): String;
var
Version: TWindowsVersion;
ResultCode: Integer;
ExecResult: String;
IIS, AppPool: Variant;
begin
GetWindowsVersionEx(Version);
if (Version.Major = 5) and (Version.Minor = 1) then // XP: IIS 5.1
begin
// TODO: Should be read from "[.NET 4]\Config\machine.config" -> system.web.processModel.userName
// - "System"
// - "Machine" -> "ASPNET" (default)
// - A custom user account
// See https://msdn.microsoft.com/en-us/library/dwc1xthy.aspx
Result := 'ASPNET';
end
else if (Version.Major = 5) and (Version.Minor = 2) then // 2003: IIS 6.0
begin
try
IIS := CreateOleObject('IISNamespace');
AppPool := IIS.GetObject('IIsApplicationPool', 'localhost/W3SVC/AppPools/' + IIsApplicationPoolName);
if (AppPool.AppPoolIdentityType = 0) then
Result := 'NT AUTHORITY\SYSTEM'
else if (AppPool.AppPoolIdentityType = 1) then
Result := 'NT AUTHORITY\LOCAL SERVICE'
else if (AppPool.AppPoolIdentityType = 2) then
Result := 'NT AUTHORITY\NETWORKSERVICE'
else if (AppPool.AppPoolIdentityType = 3) then
Result := AppPool.WAMUserName
else
RaiseException('Cannot get application pool identity: Unknown identity type (' + IntToStr(AppPool.AppPoolIdentityType) + ')');
except
RaiseException('Cannot get application pool identity: Unable to get object');
end;
end
else if (Version.Major >= 6) then // Vista / 2008 or later
begin
if not IIs7ExecAppCmd(Format('list apppool "%s" /text:processModel.identityType', [IIsApplicationPoolName]), ExecResult, ResultCode) or
(ResultCode <> ERROR_SUCCESS) then
RaiseException('Cannot get application pool identity.');
if (ExecResult = 'LocalSystem') then
Result := 'NT AUTHORITY\SYSTEM'
else if (ExecResult = 'LocalService') then
Result := 'NT AUTHORITY\LOCAL SERVICE'
else if (ExecResult = 'NetworkService') then
Result := 'NT AUTHORITY\NETWORKSERVICE'
else if (ExecResult = 'ApplicationPoolIdentity') then
Result := 'IIS AppPool\' + IIsApplicationPoolName
else
RaiseException('Cannot get application pool identity: Unknown identity type "' + ExecResult + '"');
end
else
RaiseException('Cannot get application pool identity: Unknown OS version: ' + IntToStr(Version.Major) + '.' + IntToStr(Version.Minor));
end;
Returns the physical path to a Web-Site. Useful to find the correct place to install files to.
// Throws exceptions.
function IIsGetPhysicalPath(const IIsServerNumber: Integer): String;
var
Version: TWindowsVersion;
IIS, WebService, WebSite, WebRoot: Variant;
ResultCode: Integer;
ExecResult: String;
WebSiteName: String;
PhysPath: String;
begin
GetWindowsVersionEx(Version);
if (Version.Major = 5) then
begin
try
// Create the main IIS COM Automation object
IIS := CreateOleObject('IISNamespace');
WebService := IIS.GetObject('IIsWebService', 'localhost/W3SVC');
// Get web site
WebSite := WebService.GetObject('IIsWebServer', IntToStr(IIsServerNumber));
WebRoot := WebSite.GetObject('IIsWebVirtualDir', 'Root');
except
RaiseException(Format('Web-site #%d not found: ', [IIsServerNumber]) + GetExceptionMessage());
end;
PhysPath := WebRoot.Path;
end
else if (Version.Major >= 6) then // Vista / 2008 or later
begin
// Get name of web-site
if not IIs7ExecAppCmd(Format('list site /id:%d /text:name', [IIsServerNumber]), ExecResult, ResultCode) then
RaiseException(Format('Cannot get name of web-site #%d: ', [IIsServerNumber]) + SysErrorMessage(ResultCode) + ' (' + IntToStr(ResultCode) + ')');
if (Length(Trim(ExecResult)) = 0) then
RaiseException(Format('Web-site #%d not found.', [IIsServerNumber]));
WebSiteName := ExecResult;
// Get physical path
if not IIs7ExecAppCmd(Format('list vdir /app.name:"%s"/ /text:physicalPath', [WebSiteName]), ExecResult, ResultCode) or
(ResultCode <> ERROR_SUCCESS) then
RaiseException(Format('Cannot get physical path to web-site "%s": ', [WebSiteName]) + SysErrorMessage(ResultCode) + ' (' + IntToStr(ResultCode) + ')');
PhysPath := ExecResult;
end
else
RaiseException('Cannot get physical path to web-site root: Unknown OS version: ' + IntToStr(Version.Major) + '.' + IntToStr(Version.Minor));
// Expand environment variables in path (e.g. "%SystemDrive%")
Result := ExpandEnvVars(PhysPath);
end;
So, in the end what I did was create a separate executable coded in C# which will install IIS through the command line before installation and will then register IIS with the appropriate application pool etc after installation. I then proceeded to call the executable through Inno Setup in PrepareToInstall to install IIS, and in CurStepChanged to register IIS.