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]: Error Dialog - Improve[Next]: Licence Dialog Enabling
Have your say! Join the MAKEMSI discussion list or view archive! Suggest improvements. No question too simple or too complex.
\->Tips and Tricks->User Interface Tips->FilesInUse Dialog - Display if Program Running

FilesInUse Dialog - Display if Program Running

This is a User Interface Tips example to demonstrate how you can use a custom action to detect running programs and add these to any "FilesInUse" dialog.

Note that I don't try to terminate the programs although WMI is perfectly capable of doing this as we have no idea of the impact of doing so, for example the user may be editing a critical document which will be lost if the task is simply killed.

I have broken this example into two components, a reusable macro which can be used in other scripts and the code which invokes this macro for this specific script.

Smart people will NEVER duplicate effort, having the logic in one central place minimises testing and effort if (or much more likely "when") an improvement or bug fix is required.

EXAMPLE - Script Code

This code uses a "#data" structure to supply the information about the programs which we wish to look for and then passes this to the macro (shown below).

#data "Need2Stop" 2
   ;---1. Pgm Name     2.User Friendly Name --------------
       "Excel.EXE"     "Microsoft Excel"
       "NotePad.EXE"   "The notepad editor"
#data
<$ShowFilesInUseForExecutingPrograms "Need2Stop">

EXAMPLE - Reusable Code in Macro

This is some reusable code which should be placed into one of your headers so that it is never repeated. This ensures that you can make all improvements or fixes in the one central location. As all your MSIs reuse the same code the total testing required is reduced.

The "IsProgramRunning()" function uses WMI, you may need to rewrite this function for more general use (its OK as is if it is deployed to a managed environment where WMI is expected to exist). There are a number of suitable command line tools which will report running tasks, you might also be able to detect the Window by title with other techniques.

If WMI is not available the script will not successfully complete.

Please note that this example doesn't do anything if the install is silent, it should perhaps invoke the "ForceReboot" action and use the "AFTERREBOOT" property (let me know if you make this changes - thanks). I am trying to get information on exactly how Windows Installer handles files that are in use. This is another example of why it is a good idea to centralise code, it allows you to do the best possible job in the time available and as time or information becomes available it is simple to update.

#(     '<?NewLine>'
       ;--- You may wish to replace contents of macro with your own code ----
       #define? VBSFunction.IsProgramRunning

       '============================================
       function IsProgramRunning(ProgramName, UserFriendlyName)
       '
       ' This function encapsulates the code required to test
       ' whether a program is executing.
       ' Note that WMI is used, this may not work on all
       ' operating systems or boxes (its probably best used
       ' in a managed environment).
       ' Replace the logic in this function if required.
       '============================================
           ;--- Log entry ---------------------------------------------------
           CaDebug 0, "IsProgramRunning() : " & ProgramName
           VbsCaLogInc 1
           CaDebug 2, "Checking: " & UserFriendlyName

           ;--- Run the query -----------------------------------------------
           dim oWMI    : set oWMI = GetObject("winmgmts:")
           dim Query   : Query    = "select * from Win32_Process where name='" & ProgramName & "'"
           CaDebug 0, "Executing QUERY: " & Query
           dim oResult : set oResult = oWmi.Execquery(Query)
           CaDebug 0, "Found " & oResult.Count & " programs running."

           ;--- Set return code and log exit message ------------------------
           If oResult.Count > 0 Then
              IsProgramRunning = true
              CaDebug 0, "Program """ & ProgramName & """ is running"
           else
              IsProgramRunning = false
              CaDebug 0, "Program """ & ProgramName & """ is NOT running"
           end if
           VbsCaLogInc -1
       end function
#)
#(  '<?NewLine>'
    ;--- Define a reusable macro --------------------------------------------
    #define ShowFilesInUseForExecutingPrograms

    ;--- Do some parameter validations --------------------------------------
    {$!KEYWORDS}                       ;;Don't Expect any keywords!
    {$!:#1,SEQ,SEQTABLE,CONDITION}     ;;List all valid parameters

    ;--- Build the Custom Action --------------------------------------------
    <$VbsCa Binary="PopulateFilesInUseDialog.vbs">
       ;--- Define global variables -----------------------------------------
       dim ExitRc   : ExitRc   = 0
       dim InUseCnt : InUseCnt = 0
       dim ProgramNames(), FriendlyNames()

       ;--- Entry Point -----------------------------------------------------
       <$VbsCaEntry "Install">
           ;--- Update progress dialogs message -----------------------------
           CaDebug 1, "Looking for executing programs and need to be stopped..."

           ;--- Perform the checks specified by the #data structure ---------
           #{ FOR @@X = 1 to <?Data:{$#1}.?>
              IfProgramExecutingThenAddToList "<?Data:{$#1}.@@X.1>", "<?Data:{$#1}.@@X.2>"
           #}

           ;--- Now Do whatever is required ---------------------------------
           <$VbsCaEntryName> = HandleRunningPrograms()
       <$/VbsCaEntry>


       <?NewLine><?NewLine>
       '============================================
       function HandleRunningPrograms()
       '
       ' Processes the list of running programs we have created.
       ' At the moment it simply displays the file in use dialog,
       ' in future it may do more (need to do anything for silent install?).
       '============================================
           ;--- Need to do anything? ----------------------------------------
           HandleRunningPrograms = 0
           if   InUseCnt = 0 then
                exit function
           end if

           ;--- Log entry ---------------------------------------------------
           CaDebug 0, "HandleRunningPrograms() : We found " & InUseCnt & " programs that need to be stopped."
           VbsCaLogInc 1

           ;--- For now only handle UI --------------------------------------
           HandleRunningPrograms = DisplayFilesInUseDialog()

           ;--- Log Exit ----------------------------------------------------
           CaDebug 0, "Finished HandleRunningPrograms()"
           VbsCaLogInc -1
       end function

       <?NewLine><?NewLine>
       '============================================
       sub IfProgramExecutingThenAddToList(ProgramName, UserFriendlyName)
       '
       ' As its name implies checks to see if a program is running,
       ' if it is it is added to a list.
       '============================================
           ;--- Log entry ---------------------------------------------------
           CaDebug 0, "IfProgramExecutingThenAddToList() : " & ProgramName
           VbsCaLogInc 1

           ;--- Is it running? ----------------------------------------------
           if   IsProgramRunning(ProgramName, UserFriendlyName) then
                redim preserve ProgramNames(InUseCnt)
                redim preserve FriendlyNames(InUseCnt)
                ProgramNames(InUseCnt)  = ProgramName
                FriendlyNames(InUseCnt) = UserFriendlyName
                InUseCnt                = InUseCnt + 1
           end if

           ;--- Log Exit ----------------------------------------------------
           CaDebug 0, "IfProgramExecutingThenAddToList()"
           VbsCaLogInc -1
       end sub

       <?NewLine><?NewLine>
       '============================================
       function DisplayFilesInUseDialog()
       '
       ' This must only be called sometime after "PrepareDlg",
       ' it (Windows Installer helps here again...) will hang if
       ' called at the wrong time (needs improving - loop limit?).
       '============================================
           ;--- Log entry ---------------------------------------------------
           CaDebug 0, "DisplayFilesInUseDialog()"
           VbsCaLogInc 1
           DisplayFilesInUseDialog = 0

           ;--- Prepare Basic UI message ------------------------------------
           dim BasicUiMsg : BasicUiMsg = "The following programs need to be stopped:" & vbCRLF
           dim i
           for i = 0 to InUseCnt-1
               BasicUiMsg = BasicUiMsg & vbCRLF & FriendlyNames(i)
           next

           ;--- Prepare Record ----------------------------------------------
           CaDebug 0, "Preparing record for " & InUseCnt & " programs."
           dim oMsgRec : set oMsgRec = Installer.CreateRecord(1 + (InUseCnt*2))
           oMsgRec.StringData(0) = BasicUiMsg
           for i = 0 to InUseCnt-1
               oMsgRec.StringData(i*2 + 1) = ProgramNames(i)
               oMsgRec.StringData(i*2 + 2) = FriendlyNames(i)
           next

           ;--- Display "FilesInUse" dialog if program running --------------
           CaDebug 0, "Displaying FilesInUse dialog for " & InUseCnt & " programs."
           dim DialogResponse
           do
              ;--- Display the dialog, capture the return code/response -----
              DialogResponse = session.message(msiMessageTypeFilesInUse, oMsgRec)
              VbsCaLogInc 1
                 CaDebug 0, "FileInUse DIALOG RC = " & DialogResponse
              VbsCaLogInc -1

              ;--- User wants to abort? -------------------------------------
              if  DialogResponse = msiMessageStatusCancel then
                  DisplayFilesInUseDialog = ERROR_INSTALL_USEREXIT
                  CaDebug 0, "User wants to cancel the install!"
                  exit do
              end if

              ;--- Exit if we want to ignore ------------------------------------
              if  DialogResponse = msiMessageStatusIgnore or DialogResponse = msiMessageStatusOK then
                  CaDebug 0, "User wants to ignore this issue!"  ;;If you don't want remove/disable dialog button
                  exit do
              end if
           loop

           ;--- Log Exit --------------------------------------------------------
           CaDebug 0, "Finished DisplayFilesInUseDialog() : RC = " & DisplayFilesInUseDialog
           VbsCaLogInc -1
       end function

       ;--- Function defined above ------------------------------------------
       <?NewLine><?NewLine>
       <$VBSFunction.IsProgramRunning>
    <$/VbsCa>

    ;--- Schedule somewhere after "PrepareDlg" (or dialog won't display) ----
    <$VbsCaSetup Binary="PopulateFilesInUseDialog.vbs" Entry="Install" Type="IMMEDIATE" Seq="{$Seq=^MigrateFeatureStates-^}" SeqTable="{$SeqTable=^InstallUISequence^}" CONDITION=^{$Condition=~<$CONDITION_EXCEPT_UNINSTALL>~}^>
#)


Microsoft awarded me an MVP (Most Valuable Professional award) in 2004, 2005, 2006 & 2007 for the Windows SDK (Windows Installer) area.This external link was OK when tested at 23 Aug 2008Please email me any feedback, additional information or corrections.
See this page online (look for updates)

[Top][Contents][Prev]: Error Dialog - Improve[Next]: Licence Dialog Enabling


MAKEMSI© is (C)opyright Dennis Bareis 2003-2008 (All rights reserved).
Saturday August 30 2008 at 9:06am
Visit MAKEMSI's Home PageThis external link was OK when tested at 28 Aug 2008

HTML page dated Mon, 29 Jan 2007 00:11:11 GMT
Microsoft awarded me an MVP (Most Valuable Professional award) in 2004, 2005, 2006 & 2007 for the Windows SDK (Windows Installer) area.This external link was OK when tested at 23 Aug 2008