Wix installer - Issues installing to a mapped network drive - wix

I have an issue while trying to install to a mapped network drive. I have permissions to create, edit, delete files/folders at this location. It is always for this particular file abc.xslt
This is the what the wix log tell me:
MSI (s) (48:9C) [14:48:11:524]: Source for file 'NSf100' is compressed
InstallFiles: File: abc.xslt, Directory: M:\abc\def\, Size: 17819
MSI (s) (48:9C) [14:48:11:525]: Re-applying security from existing file.
Error 1315. Unable to write to the specified folder: M:\abc\def\.
MSI (s) (48:9C) [14:50:33:621]: Product: ABC -- Error 1315. Unable to write to the specified folder: M:\abc\def\.
Then, I tried to do a RemoveFile before installing the new one as follows:
<RemoveFile Id="NSfr100" Name="abc.xslt" On="install"/>
Again, I see this error:
RemoveFiles: File: abc.xslt, Directory: M:\abc\def\
Error 1315. Unable to write to the specified folder: M:\abc\def\.
MSI (s) (9C:3C) [16:54:29:389]: Product: ABC -- Error 1315. Unable to write to the specified folder: M:\abc\def\.
Action ended 16:54:29: InstallFinalize. Return value 3
So, I tried setting permissions on that file as follows but that did not help either,
<util:PermissionEx User="Everyone" Domain="domain name" GenericRead="yes" Delete="yes" Read="yes" GenericAll="yes" GenericWrite="yes" GenericExecute="yes" WriteAttributes="yes" WriteExtendedAttributes="yes" ReadAttributes="yes" ReadExtendedAttributes="yes" ReadPermission="yes" Synchronize="no" />
The same installer works without issues when installing locally. Could someone please help me understand why this fails while installing to a mapped network drive?
Thanks much

This isn't a supported scenario for Windows Installer. The mapped drive only exists in the users logged in context. The windows installer service running as SYSTEM cannot see it.

Related

WiX Condition works in MSI but not in Bundle

When I'm building my MSI file, and I use a basic condition, the expected happens. For example, let's say that I have this in Setup File:
<Property Id="myProperty" Value="0"/>
<Condition Message="Value of myProperty is [myProperty], and it should be 1.">
<![CDATA[Installed OR myProperty = "1"]]>
</Condition>
If I build this and run the MSI file, it works -- that is, it displays the error message and quits.
Working condition when running MSI
However, if I put the MSI into a Bundle, it doesn't work. That is, when I put just this into my Bootstrapper ("Properties" below is the name of my Setup project -- bad name, I realize):
<Chain>
<MsiPackage SourceFile="$(var.Properties.TargetPath)"/>
</Chain>
And then I run the setup file, I get an error. When the installation starts, it checks the condition, gives me the expected message box (same as above), and then I get this error message:
Setup Failed
Looking at the Log, I get three error messages:
Error 0x80070643: Failed to install MSI package.
Error 0x80070643: Failed to execute MSI package.
Error 0x80070643: Failed to configure per-machine MSI package.
With the exit code:
Exit code: 0x643, restarting: No
I'm such a noob at WiX that I'm not even sure how to go about researching what the problem is -- I can't even ask an intelligent question. Hence, I'm reaching out to you kind folks!
(I'm using WiX 3.10 and Visual Studio 2015)
EDIT:
Thanks for getting back to me! I tried your suggestions:
In the installer file, I made the property public and I made it secure. I left the condition the same, and, since I don't think that I should get the value here as opposed to in the bootstrapper, I left the value of the property out. Here is the code that I made for the property/condition:
<Property Id="MY_PROPERTY" Secure="yes"/>
<Condition Message="MY_PROPERTY is [MY_PROPERTY]. Should be 1">
<![CDATA[Installed OR MY_PROPERTY = "1"]]>
</Condition>
Then, in the boostrapper file, I added a child element of and gave it a value:
<MsiPackage SourceFile="$(var.LCondErrorInstaller.TargetPath)">
<MsiProperty Name="MY_PROPERTY" Value="0"/>
</MsiPackage>
When I ran it, I got almost the same behaviour as I did before, except for one difference -- when I get the error message. This time, I get the pop-up screen with the Message condition and the same error message as I did before (see "Setup Failed" above), except this time I get it happens little later in the installation, making me think that the condition is, in fact, getting triggered in the bootstrapper.
As far as the log files, they look the same (I'm not sure how to get log files of the MSI when running the Burn file, what I do now is just run the Burn file with the flag "/l", like so: > file.exe /l logFile.log).
For clarity, here are the parts of the log file that appear to be important:
Error 0x80070643: Failed to install MSI package.
Error 0x80070643: Failed to execute MSI package.
Error 0x80070643: Failed to configure per-machine MSI package.
...
Exit code: 0x643, restarting: No
I should have been more specific when I initially asked the question about what kind of behaviour I'm looking for...
I will have more than just that one MSI file in the Burn file. What I want to do is this: when the Burn file installs, if there is a condition in one of the MSI files that is not met, I want that MSI file to simply not be installed, and the rest of the MSIs to be installed. I don't care about the UI.
If there's another way to do this, I'm all ears.
If you have launch conditions in the MSI you can replicate or move those launch conditions into the bootstrapper bundle itself to stop this type of behaviour.
Launch condition failure returns Fatal Error 1603 (0x643 in hex) which is what I would expect to see when the MSI launched by the bootstrapper fails due to launch condition not met.
You should see something like this in the msi's logs
Action ended 17:33:38: LaunchConditions. Return value 3.
MSI (c) (08:4C) [17:33:38:610]: Doing action: FatalError
Action 17:33:38: FatalError.
Action start 17:33:38: FatalError.
...
MSI (c) (08:4C) [17:33:41:188]: MainEngineThread is returning 1603
To elaborate, you would have to change your msi package definition to the following to get it to run properly through the bootstrapper
<Chain>
<MsiPackage SourceFile="$(var.Properties.TargetPath)">
<MsiProperty Name="MYPROPERTY" Value="1"/>
</MsiPackage>
</Chain>
Additionally if you want to pass in a property from your bootstrapper to your MSI the property must be a public property which is a property whose name is ALL CAPS.
If you want to use this property somewhere in the Install phase of your msi you must also mark this property as secure.

WIX Error 1327. Invalid Drive

I have windows service installation process, which worked before untill today.
When I run msiexec /i "C:\installation.msi" /log "C:\logs.log" TARGETDIR=C:\destinationPath
I got error: Error 1327. Invalid Drive: D:\
In logs I can see TARGETDIR repeated 3 times:
Property(N): TARGETDIR = D:\
Property(S): TARGETDIR = C:\
Property(C): TARGETDIR = C:\
In Wix I have
<Property Id="TARGETDIR"/>
And installation
<Directory Id="TARGETDIR" Name="SourceDir">
<Directory...
What can be wrong that firt value of property TARGETDIR is D ? I don't have this drive, can it be cache, or some windows key ? Any suggestion ?
It would help to see more of the verbose log, but if the only log entries related to cached products are as you posted then you are not doing a fresh install. A fresh setup will say something about running from the actual MSI file, and will not refer to cached context. That implies that your ProductCode (or sometimes PackageCode) is still installed on the system, and you are NOT installing your MSI product - you are causing a maintenance mode operation on that already installed product, and that's what is looking for a D:\ drive.
The fact that you removed all those registry entries and it worked confirms this, but it would have been preferable to locate that installed product and uninstall it. There are all kinds of registry entries related to PackageCode, UpgradeCode, Component IDs that may still cause issues. If you were to ask Windows which products are using a particular Component ID you may find it believes there are two. If you uninstall your product does it remove all the files and run any custom actions successfully? If the ref counting of component Ids is wrong that's the kind of thing that can happen.
when I dumped all logs using /lvoicewarmupx
I saw
MSI (s) (D0:0C) [18:55:43:619]: Using cached product context: machine assigned for product: EDA754DD57D2E6245809CEAB950DA2D7
MSI (s) (D0:0C) [18:55:43:619]: Note: 1: 1327 2: D:\
Then I removed from registry all entries for key EDA754DD57D2E6245809CEAB950DA2D7
And Problem is solved.
We have experienced a similar problem "Inavlid Drive A:" on installs where users are either:
Trying to run the MSI from a zip
Trying to run the MSI from a network location

How does the MSI Installer InstallValidate determine files in use?

I'm trying to work out how to prevent the Restart Manager from detecting that a restart is required when I uninstall my application.
My application has a Windows Service which uses a native DLL (Tanuki Wrapper) and creates another process (Java) which writes to some log files. I'm using WiX Toolset but I'm more interested in the MSI Installer itself. I've been trying some changes manually using Orca. For reference the relevant WiX configuration is (there are some other components for JARs etc that I have omitted):
<DirectoryRef Id="logs3327407xx">
<Component Guid="344ec345-bdd7-4c1d-801f-55ddf9e07735" Id="logs_wrapper_log88580873x">
<File DiskId="1" Id="fl_logs_wrapper_log88580873x" Name="wrapper.log" Source="logs\wrapper.log"/>
</Component>
</DirectoryRef>
<DirectoryRef Id="bin97543xxxx">
<Component Guid="67c93dd8-36ad-427f-9d79-64a07c719eea" Id="bin_wrapper_windows_x86_64_exe189026768">
<File DiskId="1" Id="fl_bin_wrapper_windows_x86_64_exe189026768" KeyPath="yes" Name="wrapper-windows-x86-64.exe" Source="bin\wrapper-windows-x86-64.exe"/>
<ServiceInstall Account="LocalSystem" Arguments="-s "..\conf\wrapper.conf" wrapper.console.flush=true" Description="My Example Service." DisplayName="My Service" ErrorControl="ignore" Id="srvc_i_bin_wrapper_windows_x86_64_exe189026768" Interactive="no" Name="MyService" Start="auto" Type="ownProcess" Vital="yes"/>
<ServiceControl Id="srvc_c_bin_wrapper_windows_x86_64_exe189026768" Name="MyService" Remove="uninstall" Start="install" Stop="both" Wait="yes"/>
</Component>
</DirectoryRef>
<DirectoryRef Id="bin_wrapper_native490235675">
<Component Guid="d7e4295a-1ce5-4dd2-aa92-230caac34247" Id="bin_wrapper_native_wrapper_windows_x86_64_dll156404367">
<File DiskId="1" Id="fl_bin_wrapper_native_wrapper_windows_x86_64_dll156404367" Name="wrapper-windows-x86-64.dll" Source="bin\wrapper\native\wrapper-windows-x86-64.dll"/>
</Component>
</DirectoryRef>
I understand that there is logic in the InstallValidate action that determines whether files are in use. It will either use the Restart Manager or the FilesInUse based on the MSIRESTARTMANAGERCONTROL property.
If I use the Restart Manager then it opens the dialog saying a restart is required. The logs say:
MSI (s) (1C:7C) [12:27:14:679]: Doing action: InstallValidate
Action ended 12:27:14: MigrateFeatureStates. Return value 0.
MSI (s) (1C:7C) [12:27:14:679]: PROPERTY CHANGE: Deleting MsiRestartManagerSessionKey property. Its current value is 'f2947dee632d694f8b4f1795ff254092'.
...
MSI (s) (1C:7C) [12:27:14:679]: Component: bin_wrapper_windows_x86_64_exe189026768; Installed: Local; Request: Absent; Action: Absent; Client State: Local
MSI (s) (1C:7C) [12:27:14:679]: Component: bin_wrapper_native_wrapper_windows_x86_64_dll156404367; Installed: Local; Request: Absent; Action: Absent; Client State: Local
MSI (s) (1C:7C) [12:27:14:679]: Component: logs_wrapper_log88580873x; Installed: Local; Request: Absent; Action: Absent; Client State: Local
...
MSI (s) (1C:7C) [12:27:14:741]: RESTART MANAGER: Detected that application with id 11368, friendly name 'java.exe', of type RmCritical and status 1 holds file[s] in use.
MSI (s) (1C:7C) [12:27:14:741]: RESTART MANAGER: Did detect that a critical application holds file[s] in use, so a reboot will be necessary.
MSI (s) (1C:7C) [12:27:14:741]: Note: 1: 1610
It doesn't actually say the files but if I disable the Restart Manager and use the FilesInUse instead then no dialog appears. This time the logs say:
Info 1603. The file C:\...\wrapper-windows-x86-64.exe is being held in use by the following process: Name: wrapper-windows-x86-64, Id: 11004, Window Title: '(not determined yet)'. Close that application and retry.
MSI (s) (1C:8C) [12:33:23:458]: 2 application(s) had been reported to have files in use.
Info 1603. The file C:\...\wrapper-windows-x86-64.dll is being held in use by the following process: Name: java, Id: 8284, Window Title: '(not determined yet)'. Close that application and retry.
MSI (s) (1C:8C) [12:33:23:458]: Note: 1: 2727 2:
MSI (c) (AC:28) [12:33:23:458]: File In Use: -wrapper-windows-x86-64- Window could not be found. Process ID: 11004
MSI (c) (AC:28) [12:33:23:458]: File In Use: -java- Window could not be found. Process ID: 8284
MSI (c) (AC:28) [12:33:23:458]: No window with title could be found for FilesInUse
Can someone please explain how the InstallValidate determines which files are in use?
Additionally how can I prevent the Restart Manager from saying that files are in use which will be released once the service is stopped?
As a side question, why is the Restart Manager not showing the MsiRMFilesInUse dialog? I have checked all the requirements on MsiRMFilesInUse Dialog and as far as I can tell they are all true.
I remember reading somewhere that using a ServiceControl rather than a custom action helps the Restart Manager know that those Files within that Component are used by the service. I have tried adding the files to that Component but it doesn't seem to make any difference.
The behavior of InstallValidate and in-use files related to ServiceControl isn't complicated. If there are files in use by a service and that service is in the ServiceControl table and marked to be stopped on uninstall then it will ignore those files as far as in-use behavior. Putting files in the same component makes no difference, and obviously Windows can't know that the service code is going to shut down processes that are causing files-in-use situations. To my knowledge there is no way of telling InstallValidate that some files really won't be in-use when they are due to be uninstalled.
You won't get an old-style FilesInUse dialog when Restart Manager isn't used because it requires an active window that the user can be prompted to close to shut down the app. This is the reason tray apps without an active window don't cause a FilesInUse dialog. I can't be sure, but it appears to me that the first log extract referring to Restart Manager actually IS RMFilesInUse, and I think that's the dialog you're seeing.
If the only files-in-use issues you're seeing are related to the java process that the service will shut down then a possible solution is:
Suppress all files-in-use dialogs during uninstall.
Use ServiceControl to shut down your service.
Ensure that your ServiceControl was Wait=1 and that it does not respond to the "stop service" request until the java process has been terminated, assuming you can explicitly shut it down.
Windows isn't so dumb that it will force a reboot just because files were in use at InstallValidate time but that isn't true at actual removal time. So the reboot won't be necessary if you can make sure that the files are no longer in use at the actual time they need removing/replacing/whatever. It's not unusual to see the dialog and then find that a reboot is not in fact necessary. So if you suppress the dialog and get everything shut down you won't see a reboot request.

Wix MSI fails when setting permissions on network path (util:PermissionEx)

I have an MSI that works perfectly when installing locally. If the data path is set to a network location, it fails.
Relevant LOG:
MSI (s) (BC:4C) [17:01:57:322]: Executing op: ActionStart(Name=ExecSecureObjects_64,,)
MSI (s) (BC:4C) [17:01:57:322]: Executing op: CustomActionSchedule(Action=ExecSecureObjects_64,ActionType=3073,Source=BinaryData,Target=ExecSecureObjects,CustomActionData=\\ravel\TeamData\lrieger\Tim2015Pre_Data\CreateFolderEveryone-1073741824C:\ProgramData\Nemetschek Engineering\TIM 2015.0.0.477590057-pre\CreateFolderEveryone-1073741824)
MSI (s) (BC:78) [17:01:57:324]: Invoking remote custom action. DLL: C:\Windows\Installer\MSIAEDE.tmp, Entrypoint: ExecSecureObjects
MSI (s) (BC:A8) [17:01:57:324]: Generating random cookie.
MSI (s) (BC:A8) [17:01:57:325]: Created Custom Action Server with PID 4488 (0x1188).
MSI (s) (BC:60) [17:01:57:335]: Running as a service.
MSI (s) (BC:60) [17:01:57:337]: Hello, I'm your 64bit Elevated custom action server.
ExecSecureObjects: Error 0x80070005: failed to get security info for object: \\ravel\TeamData\lrieger\Tim2015Pre_Data\
CustomAction ExecSecureObjects_64 returned actual error code 1603 (note this may not be 100% accurate if translation happened inside sandbox)
MSI (s) (BC:4C) [17:01:57:393]: Note: 1: 2265 2: 3: -2147287035
MSI (s) (BC:4C) [17:01:57:393]: User policy value 'DisableRollback' is 0
MSI (s) (BC:4C) [17:01:57:393]: Machine policy value 'DisableRollback' is 0
Action ended 17:01:57: InstallExecute. Return value 3.
WIX Code:
<Component Directory="DATA_DIRECTORY">
<RegistryValue Root="HKLM" Key="$(var.RegRoot)\Setup" Name="TIM_DATA_DIRECTORY" Value="0xff" Type="string" />
<CreateFolder>
<util:PermissionEx User="Everyone" GenericRead="yes" GenericWrite="yes" Domain="[LOGONDOMAIN]" />
</CreateFolder>
</Component>
Without the util:PermissionEx it works on the network share, but setting these permissions is a requirement, at least if the target directory is local.
Q: Is it possible to set permissions on a network location with wix/msi?
If Not, how can I detect that it is a network directory?
Or is there, in MSI, any way to mark a component as allowed to fail?
If it is not possible to do any of the above, I will probably need to write a custom action that tries to set the permissions but suppresses any failures...
Briefly, I don't think it's possible. The issue is that elevated custom actions run with the system account, and that account doesn't have any network privileges. That WiX utils code is already a custom action that's supplied as a helper for a common task, so writing your own custom action isn't going to help. If you run the CA not impersonated it won't run elevated (unless you elevate the entire MSI install at launch time) so that's not likely to help either. You need an elevated user app to do this for the network share, maybe run at first use of the app itself. This might help:
http://wixtoolset.org/documentation/manual/v3/howtos/ui_and_localization/run_program_after_install.html
I would assume it's a network share if it starts with \. A drive letter won't work with the CA anyway because drive letters are a user profile mapping (not a system mapping), so the system account won't know of them. In .Net, FileSystem.GetDriveInfo would help.

Writing install location to registry from installer

I am using WiX 3.6 to create an installer.
One of the needs is to write the location of the install location to the registry in either HKCU or HKLM depending on the ALLUSERS property.
Now based on the research I have done I think the following should work
<RegistryKey Root="HKMU"
Key="Software\OpenCover"
Action="createAndRemoveOnUninstall">
<RegistryValue Name="Location"
Type="string"
Value="[APPLICATIONFOLDER]"
Action="write"
KeyPath="yes" />
</RegistryKey>
The problem is it only works for when ALLUSERS="" i.e. HKMU is interpreted as HKCU.
If I try a perMachine installation where ALLUSERS=1 then the entry is not written to HKLM as expected, though when I look at the installer log file I see the call to WriteRegistryValues.
MSI (s) (D4:14) [22:46:24:901]: Executing op: ActionStart(Name=WriteRegistryValues,Description=Writing system registry values,Template=Key: [1], Name: [2], Value: [3])
Action 22:46:24: WriteRegistryValues. Writing system registry values
MSI (s) (D4:14) [22:46:24:902]: Executing op: ProgressTotal(Total=2,Type=1,ByteEquivalent=13200)
MSI (s) (D4:14) [22:46:24:903]: Executing op: RegOpenKey(Root=-1,Key=Software\OpenCover,,BinaryType=0,,)
MSI (s) (D4:14) [22:46:24:903]: Executing op: RegAddValue(Name=ConsoleLocation,Value=C:\Program Files (x86)\OpenCover\,)
WriteRegistryValues: Key: \Software\OpenCover, Name: ConsoleLocation, Value: C:\Program Files (x86)\OpenCover\
MSI (s) (D4:14) [22:46:24:906]: Executing op: RegCreateKey()
WriteRegistryValues: Key: \Software\OpenCover, Name: , Value:
Can someone explain how to achieve the task I need to complete
The problem is actually to do with a 32 bit installer on a 64 bit platform.
When ALLUSERS="1" is used in this scenario then the registry entries, marked up with HKMU, are actually being written, but under in this case, HKLM\Software\Wow6432Node\OpenCover. I suspect entries marked as HKLM are also redirected in the same manner.
Unfortunately the documentation on WriteRegistryValues Action does not explain the 32/64 bit of "magic" redirection and the information, details regarding the actual registry entry, doesn't appear in the logs.
To get an insight as to what is happening then the following article demystifies the observed behaviour Registry Keys Affected by WOW64. From this article we can see that the installer "thinks" it is writing to a folder HKLM\Software but this is actually being "redirected" to HKLM\Wow6432Node\Software for a 32 bit process on a 64 bit platform and hence explains why it is not reflected into the log files. The article also explains why when ALLUSERS="" and HKMU is then HKCU why the entries appear where one would expect them to be, because these entries are "shared" between 32 and 64 bit applications.
My guess is that your installer isn't elevating (UAC enabled?) and that the write to HKLM is being redirected to HKCU.
BTW, you could also consider using the Windows Installer API from within your application to query the UpgradeCode, ProductCode, ProductInformation(INSTALLLOCATION) without the need to write a registry key to store this metadata.