Using WiX (Windows Installer XML) I have created an MSI installer which installs Word templates into the users Application Data folder, e.g. on Windows XP
C:\Documents and Settings\<user>\Application Data\Microsoft\Templates
I'm retrieving the path to this folder from the registry:
<Property Id="APPDIR" Secure="yes">
<RegistrySearch Id="RegSearch_AppData"
Type="directory"
Key="Software\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders"
Name="AppData"
Root="HKCU" />
</Property>
<CustomAction Id="ActionWordTemplateFolderAssign"
Property="TEMPLATEFOLDER"
Value="[APPDIR]Microsoft\Templates" />
<InstallExecuteSequence>
<Custom Action="ActionWordTemplateFolderAssign" Sequence="1" />
</InstallExecuteSequence>
However, some users installing the MSI file on Windows Vista receive an error because the APPDIR property is empty.
Is APPDIR not the correct way to retrieve the Application Data folder? Or do I have to consider another property on Vista?
EDIT: This is just a short version of the WiX Code to retrieve Word's template folder. First I'm actually checking whether the user has a custom template folder defined by a policy or under HKCU\Software\Microsoft\Office\12.0\Common\General\UserTemplates. However, if none of these are set the fallback is to use the default location under %APPDATA%\Microsoft\Templates which is retrieved by the above code.
You should use [AppDataFolder] instead. I can't find anything about "appdir" in the windows installer property reference.
Edit after question edit: The shell folders key (great blogpost btw) where you get your appdir value from is a very old and deprecated way to get at the system folders. It is only there for backwards compatibility and you should not rely on it. Especially if you live near Raymond Chen.
Edit 2: Since the real question turns out to be "how do I find the user's word template folder"... The word template folder is not always
[AppDataFolder]\Microsoft\Templates
This is because the template folder can be configured under tools - options - file locations - user templates. Ironically we are back to searching the registry if we want to detect this:
<Property Id="USERTEMPLATES">
<RegistrySearch Id="SearchUserTemplates"
Root="HKCU"
Key="Software\Microsoft\Office\11.0\Common\General"
Name="UserTemplates"
Type="raw" />
</Property>
This registry value is normally not present however, and you cannot specify a default value that contains [AppDataFolder] here (I tried).
Instead, I would try to define two components, one which installs to USERTEMPLATES and one which installs to [AppData]\Microsoft\Templates. You can then make use of Condition elements to test for the existence of USERTEMPLATES, and install only the right one.
Some additional information:
The reference for MSI properties containing special folders:
http://msdn.microsoft.com/en-us/library/aa370905(VS.85).aspx#system_folder_properties
And a link to a related blog post:
What is the WiX equivilent of Environment.SpecialFolder.ApplicationData from .NET?
Divo - In response to your comment on localized Vista installations, the problem probably isn't so much localized Vista (unless I'm reading you wrong) but localized Office.
Microsoft\Templates might become Microsoft\Vorlagen with German office for example. It's a pain in the ass, because I haven't found a reliable source of documentation on what folder names have been localized in Office, and what haven't.
My particular problem was with installing Macros to [AppDataFolder]Microsft\Word\STARTUP - which is localized for some languages only. #$%# in the end we just get customers to manually move the templates, the majority of our markets don't have a problem but we've noticed Italian and Turkish office plus a couple of others seems to exhibit this rather annoying behaviour.
On Vista there is a new standard folder available called TemplateFolder. I think that is what you want. To use it in WiX just do something like:
<DirectoryRef Id="TARGETDIR">
<Directory Id="TemplateFolder" Name="Templates"/>
</DirectoryRef>
Then you can reference the TemplateFolder Directory where ever you may need it.
Related
This post is a follow up on this thread I created not long ago.
Basically I'm setting up a shared folder with WiX, and I'd like to come up with a clean and efficient way of doing it (see the link above for the best practices).
My current setup looks like this:
<Directory Id="MYSHAREDFOLDER" Name="FolderName">
<Component Id="MyShareComponent" Feature="MyShareFeature" Guid="MyGuid">
<util:User Id="Everyone" Name="Everyone" />
<util:FileShare Id="MyFileShare" Name="MyFolderName">
<util:FileSharePermission User="Everyone" GenericAll="yes"/>
</util:FileShare>
<CreateFolder>
<util:PermissionEx User="Everyone" GenericAll="yes" />
</CreateFolder>
</Component>
</Directory>
Ultimately I've ran into the good old issue of localization : the "Everyone" user group doesn't exists on non-english Windows, it is localized and translated into the OS culture.
I'm not trying to localize my msi, I just want to retrieve the "Everyone" user group equivalent in other cultures.
Now I've seen quite a few posts that could help me with this, but most of them are really becoming old, and in regard to the best practices (again, you can check Stein Åsmul's answer on my previous thread), I couldn't find any solution that doesn't involve using custom actions, or scripts or defining the user value directly into the .wxs file.
I'm trying to avoid custom actions, but most other solutions seems kind of obscure or hardly maintainable to me at the moment.
I'd like to know if, as of today, there is a better way to get the localized "Everyone" user group to pass on to the util:User tag ?
And if not, what solution should I go for then?
EDIT:
In regard to Stein's comments:
Unfortunately for me, "Everyone" doesn't translate at all at runtime, whether the OS is localized or not. I mean "Everyone" works on the english version of Windows of course, but my primary targets are foreign versions, where it doesn't translate.
I also forgot to mention originally that I considered using the built-in WixQueryOsWellKnownSID, because I saw Rob Mensching suggested it as well, however I get the following error and haven't been able to get around that (same behavior for User and Administrator):
Failed to create user. (-2147022694 BUILTIN\Users)
I've seen this post that was followed up here, but couldn't come up with anything better.
I've also checked the comments from this link where they explain you shouldn't be using a merge module, but I'm not. Everything is in the .wxs file.
As of the SDDL string, I have read the whole thing but as far as I understand, you need to know the exact location of the folder beforehand to generate the proper SDDL ? I can't rely on this solution as the folder location is decided at runtime. Unless I got it wrong of course.
I'm trying to write a registry value to HMLM using WIX installer, but no luck so far.
I have read official documentation, and some related information. Pitty, but official doc only says how to write to HKLU, that does not suit my needs.
I have also looked at some questions like Cannot create registry key value with WiX installer
but if I try doing like this and put it in
I have x86 installer and also tried to follow recomendation for writing to Software\Wow6432Node, but no luck.
May be there is some difference in setting it in 3.10 version? Can someone write example including some surroundings to figure out how and where shoud the value be put to create a registry folder + key-values on install and delete them on uninstall?
Thank you very much.
To write to the registry you need to add the <RegistryValue> element as a child to a <Component>.
In the short snippet I created a component which will add a registry entry to the registry at HKLM\SOFTWARE\$(var.RegistryRootKeyName)\v7 called "ClientPath" with the value of the INSTALLDIR property.
<DirectoryRef Id="BIN">
<Component Id="program.exe">
<File Id="program.exe" KeyPath="yes" Source="$(var.BinariesDir)\_bin\program.exe" />
<Shortcut
Id="ClientInstallDirShortcut"
Name="$(var.Product) $(var.InstallerVersion)"
Directory="INSTALLDIR"
Target="[#program.exe]"
WorkingDirectory="BIN"/>
<RegistryValue
Id="ClientInstallDirRegShortcut"
Root="HKLM"
Key="SOFTWARE\$(var.RegistryRootKeyName)\v7"
Type="string"
Name="ClientPath"
Value="[INSTALLDIR]"/>
</Component>
</DirectoryRef>
Now, to have this registry created, you need to include the Component in a feature that is installed during your install.
<Feature Id="ClientMain" Title="Client" Level="1" >
<ComponentRef Id="program.exe" />
</Feature>
Because you own this registry location, once you uninstall everything related to the component the registry entries will be automatically removed by the windows installer and the folders created as well if they are empty.
I think the issue you are having is related to the confusion around HKLM\SOFTWARE\Wow6432Node. You don't actually have to specify Wow6432Node in your registry key. If you do, then your registry key is probably going to HKLM\SOFTWARE\Wow6432Node\Wow6432Node\...
There are two 'views' of the registry you can see when you open/create a registry key on a 64 bit machine. You can use the 32-bit view or the 64-bit view.
When you are using the 32 bit view, Wow6432Node is automatically inserted into your registry key path for HKLM\SOFTWARE\... . You can force the 64 bit view by adding Win64="yes" to your registryvalue element but you should make sure you are only trying to write to or read the 64 bit registry on a 64 bit machine.
The default view the registry uses is tied to the bitness of the process. If you are running a 64-bit process installer on a 64-bit machine, to access the 32-bit registry locations you need to set Win64="no" (I think this is how it works). Similarily for a 32-bit installer, the default view is the 32-bit registry which automatically adds Wow6432Node to your HKLM\SOFTWARE registry keys.
In previous version of installer, created by Wix, next code exists:
<InstallExecuteSequence>
<RemoveExistingProducts After="InstallInitialize" />
</InstallExecuteSequence>
In order to work around the bug in Windows Installer described in this knowledge base article code has been fixed:
<InstallExecuteSequence>
<RemoveExistingProducts After="InstallFinalize" />
</InstallExecuteSequence>
But now, if I install program with installer with first code and then install program with installer with second code without remove installed program, all files remove and my path exist empty folders (all files in both installer have equal name).
If I open second installer and press Repair - all files appear.
What wrong and how this problem fix?
P.S. Sorry for my English :(
Just a guess: It sounds like the first and the second setup install the same files but as part of components with different component id's. To verify this, you can open both msi files with orca.exe and compare the component ids.
The component IDs should stay the same, so that they can be properly reference counted. Otherwise you will get different components trying to manage the same files, which will in this case result in disappearing files when either of the components is uninstalled.
If you follow the windows installer rule that the content of a component should never change (i.e. never remove or add files to it) then the component GUIDs generated by wix should automatically stay stable. This is one of the reasons why it is best to have one component per file.
After following the advice in this question successfully, I added a couple additional lines of code for another custom action. This one is intended to call regsvr32 on the copy of capicom which I've tried to put in the user's system folder. However, I'm getting error 2721, which seems to be a custom action not found error, from what I've seen. Any suggestions? I'm trying to maintain consistency with previous iterations of my installer by calling regsvr, rather than just adding the registry entries during install, which could be a good idea instead. :::shrug:::
<Directory Id="SystemFolder" Name="Sys">
...
<component ...>
...
<File Id="CapiCom.Dll" LongName="CapiCom.Dll" Name="CAPICOM.DLL" Source=... />
</component>
</directory>
...
<CustomAction Id="REGCAPICOM" ExeCommand='regsvr32.exe "[SystemFolder]capicom.dll"' Return = "ignore" Execute="deferred" />
...
<InstallExecuteSequence>
...
<Custom Action="REGCAPICOM" After="InstallFiles" />
</InstallExecuteSequence>
Edit: Yes, using regsvr32 as an installer is ugly. But when I downloaded the Capicom SDK, that is what MS said to do in order to install it. Searching around has found many people saying that this is a stupid way to do it...but it's also mechanism MS provided. I'll listen to suggestions for a better way. I don't consider it a big deal if Capicom being left behind when my application is uninstalled, considering that it's a standard windows component.
Edit: Hmmm. Apparently, one of the things running selfreg on the dll does is to create a random seed to add to the registry. Not sure what mechanism it uses to generate this seed but I suspect it would be considered in poor taste to just generate one myself, especially if I gave all the users the same seed. Not sure.... Apparently if I skip this Capicom does it on its own, so I'm fine.
The Right way:
c:\Program Files\Microsoft Visual Studio .NET 2003\Common7\Tools\Deployment\regcap.exe" /O capicom.reg capicom.dll
Run program from Adam Tengen's post here.
Note that Heat (and Tallow, IIRC) do not, as of this posting, work properly on Capicom.
The Wrong Way:
<CustomAction Id="RegisterCapicom" Directory="SystemFolder" ExeCommand="regsvr32.exe /s "[SystemFolder]Capicom.dll"" Return="check" Execute="deferred" />
...
<InstallExecuteSequence>
<Custom Action="RegisterCapicom" After="InstallFiles" />
</InstallExecuteSequence>
Uhh, are you really trying to install a Windows system file yourself? That's not allowed on a great many levels. Also, regsvr32.exe is SelfReg and SelfReg is well known to be evil in installations. Actually using the Windows Installer to write the registration is far superiour
However, the whole design here is very suspect.
You could use heat on the File to create a output WXS file, that will put the capicom.dll information in the registry without the use of regsvr32, when the msi is executed
Something like so:
heat file [Path\Capicom.dll] -template:product -out capicom.wxs
Then add the capicom.wxs to your installer, in that file create a ComponentGroup Element, that contains the Component(s) element(s):
<ComponentGroup Id="capicom">
<ComponentRef Id="capicom.dll"/>
</ComponentGroup>
After in the main WXS file add the Fragment element that will link the capicom component
The last step is to add the ComponentGroupRef to the feature that it belongs to:
<Feature Id="PRODUCTFEATURE">
<ComponentGroupRef Id="capicom" />
... [Other components or ComponentGroups references]
</Feature>
I've got a legacy application that's installed directly to the user's c: drive, in a directory (like c:\MyApp). Nasty stuff. Problem is, the user can specify to have a second installation on a second drive (like e:\MyApp), and they can have two different versions of the application installed at once in either directory. They can also decide to install the app elsewhere in the directory tree, but those are the two most common locations.
I did not write this scheme. It makes baby Jesus cry, as far as I'm concerned.
I have to write an installer to add a module to this scheme, and the user needs to be able to select which installation they want to install the module on. I thought I'd try this in WiX.
How do I do this?
I was going to do a directory search like
<Property Id="MyAppInstallationSearch">
<DirectorySearch Id="MyAppDirectory" Path="C:\MyApp">
</DirectorySearch>
</Property>
and then:
<Directory Id="TARGETDIR" Name="MyAppInstallationSearch">
<Directory Id="INSTALLLOCATION" Name="AdditionalTools">
</Directory>
</Directory>
to have an installation location.
So how do I:
Make that search be relative, not absolute? (the documentation specifies that this can be done, I just don't see how).
If the user has multiple locations, give them a choice of which installation to use?
It will be relative meaning, AdditionalTools will be under the folder found by MyAppInstallationSearch.
This will involve creating UI, which is not an easy thing in Wix, there are products that can create Wix output or WixEdit. You will need to search the directory on all possible places and show the user radio buttons (each bounded to a search results) to selected where to install the the new prodcut.
I had to implement the same thing and I used an external UI handler to completly customize the installation process. I also display a wizard page where the user can select the version he wants to upgrade from. But be aware that this isn't something you can do within a day or two.
I don't know which programming language you use, but if you want to use .NET you can the Development Tools Foundation included with Wix3. The libraries can be found in the SDK folder of your Wix installation (you'll mainly need Microsoft.Deployment.WindowsInstaller.dll), documentation is in the DTF.chm in the doc directory. You'll also need a bootstrapper to install the .NET framework.
You could also wait until Wix 3.5, which will include Burn, a boostrapper and external UI handler you can easily customize.
HTH