可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
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.
回答1:
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
回答2:
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")
回答3:
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.
回答4:
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>