Using NSIS to install to a website other than the Default Website.
One of my biggest gripes with the default setup package that Visual Studio produces is that for projects, it only supports installing applications to the Default Website. I don't want all my applications on the default website, and I find it a pain to have to manually move them and then have the uninstaller not work.
Since we're ready for a new release of nGallery, we've been wanting a better setup package than the default, one that lets you chose which website, setup the SQL database (if you want to use it), and setup all write permissions. We've started writing an installation script using the Nullsoft Scriptable Install System (free software, yay!). There are tons of functions available on their website to do stuff like detect the .NET framework, detect IIS, and more. There wasn't one for allowing you to pick which website to use, so I thought I'd write one myself. The end result is an installer screen that will look like this:
![[Installer screen sample]](http://www.qgyen.net/code/websample.png)
This script outputs a small Windows Hosting Script that queries the IIS metabase to get the list of website's names and their physical path, then display the names in a drop down and allow you get the path of the one they picked.
First, the script to put in your .nsi:
Function GetWebsites
; Output the VBScript
FileOpen $4 "$PLUGINSDIR\GetWebsites.vbs" w
FileWrite $4 'Set IISOBJ = getObject("IIS://localhost/" & "w3svc")$\n'
FileWrite $4 'Set objFSO = CreateObject("Scripting.FileSystemObject")$\n'
FileWrite $4 'Set objTextFile = objFSO.OpenTextFile("$PLUGINSDIR\websites.ini", 8, True)$\n'
FileWrite $4 'AllSites = ""$\n'
FileWrite $4 'for each Web in IISOBJ$\n'
FileWrite $4 'if (Web.Class = "IIsWebServer") then$\n'
FileWrite $4 'Set IISWebSite = getObject("IIS://localhost/" & "w3svc" & "/" & Web.Name)$\n'
FileWrite $4 'Set IISWebSiteRoot = getObject("IIS://localhost/" & "w3svc" & "/" & Web.Name & "/root")$\n'
FileWrite $4 'AllSites = AllSites & "|" & IISWebSite.ServerComment$\n'
FileWrite $4 'objTextFile.WriteLine("[" & IISWebSite.ServerComment & "]")$\n'
FileWrite $4 'objTextFile.WriteLine("Path=" & IISWebSiteRoot.Path)$\n'
FileWrite $4 'objTextFile.WriteLine("")$\n'
FileWrite $4 'Set IISWebSiteRoot = nothing$\n'
FileWrite $4 'Set IISWebSite = Nothing$\n'
FileWrite $4 'end if$\n'
FileWrite $4 'next$\n'
FileWrite $4 'objTextFile.WriteLine("[Websites]")$\n'
FileWrite $4 'objTextFile.WriteLine("AllSites=" & Right(AllSites, Len(AllSites)-1))$\n'
FileWrite $4 'objTextFile.Close$\n'
FileWrite $4 'Set objFSO = Nothing$\n'
FileWrite $4 'Set objTextFile = Nothing$\n'
FileWrite $4 'Set IISOBj = Nothing$\n'
FileClose $4
; Execute the script
nsExec::Exec /TIMEOUT=20000 '"$SYSDIR\cscript.exe" "$PLUGINSDIR\GetWebsites.vbs"'
; Read the value and put it in the page's INI
ReadINIStr $0 "$PLUGINSDIR\websites.ini" "Websites" "AllSites"
WriteINIStr "$PLUGINSDIR\pick_website.ini" "Field 2" "ListItems" $0
FunctionEnd
You also might want a function to include a function to change an installation path of /myApp/ to \myApp\:
;--------------------------------
; StrSlash
; By dirtydingus
;
; Usage:
; Push $filenamestring (e.g. 'c:\this\and\that\filename.htm')
; Push "\"
; Call StrSlash
; Pop $R0
; Now $R0 contains 'c:/this/and/that/filename.htm'
Function StrSlash
Exch $R3 ; $R3 = needle ("\" or "/")
Exch
Exch $R1 ; $R1 = String to replacement in (haystack)
Push $R2 ; Replaced haystack
Push $R4 ; $R4 = not $R3 ("/" or "\")
Push $R6
Push $R7 ; Scratch reg
StrCpy $R2 ""
StrLen $R6 $R1
StrCpy $R4 "\"
StrCmp $R3 "/" loop
StrCpy $R4 "/"
loop:
StrCpy $R7 $R1 1
StrCpy $R1 $R1 $R6 1
StrCmp $R7 $R3 found
StrCpy $R2 "$R2$R7"
StrCmp $R1 "" done loop
found:
StrCpy $R2 "$R2$R4"
StrCmp $R1 "" done loop
done:
StrCpy $R3 $R2
Pop $R7
Pop $R6
Pop $R4
Pop $R2
Pop $R1
Exch $R3
FunctionEnd
Next, you'll need to use the InstallOptions plug in to show the custom page. I created a basic pick_website.ini which lets you pick the website and specify the installation directory.
[Settings]
NumFields=4
[Field 1]
Type=label
Text=Please select which Web Site to set the application to use.
Left=0
Right=-1
Top=0
Bottom=10
[Field 2]
Type=Droplist
Text=droplist
ListItems=
Left=25
Right=150
Top=10
Bottom=22
[Field 3]
Type=label
Text=Path for the application:
Left=0
Right=-1
Top=30
Bottom=40
[Field 4]
Type=text
Text=webPath
State=/myApp
Left=25
Right=150
Top=40
Bottom=52
Next, have the .nsi display the custom page and call our little function to get the websites. Along with my list of pages, I have a “Page custom PickWebsite”, which tells it to go to a page called PickWebsite instead of having them pick an installation directory (commented out the MUI_PAGE_DIRECTORY page). You'll also need to do the usual calls for InstallOptions pages to reserve the files and extract the INI on runtime.
Now our custom page function looks like this:
Function PickWebsite
Call GetWebsites
!insertmacro MUI_HEADER_TEXT "Webserver Configuration" "Choose the website and path that your installation will reside at."
!insertmacro MUI_INSTALLOPTIONS_DISPLAY "pick_website.ini"
FunctionEnd
And then in the installer section for your web application, use the following to set the $INSTDIR properly:
; Get the website path and the app path
ReadINIStr $0 "$PLUGINSDIR\pick_website.ini" "Field 2" "State"
ReadINIStr $1 "$PLUGINSDIR\websites.ini" "$0" "Path"
ReadINIStr $2 "$PLUGINSDIR\pick_website.ini" "Field 4" "State"
; First, convert the / from the path to \, then setup the installation path
Push $2
Push "/"
Call StrSlash
Pop $R0
StrCpy $INSTDIR "$1$R0"
SetOutPath "$INSTDIR"
Coming next time, I'll include a function to mark the folder as an application, which can be done with a rendition of the sample to create a virtual directory.
To get a complete sample script, feel free to fetch my sample. The sample includes the functions to check for the .NET Framework and if the user is has administrator priviledges. I need to update it to check for IIS and to setup the folder as an application. Will get to that this evening, hopefully.