Background: Customer has changed the name of the application. The application has stored files under AppData folder and now that folder needs to be copied to new name (and delete the old).
I have MSI installation build with Wix 3.6. I didn't find a standard way of copying folder (only file). So I have been trying to make a custom action which uses xcopy command but this seems to be very hard.
If I make an immediate CA and schedule it after InstallFinalize, this works on WinXP but on Win7 only by runnig the MSI as admin.
If I make a deferred CA I get access denied error. This error comes even when I tested to copy from and to folder under user folder. But deferred CA succeeds if I test it with echo command.
<CustomAction Id="CopyFolder_PropertyAssign" Property="CopyFolder"
Value=""[SystemFolder]cmd.exe" /c xcopy
"[$(var.PlatformCommonAppDataFolder)]OldName"
"[$(var.PlatformCommonAppDataFolder)]NewName"
/s /i /h /k /o /y" />
<CustomAction Id="CopyFolder" BinaryKey="WixCA" DllEntry="CAQuietExec"
Execute="deferred" Return="check" Impersonate="no" />
<Custom Action="CopyFolder_PropertyAssign" Before="InstallFinalize"></Custom>
<Custom Action="CopyFolder" After="CopyFolder_PropertyAssign"></Custom>
CAQuietExec: Access denied
CAQuietExec: Error 0x80070004: Command line returned an error.
CAQuietExec: Error 0x80070004: CAQuietExec Failed
There must be really something else than access denied because same happens under user folder as well but what an earth it is?
Or is there some other solutions for this (besides creating exe package)?
EDIT:
The folder is under all user AppData.
EDIT:
This seems to be impossible. The chosen workaround was to make an immediate CA and guide the users to start the installer as admin (or that should be the situation in most cases). Thanks for help anyway!
The deferred custom action copy into AppDataFolder is probably failing because you're running deferred and with the system account, so it's trying to access the user's AppDataFolder for the system account. Note that 0x80070004 is not access denied, it's "The system cannot open the file."
The immediate CA will fail because by default these CAs aren't elevated, so evidently access requires elevation.
The usual solution to this problem is to use the CopyFile element with . to copy the file from the old location to the new location, using directory definitions for those locations. If you set delete to yes it will move the files, deleting them from the old location.
As Stein points out, this really should be done by the application when the newer version first runs. Data migration really is an application issue that should be solved in the application, and not by overloading the install. Apart from the difficulty you're having, there are also potential issues if the install fails and rolls back (restoring the removed files?) and probably other unforeseen ones.
Application Launch Sequence: If these are userprofile files
- in other words writable by the launching user of the application - then it is much better to do this cleanup in the application launch sequence. Crucially this will make the operation run for every users, and not just the user installing the setup
. It is a common mistake to forget to do the rename for all users on the box. This is assuming that these are per-user files (copies for each user), and not shared files for all users in %ALLUSERSPROFILE%
(only writable for admin users).
MSI Complexity: Please don't do this in the setup. MSI is very complex with impersonation and runtime behavior - which is what causes the mysterious error messages you see. There is absolutely no reason to do this in the setup if it can be done with user rights in the application launch sequence. You simply rename the folder on launch of the application, that should do it? Application launch code is much easier to implement and spectacularly easier to debug than custom actions (especially deferred mode custom actions). You can also provide interactive error messages if need be (as opposed to errors written into a log - or no log at all).
Implementation: Obviously beware of introducing application launch code bugs by perhaps setting a flag in the registry once rename is complete for the user in question - in order to disable this rename operation from running more than once. Roaming profiles could cause problems, make sure you set the operation to run once per machine - at least. You could also write the flag into the non-roaming section of the appdata folder hierarchy? That is probably better. Certainly in fact. Should trigger no roaming issues.
Challenges: Just a couple of challenges:
Locks?
Would there be any file / folder locks on application launch? Maybe? Catch the exception, inform the user, shutdown and retry? Refuse to launch until rename can happen? Acceptable?
Old application launch?
: Is this important? Some people try symbolic links - my two cents - they are the devil's work :-). I guess the question is, what happens if the old application does not find its files? Does it even launch? Second question is: does it matter at all that the old application does not work properly after the "cleanup"?