Archive: Does "ExecWait" inherit admin ExecutionLevel on Win7? (DX redist installer failing)


Does "ExecWait" inherit admin ExecutionLevel on Win7? (DX redist installer failing)
Our product uses DirectX11, so as part of our NSIS installer, we want to install the DirectX runtime components on the target machine. Our NSIS installer uses "RequestExecutionLevel admin". We copy all of the DirectX redist files (.cab, .dll, .exe) into a temporary directory on the target machine, and then call
ExecWait '"${DIRECTX_TEMP_PATH}\DXSetup.exe"'

This approach has worked without problem for years, on Win7, Vista, and XP. But we recently upgraded to the June 2010 version of the DirectX redist files, and now the DX installer is failing with an internal error on some Win7 x64 boxes. The DX error log shows a few weird failures that could be explained by insufficient permission levels.
Also, on one of the "broken" systems we had the user launch the same DX redist installer manually by double-clicking DXSetup.exe directly, and it succeeded.
So there seems to be a difference between launching the DX installer directly vs. launching it from within an NSIS installer.

So that's my question - when I launch a process using ExecWait, does that process inherit the administrator privileges of the NSIS process?
If not, how can I give the DirectX installer process admin access?
If yes, then can anyone suggest an explanation for why this 3rd-party installer might fail when launched out of NSIS, but succeed when launched manually? Is there anything that I might do differently to avoid these failures?

Thanks in advance!


Yes, child processes inherit the access level of the parent. Keep in mind though that requestexecutionlevel is not guaranteed to get admin access. You must verify that you actually got it using the userinfo plugin. Proper way to do it: http://pastebin.com/EvMkyLLt

But assuming that the installer actually has admin access, I doubt it would be permissions related. What is shown in the logs?


Thanks for your reply MSG.
I added the code you suggested and confirmed that the NSIS installer does have admin privs on the "broken" machine.

I have two datapoints that exhibited this type of failure. On the first machine, after the NSIS-based DX install failed, we ran the same DX installer manually by double-clicking it directly. That succeeded. Unfortunately once it succeeds it will never fail again, as the DX redist cannot be uninstalled... So that test case is now gone.
Now I have one machine left that still fails. I am afraid to try installing DX manually, because if that succeeds, then that test case will be gone as well.

The DXError.log file on the second system contains just these two entries:

--------------------
[04/24/12 11:33:22] module: dsetup32(Apr 4 2007), file: dsinline.h, line: 302, function: DXRemoveFile

Unable to remove C:\Users\I7\AppData\Local\Temp\DXAA35.tmp.

--------------------
[04/24/12 11:33:22] module: dsetup32(Apr 4 2007), file: inline.h, line: 220, function: CleanUpDirectory

Unable to remove: C:\Users\I7\AppData\Local\Temp\DXAA35.tmp\infinst.exe which is locked, reason = 32.


"unable to remove" smells like a permissions issue. but note that hte installer failed severl more times without producing any other error messages, so these errors might be a red herring.

We've run the NSIS installer four or five times on the broken machine, and each time it invoked the DX installer, and each time the DX installer failed.
The first three times we got the "An internal error has occurred. please see the log files" dialog. Each time the DX installer did some work before failing, because more and more of the dll's that it is supposed to install were appearing on the user's system. But the DX logs were not updated properly at all. I can see a dozen or so dll's that were clearly added, but which do not show up in the system's DirectX.log.
When we repeated the test yesterday, the DX installer simply hung. No progress for 15 minutes before we killed it.

I'm at my wits end. Any suggestions or ideas that anyone has, please send 'em along, I'm open to trying them.

One other idea: Is it possible that the CWD could have been messed up when we call ExeWait? When I launch an exe by calling ExeWait form an NSIS script, what CWD does that exe receive?


Is "C:\Users\I7\AppData\Local\Temp" the correct %temp% for that user?

Make sure you delete *.tmp in %temp% before testing again...


The CWD is set with SetOutPath (typically before the FILE commands). ExecWait will not change it.


Anders: yes, the user's login is "I7", so "C:\Users\I7\AppData\Local\Temp" is the correct temp folder. In fact, that account is the system administrator account.
But I just found some additional error messages from a more recent failed installation attempt. It turns out the most recent DirectX redistributable stores its log files under C:\Windows\logs, which is a new location. We did not see those files at first. So the most recent DXError.log file contains two sets of error messages like these:

--------------------
[04/25/12 10:47:48] module: dxupdate(Mar 30 2011), file: dxupdate.cpp, line: 5738, function: DirectXUpdateInstallPlugIn

Failed API: SetupIterateCabinet()
Error: (1224) - The requested operation cannot be performed on a file with a user-mapped section open.

Unable to iterate through C:\Users\I7\AppData\Local\Temp\DIRECT~1\Nov2007_d3dx10_36_x64.cab. The file may be damaged.

--------------------
[04/25/12 10:47:48] module: dsetup32(Mar 30 2011), file: dxupdate.cpp, line: 280, function: CSetup::InstallPlugIn

DirectXUpdateInstallPlugIn() failed.

--------------------
[04/25/12 10:47:48] module: dsetup32(Mar 30 2011), file: setup.cpp, line: 1727, function: CSetup::SetupForDirectX

InstallPlugIn() failed.


I looked up that error 1224, and it seems to be saying that another process had a file handle open on the cab file that the installer was trying to extract. (I know for sure that the cab file itself was not corrupt - I did a binary comparison of the cab files in the temp folder with our installer source directory, and all files were copied cleanly.) I wonder who could be holding the open file handle(s).
My NSIS script executes these commands:

SetOutPath "${DIRECTX_INSTALL_PATH}"
File '${LocalResDir}\DirectX\*'
ExecWait '"${DIRECTX_INSTALL_PATH}\DXSetup.exe"' $3

Is there any chance that the "File" command is still hanging on to any file handles after the ExecWait begins execution? Could/should I do something to confirm that the files are actually ready for consumption?

I still think it must be somehow related to NSIS, because the other system that exhibited this failure had the same DX error messages, but on that system re-running the DX installer manually outside of NSIS succeeded. So in NSIS it failed, outside of NSIS it worked.

If anyone sees anything wrong with our approach, or has any suggestions for better practices, I am all ears.
Right now I have an installer that failed on 1/3 of all machines tested. And our only recourse is to detect the DX failure, tuck our tails, and ask the user to please double-click the DX installer manually. I don't like that option one bit.


Could it be an antivirus program keeping it open while it is scanned? You might try a sleep before the ExecWait, or if you can detect the failure, immediately retry the ExecWait.


Which files does your program actually need from the DirectX runtime? You can simply use the File command to put the ones you need along where your program's main executable is. It'll install faster and reduce your install source size.

Only the .dll is needed with your program, not the .inf or .cat files.


But that probably wouldn't be legal.


Originally posted by chief-pinhead
Is there any chance that the "File" command is still hanging on to any file handles after the ExecWait begins execution?
Are you extracting this .cab? I assumed you only extracted the .exe. What is ${DIRECTX_INSTALL_PATH}?

Could you run Process Monitor to find out why some operation fails? (Maybe disk full or out of memory)

Originally posted by Anders
Are you extracting this .cab? I assumed you only extracted the .exe.
The whole redist directory is extracted, I think:
File '${LocalResDir}\DirectX\*'

Hi folks. Thanks for the ideas.

Anders: I am extracting every file that comes in the June 2010 DirectX redist package - one exe, two dlls, and 155 .cab files. Please look back at my NSIS script snippet for details. After all files are copied onto the target machine, I invoke the just-copied "DXSetup.exe" using ExecWait. That exe does a bunch of logic to decide what cab files need extracting, and then tries to extract them. Somewhere along the way it fails, apparently because some other process is holding an exclusive handle to the cab files, or to some other file-read-related shared resource.

As for the folder to which the DX files are extracted - I used to just "hard-code" the path:

  SetOutPath "$TEMP\DirectXSDK_install"


But once these problems started happening, I was worried that the files being extracted were perhaps corrupt somehow, so I started using a non-temporary path whose contents I could later inspect. So I now define

   !define DIRECTX_INSTALL_PATH "$INSTDIR\Install Media\DirectX Redist"


And I have verified that all 158 files are extracted correctly.

redxii - Microsoft is pretty clear in their documentation that you should run the redist installer hands-off, rather than trying to handle any of the logic or dll's manually. I don't believe manually adding the directX dlls is an option, even if it would allow our exe's to run.

It feels to me like AV is the most likely culprit.
I will run a test with a long sleep before the execWait.
And if we do get the failure, we'll use sysInternals to see who's holding the file handle. But I suspect that would come up empty - I bet the file handles are only held temporarily. Otherwise it would fail on the first cab file every time...

demiller9's idea is sort of pragmatic - just re-run if the ExecWait call returns failure. Unfortunately, the DX installer pops up a very nasty looking failure dialog of its own before returning, so I really can't wait that long. We need to prevent this problem, not react to it.

Thanks for everyone's ideas. Keep them coming!

I just encountered the error again, this time on my own system.
DXSetup.exe showed me the same "an internal system error has occurred" dialog, and then finished with a "Installation failed" dialog. Once I dismissed that, I was surprised to find that the return code reported by NSIS was "0" (success).

In other words, the NSIS command
ExecWait '"${DIRECTX_INSTALL_PATH}\DXSetup.exe"' $3
returned with $3 == 0, where I had expected it to return -9 (the return code for "internal error"). Also, the NSIS error flag is clear.

I was hoping I could at least detect a failure by inspecting the return value, so that I might pop up a "Please try installing DirectX manually" dialog.

Am I doing something wrong here? Is there another way to get the return value from invoking an exe?


ExecWait should always report the correct process exit code, maybe a bug in the DX installer (Process Monitor also displays process exit codes (Might have to doubleclick the process-end event))


Thanks Anders. I used procmon and was able to "capture" a DX failure. Sure enough, DXSetup.exe returns exit status 0, right after showing me the "Installation failed" dialog. I guess I should not be surprised that Microsoft screwed that one up.

Sadly, I can't find a smoking gun in the procmon events, so we still don't know what is making the DX installer fail.

FYI: we have opened a paid service ticket with Microsoft to track this issue down. If I hear anything useful from them, I will cross-post the results here. Thanks for all the suggestions.


I just tried this and it worked.

Section -DirectX

SetOutPath "$PLUGINSDIR\dxruntime"
File "directx\*"

ExecWait '"$PLUGINSDIR\dxruntime\DXSETUP.exe" /silent'

SectionEnd