I have created an msi setup file which includes some files in a "Sample" folder which should be copied to a temp folder. Anybody suggest how to do this?
Something like this:
<Directory Id="TARGETDIR" Name="SourceDir">
<Directory Id="ProgramFilesFolder">
<Directory Id="MyVendor" Name="MyVendor">
<Directory Id="INSTALLDIR" Name="MyDir">
<Component Id="MyFileId" Guid="...G1...">
<File Id="MyFileId" Name="MyFile" Source="...blabla...\MyFile" KeyPath="yes" >
</File>
</Component>
<DirectoryRef Id="TARGETDIR">
<Component Id="MyFileCopyId" Guid="...G2...">
<RemoveFile Id="MyFileRemoveId" Name="MyFile" On="install" Directory="MyCopyDir" />
<CopyFile Id="MyFileCopyId" FileId="MyFileId" DestinationDirectory="MyCopyDir" />
</Component>
<Feature Id="MyFeature" ... >
<ComponentRef Id="MyFileId" />
<ComponentRef Id="MyFileCopyId" />
The important Xml element is CopyFile. You need to create a new component that is a copy of the first one (with different ids, guids, ... of course). Both components needs to be declared in a feature.
CopyFile element is your friend. You can nest it under the original File element a number of times, depending on how many times you need to copy it. Put the correct destination folder(s) and let Windows Installer do the rest.
Related
I want to do the following where XLSTART is defined as:
<CustomAction Id="AssignXLSTART" Return="check" Execute="firstSequence" Directory ='XLSTART' Value='[AppDataFolder]\Microsoft\Excel\XLSTART'>
</CustomAction>
And then I have a subsequent CustomAction that calls some C# code that may change this value.
And then in the list of files to install I have:
<Directory Id="XlStartFolderId" Name="[XLSTART]">
<Component Id="ExcelMacro_xla" Guid="26D21093-B617-4fb8-A5E7-016493D46055" DiskId="1">
<File Id="ExcelXLA" Name="AutoTagExcelMacro.xlam" ShortName="XLMacro.xla" Source="$(var.srcFolder)\AutoTagExcelMacro.xlam"/>
</Component>
</Directory>
But the above puts it in the INSTALLDIR[XLSTART]. How do I get it to read this as a property?
You should be able to install to the userprofile directory you refer to like this:
<Directory Id="TARGETDIR" Name="SourceDir">
<Directory Id="LocalAppDataFolder">
<Directory Id="Microsoft" Name="Microsoft">
<Directory Id="Excel" Name="Excel">
<Directory Id="XLSTART" Name="XLSTART">
<Component Id="ExcelAddIn" Feature="MyFeature" Guid="{11111-1111-GUID-HERE-YOURGUIDHERE}">
<File Source="C:\SourceFiles\MyAddin.xla" />
<RemoveFolder Id="Microsoft" On="uninstall" Directory="Microsoft" />
<RemoveFolder Id="Excel" On="uninstall" Directory="Excel" />
<RemoveFolder Id="XLSTART" On="uninstall" Directory="XLSTART" />
<RegistryValue Root="HKCU" Key="Software\MySoftware" Name="installed" Type="integer" Value="1" KeyPath="yes" />
</Component>
</Directory>
</Directory>
</Directory>
</Directory>
I would suggest you use the per-machine xlstart folder instead - if it still exists. I am not sure it does. The the addin is loaded for every user on the box on every launch. Generally I prefer this. It has been ages since I looked at this, so this could have changed in newer Office versions - in fact I am sure it has, but the details are unclear to me.
System Folder Properties: There are a number of System Folder Properties that can be used in MSI files to specify installation location - LocalAppDataFolder is just one of them: https://learn.microsoft.com/en-us/windows/win32/msi/property-reference#system-folder-properties
Figured it out. You need to install to the INSTALLDIR and then use CopyFile
<!-- place it in C:\Program Files (x86)\Microsoft Office\Root\Office16\XLSTART\ -->
<Component Id="ExcelMacro_xla" Guid="26D21093-B617-4fb8-A5E7-016493D46055" DiskId="1">
<File Id="ExcelXLA" Name="AutoTagExcelMacro.xlam" ShortName="XLMacro.xla" Source="$(var.srcFolder)\AutoTagExcelMacro.xlam">
<CopyFile Id='CopyXlMacro' DestinationProperty='XLPATH' DestinationName='AutoTagExcelMacro.xlam'/>
</File>
</Component>
I have created a multi lingual app that uses 2 diferent resource files to manage the UI language, so when I build and execute my program, in my bin directory I have my app files and two folders, en-GB and pt-PT.
I am now trying to create a installer with Wix, for that I am defining the following directories:
<Fragment>
<Directory Id="TARGETDIR" Name="SourceDir">
<Directory Id="ProgramFilesFolder">
<Directory Id="INSTALLFOLDER" Name="App" >
<Directory Id="LOCALEEN" Name="en-GB"/>
<Directory Id="LOCALEPT" Name="pt-PT"/>
</Directory>
</Directory>
</Directory>
</Fragment>
And then, I define the following components:
<Fragment>
<ComponentGroup Id="ProductComponents" Directory="INSTALLFOLDER">
<Component Id="App.resources.en.GB.dll" Guid="...">
<CreateFolder />
<File Id="App.resources.en.GB.dll" Name="App.resources.dll" Source="$(var.App.App_TargetDir)en-GB\App.resources.dll" />
</Component>
<Component Id="App.resources.pt.PT.dll" Guid="...">
<CreateFolder />
<File Id="App.resources.pt.PT.dll" Name="App.resources.dll" Source="$(var.App.App_TargetDir)pt-PT\App.resources.dll" />
</Component>
... Other components...
</ComponentGroup>
</Fragment>
When I rebuild my solution I get the following error:
'App.resources.dll' is installed in '[ProgramFilesFolder]\App\' by two
different components on an LFN system: 'App.resources.en.GB.dll' and
'App.resources.pt.PT.dll'. This breaks component reference counting.
I understand the problem, both resources dll are being copied to the installation folder, and not to the specific resources file... But I don't know how to solve it. Anyone can give any hints on how to solve this?
Just reference the directory where you want your components eg. Directory="LOCALEEN". There is no need to specify <CreateFolder />
I also recomend to maintain some kind of naming convention. Your Components and Fils have the same id. See https://stackoverflow.com/a/1801464/4634044. So this should do what you expect:
<Fragment>
<ComponentGroup Id="ProductComponents" Directory="INSTALLFOLDER">
<Component Id="C_EnglishLocale" Guid="..." Directory="LOCALEEN">
<File Id="Fi_EnglishLocale" Name="App.resources.dll" Source="$(var.App.App_TargetDir)en-GB\App.resources.dll" />
</Component>
<Component Id="C_PolnishLocale" Guid="..." Directory="LOCALEPT">
<File Id="Fi_PolnishLocale" Name="App.resources.dll" Source="$(var.App.App_TargetDir)pt-PT\App.resources.dll" />
</Component>
</ComponentGroup>
</Fragment>
I am trying to create a start menu shortcut for an application using WiX, the problem is that I am receiving an Ice64 error stating that the parent directory of the shortcut directory is not in the RemoveFile table.
I do not want to remove this parent folder (organisation folder) on uninstall as other applications may have shortcuts in other children of it.
My code looks like
<Feature Id="ProductFeature" Title="MyApplication" Level="1">
<ComponentGroupRef Id="Components" />
<ComponentRef Id="ProfilesShortcut"/>
</Feature>
<Fragment>
<Directory Id="TARGETDIR" Name="SourceDir">
<Directory Id="ProgramFilesFolder">
<Directory Id="OrgDir" Name="OrganisationName">
<Directory Id="AppDir" Name="MyApplication" />
</Directory>
</Directory>
<Directory Id="ProgramMenuFolder">
<Directory Id="ProgFilesOrgDir" Name="OrganisationName">
<Directory Id="ProgFilesAppDir" Name="MyApplication" />
</Directory>
</Directory>
</Directory>
</Fragment>
<Fragment>
<DirectoryRef Id="ProgFilesAppDir">
<Component Id="ProfilesShortcut" Guid="*">
<Shortcut Id="ApplicationStartMenuShortcut"
Name="MyApplication"
Description="My Application"
Target="[#MyApplication.exe]"
WorkingDirectory="AppDir"/>
<RemoveFolder Id="ProgFilesAppDir" On="uninstall"/>
<RegistryValue Root="HKCU" Key="Software\Organisation\MyApplication" Name="installed" Type="integer" Value="1" KeyPath="yes"/>
</Component>
</DirectoryRef>
</Fragment>
Feel free to add the remove file element to your shortcut component.
Remove an empty folder if the parent component is selected for installation or removal.
The RemoveFolder element will only remove empty folders so if your product is not the only part of that suite installed it will leave the parent folder alone since other products will have put files/folders there. Consider the scenario where your product is either the only one installed (should remove the folder) or is the last one to be uninstalled from the suite (should remove the folder). In these two cases the folder should get removed. The order of RemoveFolder elements get defined might matter so I would test putting the ProgramFilesOrgFolder remove element before and after ProgFilesAppDir remove element just to see if one of them fails.
Alternatively you can just suppress ICE64 but then you will leave a folder behind once all the products are uninstalled. Not a huge deal really but might be annoying to some users.
I use auto-guids in my <Product> but can't figure out how to use them with <Module>. I only get this error:
The component X has a key file with path 'TARGETDIR\company...'.
Since this path is not rooted in one of the standard directories (like
ProgramFiles Folder), the component does not meet the criteria for
having an automatically generated guid.
Above, company is the value mapped to !(loc.ProductManufacturerFolderName).
The only problem is that's not true. My directories are rooted in ProgramFiles just like my product is and my product works fine:
<Directory Id="TARGETDIR" Name="SourceDir">
<Directory Id="ProgramFiles64Folder">
<Directory Id="MODULEINSTALLLOCATION" Name="!(loc.ProductManufacturerFolderName)">
<Directory Id="Data" Name="Data">
All my component declarations look roughly like this:
<Component Id="DocumentationParty_Business_TestCases_v1xlsx.component" Guid="{YOURGUID-1234-1234-84B3-C595A63428AD}" MultiInstance="yes">
<File Source="../../Development/Integration/SSIS/Documentation/Party_Business_Test Cases_v1.xlsx" KeyPath="yes" Id="DocumentationParty_Business_TestCases_v1xlsx.file" />
</Component>
Breaking it is easy, you only have to change the GUID to * and the above error results. This is broken:
<Component Id="DocumentationParty_Business_TestCases_v1xlsx.component" Guid="*" MultiInstance="yes">
<File Source="../../Development/ClaimsIntegration/SSIS/Documentation/Party_Business_Test Cases_v1.xlsx" KeyPath="yes" Id="DocumentationParty_Business_TestCases_v1xlsx.file" />
</Component>
I have a .wxs file for each directory to which components will be installed. All my component-holding .wxs files have the following structure:
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
<Fragment>
<ComponentGroup Id="DatabasePolicy_Files">
<ComponentRef Id="DatabasePolicyCreateDatabasecmdtemplate.component" />
</ComponentGroup>
<DirectoryRef Id="DataPolicy">
<Component Id="DatabasePolicyCreateDatabasecmdtemplate.component" Guid="*" MultiInstance="yes">
<File Source="../../Development/Database/Policy/CreateDatabase.cmd.template" KeyPath="yes" Id="DatabasePolicyCreateDatabasecmdtemplate.file" />
</Component>
</DirectoryRef>
</Fragment>
</Wix>
Each <ComponentGroup> is included in my master .wxs file through a <ComponentGroupRef>. This works in all my MSI projects and breaks only now that I've started working with merge modules. Also, I've tried commenting out all components except for which matches the above definition and it still breaks on the same error.
What is the problem?
I've had this similar issue myself and based on your error message it's probably the same.
Try adding a ComponentGuidGenerationSeed, that should solve your issue. The ComponentGuidGenerationSeed acts on all subfolders as well so a single one at the top-level is sufficient for all folders.
Example:
<Directory Id="DOCUMENTATIONFOLDER" Name="Documentation" ComponentGuidGenerationSeed="a9f690d3-22b3-488f-bdac-bb665c25933c"/>
http://wixtoolset.org/documentation/manual/v3/xsd/wix/directory.html
The Component Guid Generation Seed is a guid that must be used when a
Component with the generate guid directive ("*") is not rooted in a
standard Windows Installer directory (for example, ProgramFilesFolder
or CommonFilesFolder).
How would I install files directly into a pre-existing folder on the user's computer? All documentation I read only explains creating a custom INSTALLDIR.
Eg. c:\ProgramFiles(x86)\ExampleFolderA\ExampleFolderB\InstalledFile.exe
You should first define the directory structure:
<Directory Id="TARGETDIR" Name="SourceDir">
<Directory Id="ProgramFilesFolder">
<Directory Id="ExampleFolderAId" Name="ExampleFolderA">
<Directory Id="ExampleFolderBId" Name="ExampleFolderB" />
</Directory>
</Directory>
</Directory>
Note that the definition above does NOT create directories when the installation runs. In order for the directories to be actually "created" you have to either place files there (using Component elements), or explicitly state that the directory is empty.
Something like this:
<DirectoryRef Id="ExampleFolderAId">
<Component Id="SampleComponent" Guid="GUID-GOES-HERE">
<File Id="SampleFile" Source="C:\readme.txt" KeyPath="yes" />
</Component>
</DirectoryRef>
or
<DirectoryRef Id="ExampleFolderBId">
<Component Id="EmptyFolderComponent" Guid="GUID-GOES-HERE">
<CreateFolder />
</Component>
</DirectoryRef>
Hope you get the idea.