4

I am making a custom installer. Where I added Utility.CA.dll to perform my custom action. In this case I want to access local file with relative to setup.msi file path. The custom action method can use the direct path e:\utility\myfile.txt but I can not find the path '..\utility\myfile.txt'. After some experiment I got that Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location) Shows C:\Users\current_username\AppData\Local\Temp\MSIF384.tmp- folder.

What can I do? need wix code example.

PhilDW
  • 20,260
  • 1
  • 18
  • 28
pcbabu
  • 2,219
  • 4
  • 22
  • 32

4 Answers4

3

First, I should mention it is very challenging to reference a file relative to the source of a .MSI because later (repair, patch, uninstall) the .MSI file will be executed out of the installer cache and the loose files (like myfile.txt) will not be available. You need to write your custom action very carefully to handle that fact.

What you are looking for is the Directory with identifier SourceDir. You can get the value of SourceDir by calling:

 string sourceDir = session["SourceDir"];

Note: I'm assuming you're using DTF where the session object is provided to your managed custom action.

Now, the complexity is that the SourceDir property is only set when the Windows Installer has done ResolveSource. On initial install, where the .MSI file is double-clicked the source will be resolved (because it is the initial install and will need files). Subsequent installs may not need the source (e.g. uninstall should not need you to put the CD back in the drive to succeed). Therefore, you'll either have to call ResolveSource action in your .MSI install sequence (which would prompt the user to provide the original .MSI file again) or write the custom action code such that it does not require SourceDir in all cases.

You can read up a little more about SourceDir here: http://robmensching.com/blog/posts/2010/1/26/stackoverflow-what-does-namesourcedir-refer-to

Rob Mensching
  • 33,834
  • 5
  • 90
  • 130
  • Can you give any good idea for resolving the source. I have searched all my PC folders with desired folder structure and with a lot of effort for searching full directory list of computer.If I got same folder structure I have a problem I will choose by last access time of main msi installer file. Here the problem arises. Windows 7 does not always update the Last Access time and the function GetFiles() changes the folder LaslAccessTime. I have kept 2-3 same kinda folder with my structure and this logic doesn't work... Please provide any good idea.. – pcbabu Apr 04 '13 at 14:11
  • 1
    I don't understand your question. `ResolveSource` is an action in MSI that will cause the Windows Installer to search for the original .msi file. If it can't find it where it left it (or in additionally registered sources) the Windows Installer will prompt for the path to the .msi file. That prompt is not a good user experience, especially on uninstall. – Rob Mensching Apr 04 '13 at 17:48
1

This works for me; in Product.wxs:

<Binary 
    Id="WixMyCustomActions"         
    SourceFile="..\WixMyCustomActions\bin\WixMyCustomActions.CA.dll" />

<CustomAction 
    Id="MyMethod" 
    BinaryKey="WixMyCustomActions" 
    DllEntry="MyMethod" 
    Execute="immediate" 
    Return="check" />

WixMyCustomActions.CA.dll is a C# class library in the same solution as the Wix project. In the WixMyCustomActions.CA.dll project properties, Build Events, I have a post build event to copy the WixMyCustomActions.CA.dll and WixMyCustomActions.CA.pdb from bin\Debug or bin\Release to bin:

copy "$(TargetDir)*.dll" "$(ProjectDir)bin" /Y
copy "$(TargetDir)*.pdb" "$(ProjectDir)bin" /Y

By copying the dll, my Product.wxs will reference whichever configuration (Debug or Release) was built last.

Edit: to get a file relative to your CA dll, use this to find the directory of the CA assembly:

using System.IO;
using System.Reflection;

// etc

string assemblyDirectory = 
    Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);

You can now find files relative to this directory.

Polyfun
  • 9,479
  • 4
  • 31
  • 39
  • Its ok but how can i read `..\utility\myfile.txt` file from `MyMethod` method? – pcbabu Apr 03 '13 at 10:57
  • Current directory of an installer is `"C:\\Users\\user\\AppData\\Local\\Temp\\MSI2C71.tmp-` which is assigned to `assemblyDirectory` variable... When a installer runs `Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location)` changed to `%temp%` but I need to determine the location where I clicked the msi file. – pcbabu Apr 03 '13 at 11:30
1

If you need to get the path to the folder in which the msi file resides, you can use this snippet to retrieve it:

Path.GetDirectoryName(session["OriginalDatabase"])

The OriginalDatabase property can be used in the InstallUISequence and the InstallExecuteSequence.

To access a file relative to your msi you would use

Path.Combine(Path.GetDirectoryName(session["OriginalDatabase"]), "myfile.txt")
BdN3504
  • 1,693
  • 21
  • 29
  • No matter where I put my custom action in the InstallExecuteSequence (I tried After='InstallInitialize' and Before='InstallFinalize'), session does not contain a key called "OriginalDatabase") – Gyum Fox Feb 29 '16 at 16:34
  • OK what I did to have this session variable available was to add this to the InstallExecuteSequence: <![CDATA[NOT Installed]]> – Gyum Fox Feb 29 '16 at 16:59
0

There is a property that can be read from MSI - SourceDir.

You can refer this property in your c# code by string sourceDir = session["SourceDir"];

However, you will have to resolve the source before trying to get the source. That is your MSI does not know where it is being run from. So add the following standard action in your InstallExecuteSequence.

<ResolveSource After="CostInitialize"/>

Note that, you should put this before CostFinalize and after CostInitialize, otherwise it will give error ICE27: 'ResolveSource' Action in InstallExecuteSequence table in wrong place. Current: Selection, Correct: Costing.

Your custom action should be referred after the ResolveSource element.

<InstallExecuteSequence>
      <ResolveSource After="CostInitialize"/>
      <Custom Action="CustomActionThatNeedsRelativePath" After="CostFinalize"/>
</InstallExecuteSequence>
Jaydeep Ranipa
  • 421
  • 5
  • 16