Getting the Current Windows Username and Using it with InstallShield
Using application objects in ZENworks 7 made it easy to use the current windows users name by putting either the %CN% or %USERNAME% into the registry or ini files as required.
To get a similar result with InstallShield and MSI installs requires some extra work but is still relatively simple.
First thing is that an InstallShield MSI already contains a variable called USERNAME, so that’s easy right, just put [USERNAME] into the setup where needed and the jobs a good one.
InstallShield reads that variable as the username that registered windows when it was first installed – see system properties/General/Registered. This is not generally what we want.
The following steps will set up the app so that it uses the current username in the USERNAME variable.
- Open the ISM file (not the MSI) with installshield.
If you haven’t got the ISM file go to the file menu select open and navigate to the MSI file. Then from the open as drop down list select wizard. Go to next and select the option to convert the file to an ISM.
This procedure has to be done with an ISM file as we need installscript and this is not available when editing an MSI.
- Select the Installation Designer tab, navigate to the Behaviour and Logic section and select installscript.
- Right click Files and Select New Script File.
This will generate setup.rul and MyFunction
- Go to the bottom of the MyFunction section and enter the following code.
#define TITLE "GetUsername" #define ENV_USER "USERNAME" // Include Ifx.h for built-in InstallScript function prototypes. #include "Ifx.h" export prototype ExFn_GetEnvVar(HWND); function MyFunction(hMSI) // To Do: Declare local variables. STRING svEnvVar; begin GetEnvVar (ENV_USER, svEnvVar); MsiSetProperty(hMSI, "USERNAME", svEnvVar); end;
Just delete or overwrite the skeleton code that’s automatically produced.
For anyone who’s interested the code breaks down as follows
#define TITLE "GetUsername"
“This is not actually needed, I use it during testing, I leave it as a quick reference (I know that’s bad but I’m a sysadmin not a programmer).”
#define ENV_USER "USERNAME"
“This is defines a placeholder for the Windows Environment Variable we want. In this case username – Yes this will work with any Windows Environment Variable.”
“Include Ifx.h for built-in InstallScript function prototypes.”
export prototype ExFn_GetEnvVar(HWND);
“The function is external so it needs prototyping (I think that’s right. remember I’m SysAdmin, if you need to know why ask a programmer :)). This line created the other function”
“The function header”
“This declares a local variable.”
“This starts the function”
GetEnvVar (ENV_USER, svEnvVar);
“This calls the installshield function GetEnvVar, which gets the Windows Environment variable we want (ENV_USER) and stores it in svEnvVar.”
MsiSetProperty(hMSI, "USERNAME", svEnvVar);
“This calls the installshield function MsiSetProperty, which then stores the svEnvVar data in the INTERNAL installshield USERNAME variable, overwriting the current one.”
“end of function.”
- Now, still under the Behaviour and Logic Section, Select Custom Actions and Sequences. Right Click Custom Actions and select the custom action wizard
- Select next on the wizard welcome screen.
Enter a name on the Basic Information screen and select next.
I’ve entered GetUserName .
Select Run Installscript code from the type dropdown box and select next.
Select the Function from the Source dropdown box, in this case MyFunction, and select next.
Leave the Processing as synchronous and select next
Leave both the execution options on the defaults and select next.
Leave the UI sequence on default and set the Execute Sequence to After Isolate Components. We need this function to run early to ensure that the correct information is in the USERNAME variable. Select Next.
Select Finish and the wizard is done.
The Custom Actions should now look like this.
The variable can now be used and will act as expected, by putting the users name where required.
- An example registry edit, using USERNAME. The variable needs to be enclosed in square brackets when used so it’s [USERNAME].
- The application then needs to be built as an MSI. If there is already an MSI in the directory it is advisable to copy it elsewhere to ensure there is an original copy to fall back on if needed.
First compile the code for the function that was created, using the toolbar button
If there are any errors then you’ll need to check the code is correct.
Then the MSI can be built using this toolbar button
- One final gotcha is that you can’t use this method to rename files. If you try to put a filename in using a variable for all or part of then it will just use that for the file name.
e.g. [SomeVariable].exe within the MSI will be copied to windows as [SomeVariable].exe and not renamed (I hope that makes sense).
If you need to have that functionality there is a RenameFile variable. Which could be used something like this
#define FILENAME1 "SourceFile.exe" #define FILENAME2 ".exe" #define SOURCE_DIR "C:\\SomeDirectory" #define TARGET_DIR "C:\\SomeDirectory" #include "ifx.h" #include "isrt.h" #include "iswi.h" function MyFunction(hMSI) // Declare local variables. STRING svName; NUMBER nvSize; begin // Retrieve the Variable from the MSI database nvSize = 256; MsiGetProperty (hMSI, "USERNAME", svName, nvSize); // Set up system variables for rename operation. SRCDIR = SOURCE_DIR; TARGETDIR = SOURCE_DIR; // Rename FILENAME1 to FILENAME2. RenameFile (FILENAME1, svName + FILENAME2); end;
This example uses the USERNAME variable, but it will work for any variable within the MSI database.
FILENAME1 is the name of the file that wants renaming.
FILENAME2 contains the name or PART of the name that the file needs reaming too.
SOURCE_DIR and TARGET_DIR contain the directory where the file resides. Note the use of \\ in the path, this is required between directories a single \ doesn’t work.
Remember to use a different name for the function if MyFunction already exists.
MsiGetProperty retrieves the variable from the internal MSI database (in this case USERNAME) and stores the value in the svName variable within the function.
SRCDIR and TARGETDIR are set to the directory paths defined earlier.
RenameFile goes to SRCDIR and locates FILENAME1, it then renames it to svName concatenated to FILENAME2 and saves it back to TARGETDIR.
This function manipulates files on the actual windows system, not within the MSI so the files must exist before hand. This means that when setting up a custom action for the function it needs to scheduled towards the end of the installation, after the files have been copied.
Hope all this is useful and maybe eases the migration to ZCM for someone.