MAKEMSI quickly and reliably creates MSI files in a non-programmatic way
Have your say! Join the MAKEMSI discussion list or view archive! Suggest improvements. No question too simple or too complex.
[Bottom][Contents][Prev]: TryMeTemplate-SimpleSchedule.MM[Next]: TryMeDllCustomAction.MM
Have your say! Join the MAKEMSI discussion list or view archive! Suggest improvements. No question too simple or too complex.
\->MAKEMSI Installs...->Samples Installed by MAKEMSI->Samples - Build New MSI/MSM->TryMeCreateIisSite.MM

TryMeCreateIisSite.MM

This is one of the MAKEMSI samples which build a new MSI/MSM. This MSI makes use of these "TryMe.MM" files:

  1. TryMe.ver
  2. TryMe.rtf

See the Internet Information Services Applet to see how the created web sites look in that tool. You should also be sure to read the "IIS configuration" section of this manual.

;----------------------------------------------------------------------------
;    MODULE NAME:   TryMeCreateIisSite.MM
;
;        $Author:   USER "Dennis"  $
;      $Revision:   1.3  $
;          $Date:   01 Nov 2006 17:38:20  $
;       $Logfile:   C:/DBAREIS/Projects.PVCS/Win32/MakeMsi/TryMeCreateIisSite.MM.pvcs  $
;
;    DESCRIPTION
;    ~~~~~~~~~~~
;    Demonstrates a number of IIS things, it creates web sites under the
;    default plus a new one under port 1234.
;
;    The code backups the metabase but I highly recommend before testing any
;    code that modifies the metabase that you also MANUALLY back it up to be
;    obsolutely positive you have something you can restore.
;    The IIS/ADSI bit can easily get stuffed up while you are "playing"
;    around.
;
;    Personally I would always use an "IisWebVirtualDir" over an
;    "IisWebDirectory" (where possible) and you can google these class
;    names or any of the properties or methods demonstrated here to find
;    documentation or other example code.
;----------------------------------------------------------------------------


;----------------------------------------------------------------------------
;--- Some configuration for this script -------------------------------------
;----------------------------------------------------------------------------
#define  IIS_IS_RESTRICTED_TO_1_SITE_AT_A_TIME  Y        ;;Y/N - WIN2000/WINXP PRO won't allow secondary sites to start (licencing)
#define  BACKUP_IIS_CONFIGURATION_TO_PLAY_SAFE  Y        ;;Y/N
#define  IIS_SUPPORTS_APPPOOLS                  N        ;;Y/N - Certainly WINXP PRO doesn't...
#define  DEBUG_USER_ADSI_PROPERTY_ASSIGNMENT    Y        ;;Y/N - Add some debug code....
#define  IIS_STOP_START_SERVICES                N        ;;Y/N - Used to always do, but lately haven't found the need (maybe IIS fixes etc!)...
#define  IIS_MAX_BACKUPS_PER_DAY                1        ;;No more than this number will be made per day (0= No restriction)


;----------------------------------------------------------------------------
;--- Include MAKEMSI support (with my customisations and MSI branding) ------
;----------------------------------------------------------------------------
#define VER_FILENAME.VER  TryMe.Ver      ;;I only want one VER file for all samples!
#include "ME.MMH"


;----------------------------------------------------------------------------
;--- Display a message to the user if IIS isn't installed! ------------------
;----------------------------------------------------------------------------
;---[4Doco-IsIisInstalled?]---
#(
    ;--- Look for a service registry value ----------------------------------
    <$RegistryRead
         Property="IISINSTALLED4CONFIG"
             HKEY="LOCAL_MACHINE"
              KEY="SYSTEM\CurrentControlSet\Services\W3SVC"
             NAME="DisplayName"
        Condition="not Installed"
          Default="NoIIS"
    >
#)
#(
    <$AbortIf
        condition=^IISINSTALLED4CONFIG = "NoIIS"^
          Message=^The install of "<$ProdInfo.ProductName>"
                   version <$Productversion> can't continue as it
                   requires IIS (and IIS isn't installed on this machine).^
              Seq="AppSearch-"
              SeqTable="InstallUISequence InstallExecuteSequence"
    >
#)
;---[4Doco-IsIisInstalled?]---


;----------------------------------------------------------------------------
;--- Create INSTALLDIR ------------------------------------------------------
;----------------------------------------------------------------------------
<$DirectoryTree Key="INSTALLDIR" Dir="c:\program files\TryMe (makemsi sample)\<$MAKEMSI_MM_BASENAME>" CHANGE="\" PrimaryFolder="Y">


;----------------------------------------------------------------------------
;--- Lets install some pretty average HTML files for errors -----------------
;----------------------------------------------------------------------------
#define BUILD_HTML_DIR <$MAKEMSI_OTHER_DIR>\BuiltHtml
#(
    #define InstallPrettyAverageHtmlFile

    ;--- Create a file ------------------------------------------------------
    #define SHORTNAME.{$#1}.File  My{$#1}Message.html   ;;Name of file we will create for message
    <$FileMake "<$BUILD_HTML_DIR>\<$SHORTNAME.{$#1}.File>">
    #(  '<?NewLine>'
        <html>
            <head>
                <title>My pretty average error message for {$#1}</title>
            </head>
            <body>
                <p>My pretty average error message for {$#1}
            </body>
        </html>
    #)
    <$/FileMake>

    ;--- Add to installation directory --------------------------------------
    <$File
             Source="<$BUILD_HTML_DIR>\<$SHORTNAME.{$#1}.File>"
        Destination="[INSTALLDIR]\"
            Comment=^This is the new error message for server error number "{$#1}".^
    >
#)
<$InstallPrettyAverageHtmlFile "400">
<$InstallPrettyAverageHtmlFile "404">
<$InstallPrettyAverageHtmlFile "405">


;----------------------------------------------------------------------------
;--- Demonstrate a way to automatically add debug code ----------------------
;----------------------------------------------------------------------------
#if ['<$DEBUG_USER_ADSI_PROPERTY_ASSIGNMENT $$UPPER>' = 'N']
    #info "Not automatically adding some debug lines to user code..."
#else
    #info "Automatically adding some debug lines to user code..."
;---[4Doco-DebugProperyAssignmentOrMethodCall]---
#DefineRexx '@@AdsiInterCeptUserCode'
    ;--- If line starts with "." then log it --------------------------------
    FileLine = strip(FileLine)
    if  left(FileLine, 1) = '.' then
    do
        ;--- Debug the debug code! ------------------------------------------
        call info FileLine;

        ;--- Add a command to log the assignment or method invokation -------
        @@DbgTxt = 'CaDebug 0, "<<USER LINE>>: ' || ReplaceString(FileLine, '"', '""') || '"';
        FileLine = @@DbgTxt || '<?NewLine>' || FileLine;
    end;
#DefineRexx
#define+ ADSI_APPCFG_BEFORE_#INCLUDE    \
         #intercept '@@AdsiInterCeptUserCode'
#define+ ADSI_APPCFG_AFTER_#INCLUDE     \
         #intercept
;---[4Doco-DebugProperyAssignmentOrMethodCall]---
#endif


;----------------------------------------------------------------------------
;--- I recommend you backup IIS metabase! -----------------------------------
;----------------------------------------------------------------------------
#if ['<$BACKUP_IIS_CONFIGURATION_TO_PLAY_SAFE $$UPPER>' <> 'N']
;---[4Doco-BackupIisMetaBase]---
#define? IIS_MAX_BACKUPS_PER_DAY 5              ;;set a default value
<$Adsi "LocalHost" PARENT="IIS:/" Dump="Y">
   ;--- Need some VB constants (dumped the lot, you can search for doco) ----
   const MD_BACKUP_FORCE_BACKUP    = 4            ;;Backup
   const MD_BACKUP_HIGHEST_VERSION = &HFFFFFFFE   ;;Backup, Delete, Restore
   const MD_BACKUP_MAX_VERSION     = 9999         ;;Limit
   const MD_BACKUP_MAX_LEN         = 100          ;;Limit
   const MD_BACKUP_NEXT_VERSION    = &HFFFFFFFF   ;;Backup
   const MD_BACKUP_OVERWRITE       = 1            ;;Backup
   const MD_BACKUP_SAVE_FIRST      = 2            ;;Backup
   const MD_BACKUP_NO_MORE_BACKUPS = &H80070103

   ;--- Create a "today" based backup name (we may restrict backups under this name) ---
   dim BackupName : BackupName = "MAKEMSI - " & day(date()) & " " & MonthName(month(date())) & " " & year(date())   ;;Example = "MAKEMSI - 18 May 2006"

   ;--- Now perhaps we'd like to restrict number of backups made today? -----
   dim MakeBackup
   #if  ['<$IIS_MAX_BACKUPS_PER_DAY>' = '0']
        CaDebug 0, "No restrictions on the number of backups made..."
        MakeBackup = true
   #elseif
        ;--- User does want to restrict number of backups -------------------
        CaDebug 0, "We don't want more than <$IIS_MAX_BACKUPS_PER_DAY> backups made..."
        VbsCaLogInc(1)
        on error resume next
            dim Index : Index = 0
            dim BuVersion, BuLocation, BuDateTime
            do while true
                ;--- See if this "index" exists -----------------------------
                .EnumBackups BackupName, Index, BuVersion, BuLocation, BuDateTime
                if (err.number <> 0) then
                    if (err.number = MD_BACKUP_NO_MORE_BACKUPS) then
                        exit do
                    End If
                end if

                ;--- Found a backup -------------------------------------------------
                Index = Index + 1
                CaDebug 0, "BACKUP #" & Index & " created at " & BuDateTime & " UTC"
            loop
        on error goto 0
        CaDebug 0, "Found " & Index & " backups made today."     'Note backup name includes todays date!
        VbsCaLogInc(-1)
        if  Index < <$IIS_MAX_BACKUPS_PER_DAY> then
            MakeBackup = true
        else
            MakeBackup = false
        end if
   #endif

   ;--- Backup the IIS configuration ----------------------------------------
   if  not MakeBackup then
       CaDebug 0, "We don't want to make any more backups today..."
   else
       CaDebug 0, "Backing up the complete IIS configuration..."
       .Backup BackupName, MD_BACKUP_NEXT_VERSION, (MD_BACKUP_SAVE_FIRST or MD_BACKUP_FORCE_BACKUP)
       CaDebug 0, "Successfully backed up the IIS configuration"
   end if
<$/Adsi>
;---[4Doco-BackupIisMetaBase]---
#endif



;----------------------------------------------------------------------------
;--- Define Some directories ------------------------------------------------
;----------------------------------------------------------------------------
<$DirectoryTree Key="WWWROOTDIR" Dir="c:\Inetpub\wwwroot" MAKE="Y">
#(
    ;--- Need to know the "Default Web Site" directory ----------------------
    <$RegistryRead
       Property="WWWROOTDIR"                    ;;Typical value is "c:\Inetpub\wwwroot"
           HKEY="LOCAL_MACHINE"
            KEY="SOFTWARE\Microsoft\InetStp"
           NAME="PathWWWRoot"
      Condition="not Installed"
    >
#)
<$Component "CreateEmptyFolders" Create="Y" Directory_="<$AnyDir>" LM="Y">
    <$DirectoryTree Key="WWWDIR"  Dir="[WWWROOTDIR]WwwNewIisWebDirectory" MAKE="Y">      ;;Must match name of "IisWebDirectory" below!
    <$DirectoryTree Key="WEBDIR1" Dir="[INSTALLDIR]WEBDIR1"  MAKE="Y">
    <$DirectoryTree Key="WEBDIR2" Dir="[INSTALLDIR]WEBDIR2"  MAKE="Y">
    <$DirectoryTree Key="WEBDIR3" Dir="[INSTALLDIR]WEBDIR3"  MAKE="Y">
<$/Component>
#data 'CaIisData' 2                 ;;Used to pass data to the ADSI commands
    ;--- Our ADSI user code references these items --------------------------
    "INSTALLDIR"                   "[INSTALLDIR]"
    "WEBDIR1"                      "[WEBDIR1]"
    "WEBDIR2"                      "[WEBDIR2]"
    "WEBDIR3"                      "[WEBDIR3]"

    ;--- Configuration (the "ADSI" command will examine these) --------------
    "P1234NewWebApplication.SiteId"  "1234,1000,2000,3000,*"    ;;Specify one or more proposed "Site ID" numbers to use (* indicates its OK if these are all already used and we need to assign another)
#data


;----------------------------------------------------------------------------
;--- Create a new site under the standard IIS "Default Web Site" ------------
;----------------------------------------------------------------------------
<$Adsi "Default Web Site" Class="IIsWebServer">  ;;/1 (normally? Always?)
       ;--- Disable this site if required (is licence means we have a 1 site limit) ---
       #if ['<$IIS_IS_RESTRICTED_TO_1_SITE_AT_A_TIME $$UPPER>' = 'Y']
           ;--- We want to start the OTHER main site we create (on port "1234") ---
           CaDebug 0, "CONFIG ""IIS_IS_RESTRICTED_TO_1_SITE_AT_A_TIME"" = ""<$IIS_IS_RESTRICTED_TO_1_SITE_AT_A_TIME>"" so stopping default web site to allow another to be started below!"
           .Stop()
           CaDebug 0, "Stop completed"
       #endif

   ;--- Need to create web sites under the "ROOT" ---------------------------
   <$Adsi "ROOT">                                 ;;/1/ROOT
       ;--- Disable this site if required (if licence means we have a 1 site limit) ---
       ;#if ['<$IIS_IS_RESTRICTED_TO_1_SITE_AT_A_TIME $$UPPER>' = 'Y']
       ;    CaDebug 0, "CONFIG ""IIS_IS_RESTRICTED_TO_1_SITE_AT_A_TIME"" = ""<$IIS_IS_RESTRICTED_TO_1_SITE_AT_A_TIME>"" so disabling default web site to allow another to be created!"
       ;    .AppDisable()                ;;The AppDisable method has no effect if the application is running in-process (it doesn't work here...)
       ;    CaDebug 0, "AppDisable completed"
       ;#endif

       ;--- If "C:\Inetpub\wwwroot\NewIisWebDirectory" doesn't exist then you won't get an error but it won't show up in admin tool until you create it either) ---
       <$Adsi "WwwNewIisWebDirectory" Class="IisWebDirectory" Create="Y">
           .ContentIndexed = False
           .DontLog        = True

           ;--- Change 400 --------------------------------------------------
           CaDebug 0, "Processing .HttpErrors (400)..."
           VbsCaLogInc(1)
           dim HDIR   : HDIR   = VbsCaCadGet("INSTALLDIR")      ;;We installed the HTML for errors here...
           dim New400 : New400 = "400,*,FILE," & HDIR & "<$SHORTNAME.400.File>"
           dim Errors : Errors = oParent.HttpErrors     'This object doesn't exist yet, will get parent's by default
           dim Fnd400 : Fnd400 = -1                     '-1 = Not found
           dim i
           for i = lbound(Errors) to ubound(Errors)
               ;--- Try to determine index of a message to modify -----------
               CaDebug 0, "Found #" & i & " = " & Errors(i)
               dim ErrPart : ErrPart = split(Errors(i), ",")
               if  ErrPart(0) = "400" then
                   Fnd400 = i
                   VbsCaLogInc(1)
                        CaDebug 0, "Found 400..."
                   VbsCaLogInc(-1)
               end if
           next
           i = ubound(Errors)
           if   Fnd400 = -1 then
                ;--- We didn't find one to replace, so create new entry -----
                i = i + 1
                Fnd400 = i
                CaDebug 0, "Resizing array so we can add a 400 message at #" & Fnd400
                redim preserve Errors(Fnd400)
           end if
           Errors(Fnd400) = New400
           .HttpErrors = Errors
           VbsCaLogInc(-1)
           CaDebug 0, "Finished processing .HttpErrors (400)..."
       <$/Adsi>

       ;--- "NewIisWebVirtualDir" better as "path" not "forced" by IIS ------
       <$Adsi "WwwNewIisWebVirtualDir" Class="IisWebVirtualDir" Create="Y" DATA="CaIisData">
           ;--- Point this site to a particular directory -------------------
           .path  = VbsCaCadGet("WEBDIR1")

           ;--- Now update "Custom Errors" ----------------------------------
           dim HDIR : HDIR = VbsCaCadGet("INSTALLDIR")
           #(
                ;--- Note that this clears all existing entries and adds 2 new ---
                .HttpErrors = array(
                                    "404,*,FILE," & HDIR & "<$SHORTNAME.404.File>",   ;;Obviously this MSI hasn't actually installed these (its just an example)...
                                    "405,*,FILE," & HDIR & "<$SHORTNAME.405.File>"
                                   )
           #)
       <$/Adsi>
   <$/Adsi>
<$/Adsi>


;----------------------------------------------------------------------------
;--- Create a new IIS Application Pool --------------------------------------
;----------------------------------------------------------------------------
#if ['<$IIS_SUPPORTS_APPPOOLS $$UPPER>' = 'Y']
    <$Adsi "AppPools" Class="IIsApplicationPools">
        <$Adsi "My.New.AppPool" Class="IIsApplicationPool" Create="Y">
        <$/Adsi>
    <$/Adsi>
#endif


;----------------------------------------------------------------------------
;--- Create a new IIS web server/site ---------------------------------------
;----------------------------------------------------------------------------
<$Adsi "P1234NewWebApplication" Class="IISWebServer" Create="Y" DATA="CaIisData">   ;;Won't "start" on non-server versions of Windows (error 0x80070032 (-2147024846): "The request is not supported.")
   ;--- Basic configuration -------------------------------------------------
   .ServerBindings = array(":1234:")       ;;Binds to TCP-IP port 1234
   .ContentIndexed = False
   .DontLog        = True

   ;--- A web server MUST have a "ROOT" entry -------------------------------
   <$Adsi "ROOT" Class="IisWebVirtualDir" Create="Y">
       ;--- Basic configuration (for this virtual directory) ----------------
       .ContentIndexed = False
       .DontLog        = True
       .path           = VbsCaCadGet("WEBDIR2")
       #if ['<$IIS_SUPPORTS_APPPOOLS $$UPPER>' = 'Y']
           .AppPoolId  = "My.New.AppPool"
       #endif

       ;--- Modify characteristics for one specific file --------------------
       <$Adsi "ThisAsp.asp" Class="IIsWebFile" create="Y">      ;;Bote that this does not create or modify the file!
            .AuthAnonymous    = True
            .AuthNTLM         = True
       <$/Adsi>

       ;--- Make it possible to web "browse" a directory tree ---------------
       <$Adsi "P1234BrowsableDirTree" Class="IisWebVirtualDir" Create="Y">
           .path                   = VbsCaCadGet("WEBDIR3")
           .AuthAnonymous          = False     ;;No anonymous access
           .AuthNTLM               = True      ;;Use Windows Integrated security
           .AccessExecute          = False
           .AccessNoRemoteExecute  = False
           .AccessRead             = True
           .AccessScript           = False
           .AccessSource           = False
           .AspEnableParentPaths   = False
           .ContentIndexed         = False
           .DefaultDoc             = ""
           .DirBrowseShowExtension = True
           .DirBrowseShowLongDate  = True
           .DirBrowseShowSize      = True
           .DontLog                = True
           .EnableDirBrowsing      = True
       <$/Adsi>
   <$/Adsi>
<$/Adsi>


;----------------------------------------------------------------------------
;--- Stop/start IIS services ------------------------------------------------
;----------------------------------------------------------------------------
#if ['<$IIS_STOP_START_SERVICES $$UPPER>' <> 'N']
    #info "Adding stop/start IIS services code..."
;---[4Doco-StopStartIisServices]---
<$Component "StopStartIisServices" Directory_="<$AnyDir>" LM="Y">
    <$ServiceControl Name="IISADMIN" AtInstall="start stop" AtUnInstall="start stop" Wait="Y">
    <$ServiceControl Name="W3SVC"    AtInstall="start stop" AtUnInstall="start stop" Wait="Y">      ;;You may also want to restart ftp (maybe others)!
<$/Component>
;---[4Doco-StopStartIisServices]---
#endif


Microsoft awarded me an MVP (Most Valuable Professional award) in 2004, 2005, 2006, 2007, 2008 & 2009 for the Windows SDK (Windows Installer) area.Please email me any feedback, additional information or corrections.
See this page online (look for updates)

[Top][Contents][Prev]: TryMeTemplate-SimpleSchedule.MM[Next]: TryMeDllCustomAction.MM


MAKEMSI© is (C)opyright Dennis Bareis 2003-2008 (All rights reserved).
Saturday May 28 2022 at 3:11pm
Visit MAKEMSI's Home Page
Microsoft awarded me an MVP (Most Valuable Professional award) in 2004, 2005, 2006, 2007, 2008 & 2009 for the Windows SDK (Windows Installer) area.