cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 
kirants
Level 5

Replace a single file embedded in an MSI

First off, I would like to say I am no installer guy. Only now am I learning a little about MSIs, tables etc.. so please bear with me if I sound too newbish.

I am faced with a situation like this:
There is an xml file that is to be installed by one of the products am working on. This XML file is not fixed in the sense that, it has to be generated fresh for each customer. As such, I already have the ability to use InstallShield standalone component and crank out a new installer using the new xml file each time. However, this would need me to maintain all the input files, merge modules , prqs needed by the installer ( times the no. of version ofs the product ). I would like to simplify this so that the installer would be generated with a dummy xml file during build time, but then on demand, this installer will be massaged and injected with the new xml file.

With that background, I did some research on various tools and this is what I got done so far:

  • Managed to use msidb to extract the data1.cab using:
    msidb.exe" -d test.msi -x Data1.cab
  • Managed to use cabarc to extract the data1.cab contents on to a local folder using:
    cabarc X Data1.cab *.* c:\tmp\files\
  • Managed to replace the xml file in the files folder
  • Managed to repack the cab file using:
    cabarc N Data1.cab c:\tmp\files\*.*
  • Managed to inject it back into the msi using:
    rem remove stream first
    msidb.exe" -d test.msi -k Data1.cab
    rem now add it
    msidb.exe" -d test.msi -a Data1.cab


I see that the msi is created and when I use orca to open it, I see the media table having the #data1.cab entry. However, when I run the msi, it comes up with an error that a file is not found and aborts installation.

I have seen quite a few posts with a similar situation, but haven't really seen a solution that works. Some recommend using transforms, some recommend pulling that xml file out of the CAb and use from elsewhere, so it could be updated without repacking the CAb etc..

Any help would be appreciated. Am willing to try what I can. Have most of the tools like CAB SDK, Installer SDK, Wi*.vbs files, orca.
Labels (1)
0 Kudos
(11) Replies
MichaelU
Level 12 Flexeran
Level 12 Flexeran

This approach is going to be full of difficult steps like you've seen. The failure is probably due to the order or keys of the files in the CAB changing, as MSI cares about this. I'd suggest one of two things. If the XML file is similar enough in all scenarios that it only needs a small piece of it updated (say an attribute's value), it may be worth exploring using the XML Changes view to update a base file to have what you need by updating properties, or even transforming the XML changes with a transform.

If the differences aren't so simple and you truly do need a separate file, perhaps you could add your dummy file as a Support File, then restream the updated one into its entry in the ISSetupFile table as your only post-build step. You'd have to update any logic in your installer to actually copy it into place (and remove it at uninstall) if you go with this route, but it'll make the post-build part much more approachable.

As an aside, when you're making these surgical updates post-build, don't forget to take care to update the Package Code (of the summary information stream), and possibly the ProductCode and/or UpgradeCode properties as appropriate.
0 Kudos
kirants
Level 5

ok. I figured how to do this:

Here is what I am doing in case it helps someone:
It's a batch file using the baseline from here.


@ECHO OFF
@ECHO.
@ECHO Updating MSI file with new files
@ECHO.

@ECHO Remove target output file
rmdir /S /Q dump

@ECHO unzip MSI
msiexec /a Input.msi TARGETDIR="c:\tmp\TestPackaging\dump" /quiet

@ECHO press any key when installation is done
rem pause

@ECHO replace xml file
copy .\some.xml ".\dump\CommonAppData\{Company Name}\{Product Name}\some.xml"

@ECHO Update MSI information from source tree
CScript //nologo WiFilVer.vbs .\dump\Input.msi
CScript //nologo WiFilVer.vbs .\dump\Input.msi /U

@ECHO Copy original file to new output file
COPY /Y ".\dump\Input.msi" ".\dump\Output.msi"

REM @ECHO Create a single cabinet file with all your files
REM CScript //nologo WiMakCab.vbs ".\dump\Output.msi" Data1 /C

@ECHO Create and embed the file cabinet
CScript //nologo WiMakCab.vbs ".\dump\Output.msi" Data1 /C /U /E /S

DEL /Q "Data1.CAB"
DEL /Q "Data1.INF"
DEL /Q "Data1.RPT"
DEL /Q "Data1.DDF"

@ECHO Done
PAUSE


This works fine. Question I have is with the first step i.e. admin install using msiexec /a.

Is this adminstrative install gauranteed to install all the files ? Reason being, there some optional command lines I can use to enable/disable certain features. Is it safe to use the msiexec /a ?
0 Kudos
kirants
Level 5

MichaelU, thanks so much for the reply.

I figured how to do this, although I have a question towards the end. Also not sure if your concerns are still valid with the approach I have listed below ( especially the product code , package and upgrade codes ). Would appreciate your insight into this.

Here is what I am doing in case it helps someone:
It's a batch file using the baseline from here.


@ECHO OFF
@ECHO.
@ECHO Updating MSI file with new files
@ECHO.

@ECHO Remove target output file
rmdir /S /Q dump

@ECHO unzip MSI
msiexec /a Input.msi TARGETDIR="c:\tmp\TestPackaging\dump" /quiet

@ECHO press any key when installation is done
rem pause

@ECHO replace xml file
copy .\some.xml ".\dump\CommonAppData\{Company Name}\{Product Name}\some.xml"

@ECHO Update MSI information from source tree
CScript //nologo WiFilVer.vbs .\dump\Input.msi
CScript //nologo WiFilVer.vbs .\dump\Input.msi /U

@ECHO Copy original file to new output file
COPY /Y ".\dump\Input.msi" ".\dump\Output.msi"

REM @ECHO Create a single cabinet file with all your files
REM CScript //nologo WiMakCab.vbs ".\dump\Output.msi" Data1 /C

@ECHO Create and embed the file cabinet
CScript //nologo WiMakCab.vbs ".\dump\Output.msi" Data1 /C /U /E /S

DEL /Q "Data1.CAB"
DEL /Q "Data1.INF"
DEL /Q "Data1.RPT"
DEL /Q "Data1.DDF"

@ECHO Done
PAUSE


This works fine. Question I have is with the first step i.e. admin install using msiexec /a.

Is this adminstrative install gauranteed to install all the files ? Reason being, there some optional command lines I can use to enable/disable certain features. Is it safe to use the msiexec /a ?
0 Kudos
kirants
Level 5

ok. I figured how to do this, although I have a question towards the end

Here is what I am doing in case it helps someone:
It's a batch file using the baseline from here.


@ECHO OFF
@ECHO.
@ECHO Updating MSI file with new files
@ECHO.

@ECHO Remove target output file
rmdir /S /Q dump

@ECHO unzip MSI
msiexec /a Input.msi TARGETDIR="c:\tmp\TestPackaging\dump" /quiet

@ECHO press any key when installation is done
rem pause

@ECHO replace xml file
copy .\some.xml ".\dump\CommonAppData\{Company Name}\{Product Name}\some.xml"

@ECHO Update MSI information from source tree
CScript //nologo WiFilVer.vbs .\dump\Input.msi
CScript //nologo WiFilVer.vbs .\dump\Input.msi /U

@ECHO Copy original file to new output file
COPY /Y ".\dump\Input.msi" ".\dump\Output.msi"

REM @ECHO Create a single cabinet file with all your files
REM CScript //nologo WiMakCab.vbs ".\dump\Output.msi" Data1 /C

@ECHO Create and embed the file cabinet
CScript //nologo WiMakCab.vbs ".\dump\Output.msi" Data1 /C /U /E /S

DEL /Q "Data1.CAB"
DEL /Q "Data1.INF"
DEL /Q "Data1.RPT"
DEL /Q "Data1.DDF"

@ECHO Done
PAUSE


This works fine. Question I have is with the first step i.e. admin install using msiexec /a.

Is this adminstrative install gauranteed to install all the files ? Reason being, there some optional command lines I can use to enable/disable certain features. Is it safe to use the msiexec /a ?
0 Kudos
MichaelU
Level 12 Flexeran
Level 12 Flexeran

Administrative installs should create a full image of the contents of the MSI; feature filtering happens when that image is run to install it on a target machine. As for the package and product code question, it's a rule that can be broken, but only carefully, when you know 100% that the slightly different packages will never be installed on the same computer, and probably other caveats I'm not thinking of right now. So don't do it lightly. Take a look at the windows installer help topics on this, including Package Codes.
0 Kudos
kirants
Level 5

Thanks for the heads up. Appreciate it. I will look into the link you pointed out. Skimming through it, it seems that what I am doing is messy.

I am starting to like the idea of using an mst. Some questions I have:
1. Can the mst file generation ( which references the new xml file ) be automated/scripted ? i.e. I need to crank this all up without using an IDE.
2. Also, I would like to have a single installer EXE. Can the mst file be embedded into the EXE ? Again, if yes, can this step of embedding the mst into the EXE be automated/scripted ( using IsCmdBld / vbscripting or other utils ? )
0 Kudos
MichaelU
Level 12 Flexeran
Level 12 Flexeran

It's certainly possible to script the creation of transforms. For the complexity you're likely to be dealing with, it may be reasonable to use Windows Installer automation, but it should also be possible to use InstallShield's automation layer. I don't think it would be feasible to do it with command lines alone.

As for packing it back up in a single EXE, we don't offer any way to do this after the build (except pure InstallScript projects with the ReleasePackager exe), sorry.
0 Kudos
kirants
Level 5

MichaelU wrote:
As for packing it back up in a single EXE, we don't offer any way to do this after the build (except pure InstallScript projects with the ReleasePackager exe), sorry.

Thanks for the reply ! I need to have an EXE installer since I have PRQ dependencies. So ,that practically kills all possibilities of being able to do this even if I could get the xml into the MSI using a transform or otherwise 😞

Thanks again for your help. I bumped into another thread where a similar scenario was presented where the OP had all the files needed for bundling but that last step wasn't possible since there wasn't a tool that could do this. Is this something that is planned for a future release ?
0 Kudos
kirants
Level 5

I have been thinking about the XML Changes view. I have managed to manipulate the XML file contents with a value of a property. Now, all I have to do is change the property value. I again managed to change the property at runtime by configuring the CmdLine key in the setup.exe setup.ini section ( using the IDE ).

I could now theoretically do the same using setupini.exe isn't it ? Is there a setupini.exe for IS 2009 ? I tried the one for IS 10.5 and although it said it successfully modified the setup.exe, the setup.exe now fails to run ( "error reading setup initialization file" generated when I run the exe ). Any clues ?
0 Kudos
kirants
Level 5

Just a quick update on this. I got this thing to work using a internal_setupini.exe available in the Attachments section ( the link in the downloads section is broken ).

http://kb.acresso.com/selfservice/microsites/search.do?cmd=displayKC&docType=kc&externalId=Q107253
0 Kudos
kirants
Level 5

This issue is resolved. Just so someone in future might need something like this, here is what I did:

Goal: Each installation needed to have a just a different values in a predefined XML file that is installed. So, was looking for a way to do this post installer generation rather than having to build the installer all the way each time.

solution:Gist was to use XML File changes view to modify the installed XML using user-defined property values. The property values would in turn be set by a custom action which would be written to read from a user-defined section in the setup.ini file embedded in the setup.exe. The user defined section in the setup.ini would be inserted by the setupini.exe tool.

These are the steps to update a key value of say customdata in an XML file called , say CustomData.xml:

  • Let us assume the CustomData.xml is already added to the file and folders view.
  • Go to Property Manager and add a new property called, say, CUSTOMDATA
  • Go to XML File Changes view and on the RHS, right click and select import and point to the CustomData.xml. This should generate the XML file structure.
  • Here in , select the file name and on the general tab , make sure the XML file destination is set right.
  • Next, select the node whose data you want modify at installation time. Go to the Advanced tab in the RHS and int the Content edit field, enter [CUSTOMDATA] ( the square brackets indicating it is a property ). Save project.
  • Go to InstallScript section and if there is not a file, add add new script file from right mouse menu and add a function like below:

    #include "ifx.h"

    export prototype UpdateCustomData(HMSI);

    #define INI_NAME "setup.ini"

    // The section, key, and value to update the file.

    #define SECTION_NAME "CustomDataSection"
    #define KEY_NAME "CustomData"

    #define KEY_PROPERTY_NAME "CUSTOMDATA"

    function UpdateCustomData(hMSI)
    // To Do: Declare local variables.
    NUMBER nResult, nvResult1,nvResult2;
    STRING svMatchingFileName,svResult1,svResult2,svFileName,svPath;
    STRING svValue;
    begin

    nResult = FindAllFiles (FOLDER_TEMP, "*.MSI", svMatchingFileName, RESET);
    while(nResult = 0)
    //extract file name
    ParsePath ( svFileName, svMatchingFileName,FILENAME);
    ParsePath ( svPath, svMatchingFileName,PATH);

    if(GetFileInfo(svMatchingFileName,FILE_SIZE,nvResult1,svResult1) = 0) then
    if(GetFileInfo(SRCDIR ^ svFileName,FILE_SIZE,nvResult2,svResult2) = 0) then
    //compare
    if(StrCompare(svResult1,svResult2) = 0 ) then
    // To Do: Write script that will be executed when UpdateRegistrationInformation is called.
    if (GetProfString (svPath ^ INI_NAME, SECTION_NAME, KEY_NAME, svValue) == 0) then
    // Display the key and its current value.
    MsiSetProperty(hMSI,KEY_PROPERTY_NAME,svValue);
    endif;

    nResult = -1;
    endif;
    endif;
    endif;
    if(nResult = 0) then
    nResult = FindAllFiles (FOLDER_TEMP, "*.MSI", svMatchingFileName, CONTINUE);
    endif;
    endwhile;
    end;

    What this does is, it finds the setup.ini file and reads the section in it , gets the value and updates the MSI's property.
  • Now, go to custom actions and sequences view, right clikck on Custom Actions on the RHS, select New Install Script and enter a name. In the common tab on the RHS, select the UpdateCustomData from the dropdown. Select an appropriate sequence ( for e.g. Install UI Sequence set to After SetupInitialization ).
  • Those steps above should create an installer that is capable of reading from ini file and updating the xml file during installation.
  • The last step that needs to be done after the setup.exe is ready is massaging the custom data in the setup.exe which can be done through command line as:
    SetupIni.exe setup.exe CustomDataSection CustomData "my own custom data"


Hope this helps someone.. Thanks to MichaelU for all the tips provided.
0 Kudos