In the list of System Folder Properties provided by Windows installer, I see a TemplateFolder which is the "full path to the Template folder for the current user". So I can use TemplateFolder as the Directory ID in wix to install user-specific templates.
However, I need to install to the common templates folder, i.e. the one referred to as CSIDL_COMMON_TEMPLATES (also FOLDERID_CommonTemplates) in the windows API. Windows installer doesn't seem to provide a system folder property for this one.
How do I install files to the common templates folder with wix?
The documentation tells us on the "TemplateFolder"-ID that it depends on the installation context whether it is "the full path of the Templates folder for all users" (per-machine) or "the full path of the Templates folder for the current user" (per-user).
I ended up defining a COMMONTEMPLATEFOLDER like this:
<DirectoryRef Id='TARGETDIR'>
<Directory Id='COMMONTEMPLATEFOLDER' Name='Templates' />
</DirectoryRef>
<Property Id="COMMONTEMPLATEFOLDER">
<RegistrySearch Id="searchCommonTemplateFolder"
Root="HKLM"
Key="SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\User Shell Folders"
Name="Common Templates"
Type="raw" />
</Property>
This seems to work fine, but unfortunately it relies on undocumented registry entries. I would still like to find a better way.
Related
I have Wix project in which I need a common used dll library to be installed if it's absent.
If this dll exists I should not overwrite it.
So, when I set DefaultVersion="0.0.0.0" this dll is not overwritten if it exists, its ok. But when I delete app, the dll is beeing removed. How do I prevent removing dll in the case when it existed before installation?
I don't want to make it permanent because it should be removed if it didn't exist before installation.
<Component Id="myLib.dll" Permanent="no" Directory="Shared_Dir">
<File Name="myLib.dll" KeyPath="yes"
Source="mySource\myLib.dll"
DefaultVersion="0.0.0.0"
/>
Add reference to WixUtilExtension and xmlns:util="http://schemas.microsoft.com/wix/UtilExtension" attribute to Wix element in your code.
Define <Property Id="Dll_Installed" Value="false"/> in Product element.
Add child <Condition>NOT Dll_Installed</Condition> to component myLib.dll.
Add that somewhere in your code:
<Fragment>
<util:FileSearch
Id="Dll_Installed"
Variable="Dll_Installed"
Path="[Shared_Dir]myLib.dll"
Result="exists"/>
</Fragment>
DefaultVersion attribute is not necessary.
The feature you are describing is reference counting. The Windows Installer reference counts with Components. Components are identified by their GUID.
So the normal way to address this requirement is to put the File in a Component and make sure the GUID of the Component is stable. The WiX Toolset should do exactly that automatically if if you do not specify the Component/#Guid attribute.
So the default behavior should just work for you.
The only other piece of the puzzle is the Windows Installer will install the latest version of a file. If the file version is the same or less the file will not be installed but will be reference counted.
Based on the details in the question it seems like you should be just fine with:
<Component Directory="Shared_Dir">
<File Source="mySource\myLib.dll" />
</Component>
One might ask why the Windows Installer use Components to reference count files. We'll, it allows you to group other resources, like registry keys, together and control their install as a unit. Much more important if you are installing COM servers than plain old files.
The Wix source code that I feed to the Wix compiler to build an MSI package for my application, contains the following PermissionEx directive, part of a file component which Windows Installer should install with additional (to those that should be inherited by default) permissions:
<PermissionEx Sddl="D:AR(A;;FW;;;BU)" />
As you can surmise, I intend to install the file with inherited permissions ("AR") included in its ACL and on top of that allow members of the Built-in Users group ("BU") to be allowed ("A") to write to the file ("FW").
The code above does not have the desired effect -- the file is installed, but only that single explicit ACE is listed, none of the ACEs that are supposed to be inherited from parent folder.
In contrast, if I subsequently remove all permissions from the file and run cacls file /S:D:AR(A;;FW;;;BU), i.e. specify exactly the same SDDL string, it does work as intended -- the permissions from parent are inherited and form part of the ACL, together with the explicit non-inherited ACE.
I am using Wix 3.11.1.2318 and the Windows Installer version is 5.0.16299.611, all running on Windows 10 Enterprise 64-bit. Orca tells me the MsiLockPermissionsEx table embedded in my built MSI file is populated with the intended SDDL record. So why is the file created without inheriting permissions from its containing folder?
I tried to use "AI" in place of "AR", and both strings together, but none of it had any effect either.
Is this some known limitation or a quirk with Windows Installer? I know that people were talking a while back how the old LockPermissions table (the one specified for Windows Installer versions earlier than 5) was inadequate in this specific regard -- inherited permissions, namely -- but they also said Microsoft was out to address this very issue with the new table feature.
Otherwise what am I doing wrong?
Given your knowledge in this field, you probably have already tried this. It would also be much better to eliminate the need for permissioning, but two snippets for you - notice the Append attribute:
Create a WiX project in Visual Studio. Add the Util namespace to the WiX element:
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi"
xmlns:util="http://schemas.microsoft.com/wix/UtilExtension">
In Visual Studio project, right click References and add reference to "%ProgramFiles(x86)%\WiX Toolset v3.11\bin\WixUtilExtension.dll".
Permission Folder:
<Component Feature="ProductFeature" Id="Test.exe" Guid="PUT-GUID-HERE">
<File Source="C:\Test.exe" />
<CreateFolder>
<util:PermissionEx User="Power Users" GenericWrite="yes" />
</CreateFolder>
</Component>
Permission File:
<Component>
<File Source="C:\Test2.exe">
<util:PermissionEx Append="yes" User="Users" GenericWrite="yes" />
</File>
</Component>
Take a look at WiX's custom PermissionEx in the Util extension.
http://wixtoolset.org/documentation/manual/v3/xsd/util/permissionex.html
I am using wix registry search in order to locate a plugin directory for a program I am trying to integrate with. The value of the registry key looks like this:
%PROGRAMFILES%\Product\Plugins
When I do a registry search with type="raw" like this:
<Property Id="PLUGINDIR_STRING">
<RegistrySearch Id="PluginDirSearchString"
Root="HKLM"
Key="$(var.PluginDirRegKey)"
Name="$(var.PluginDirRegKeyName)"
Type="raw"
Win64="yes" />
</Property>
I will get the exact string.
When I use type Directory like this:
<Property Id="PLUGINDIR">
<RegistrySearch Id="PluginDirSearch"
Root="HKLM"
Key="$(var.PluginDirRegKey)"
Name="$(var.PluginDirRegKeyName)"
Type="directory"
Win64="yes" />
</Property>
I will get an empty string. I am checking the values like this:
<Condition Message='plugin dir "[PLUGINDIR_STRING]" found using registry key "$(var.PluginDirRegKey)" is not a valid path, make sure the path exist'>PLUGINDIR_STRING AND NOT PLUGINDIR</Condition>
<Condition Message='Unable to find registry key "$(var.PluginDirRegKey)", make sure $(var.PRODUCT) is installed'>PLUGINDIR_STRING</Condition>
The first message is displayed indicating that PLUGINDIR_STRING is found, but not PLUGINDIR. I thought the Directory option should expand %programfiles%? How to correctly handle a registry value like this?
%PROGRAMFILES%\Product\Plugins
The raw search does not work the same as directory search. A raw search returns the values in the registry item. A directory search retrieves the value then checks to see if that registry exists - it literally is a directory search for the directory in that registry item and if it doesn't exist then it will not set the property value. The idea is that if you want to use that directory as an install location it tells you that it doesn't exist.
Anyway a verbose log should verify if this is going on - look at the AppSearch and you may find something like a 1314 error saying that the directory in that registry item does not exist.
I cannot reproduce the error you are seeing. Both raw and directory registry searches are expanding %programfiles% correctly. Everything else you are doing seems correct, so the most likely issue with your failing directory search is that your setup is a 32-bit setup and you are attempting to locate a 64-bit directory. Your AppSearch in a 32-bit install will locate the %programfiles% string in your registry entry, but it will invoke WIN64DUALFOLDERS, and search for the directory in the ProgramFiles(x86) folder. If the directory isn't there you will see an empty property value returned.
Use the well defined MSI property to get the program files location.
You can find the list of well defined properties here
So set your property to [ProgramFilesFolder]\Product\Plugins. There is also ProgramFiles64Folder but from the snippets you supplied I can't tell which one you should be using. Ideally you use a 32-bit installer to install into Program Files on a 32-bit OS and Program Files (x86) on a 64-bit OS and you would use a 64-bit installer to install into Program File on a 64-bit OS.
I am using the Wix Toolset 3.8 Visual Studio add-in. I have created a WIX Setup Library which encapsulates some common components that will be consumed by various service deployment projects. This library includes references to several solution files which need to be packaged into each deployment.
These dependencies are used in my wix library fragments in a few different ways. For example:
<Binary Id="InstallationScript" SourceFile=".\Dependency1.vbs" />
<CustomAction Id="DependencyAction"
BinaryKey="InstallationScript"
VBScriptCall=""
Execute="deferred"
Return="check"
HideTarget="no"/>
<Component Id="DLL_Component" Guid="4745D9E2-0EBA-EE57-D8DB-677ADA9E9EC8">
<File Id="DLL1" Name="Dependency1.DLL" Source=".\Dependency1.dll" Vital="yes" KeyPath="yes" DiskId="1"/>
</Component>
The WIX Setup Library builds fine whether I add the files to the project as a link or copy them into the project directory, and whether I reference them in the project directory or use a relative path to their original location. The problem comes when I include the fragment in my WIX Setup Project for my service deployment.
<Custom Action="DependencyAction" Before="InstallFinalize">
NOT INSTALLED AND NOT UPGRADINGPRODUCTCODE
</Custom>
The Setup Project is not at the same relative depth in the folder structure, so a relative path does not point to the same place as it does when referenced from the WIX Library. When I reference the action or feature from the WIX library I get errors like The system cannot fine the file '.\Dependency1.dll' The XML is being read exactly as it is and referenced from the Setup Project location rather than from the Library Project location, so the relative directory is wrong. I can set the relative path in the WIX library to what it should be for the Setup Project that consumes it, or I can copy the solution files into the Setup Project directory as well, but of course, that completely defeats the purpose of using a common WIX library. There are other service deployment projects that need to consume this library.
So the question is, how do I reference solution files in a WIX library in a way that can be used by the Setup Project that consumes it, regardless of the directory that the Setup Project is in?
Create a binary .wixlib that contains the files referenced in your authoring. (That's -bf at the command line, BindFiles property in the .wixproj, "Bind files into the library file" checkbox in Votive.)
I know this has been asked a few times in the past. And I have read all the responses and answers and none of them seem appropriate.
I've tried putting the .config in the same component and in a different component.
I've tried CompanionFile in the same component and in a different component.
I've tried CopyFile.
I've tried a Custom Action.
The component needs to be in the GAC because it is a general logging dll used by many different subsystems (services, websites, etc). But it is configured the same for all systems.
So for now I'm installing the file to a SDK directory and telling the user to copy the file.
The ONLY thing that works is copying the file manually and saying yes to "you need admin perms to do this".
<File Id="SDK.Logging.dll.config"
Source="$(var.LOGGER_DIR)\SDK.Logging.dll.config"
Vital="yes">
</File>
<Property Id="XCOPY">xcopy.exe</Property>
<CustomAction Id="Copy.SDK.Logging.dll.config"
Property="XCOPY"
ExeCommand='"[INSTALLLOCATION]SDK.Logging.dll.config" "[GAC.SDK.Logging.Dir]"' />