3

I launch the exe through ShellExecuteEx:

tstring sPath = _T("C:\\Test\\MyApp.exe");
tstring sArgs = _T("/S");
SHELLEXECUTEINFO lpExecInfo = {0,};
lpExecInfo.cbSize  = sizeof(SHELLEXECUTEINFO);
lpExecInfo.lpFile = sPath.c_str();
lpExecInfo.fMask=SEE_MASK_NOASYNC ;     
lpExecInfo.hwnd = NULL;
lpExecInfo.lpVerb = NULL;
lpExecInfo.lpParameters = sArgs.c_str();
lpExecInfo.lpDirectory = NULL;
lpExecInfo.nShow = SW_SHOWNORMAL;

if (!ShellExecuteEx(&lpExecInfo)) {
    // handle the error
    throw CException("Cannot launch an application");
}

int nRes = (int)lpExecInfo.hInstApp; // nRes = 42
DWORD dwErr = GetLastError(); // dwErr = 0

How can I detect if launching is cancelled by UAC? ShellExecuteEx succeeds in this case (hInstApp = 42, GetLastError returns 0).

Thanks

FruitBreak
  • 570
  • 1
  • 7
  • 19
Vitaly
  • 597
  • 1
  • 5
  • 12
  • Vitaly, when having follow up questions post them as comment (as this one) or edit your original post. Don't post it as an answer if it is still a question. – RedX Oct 19 '11 at 06:51

3 Answers3

2

FAR Manager is able to detect the UAC cancellation even when using ShellExecuteEx.

   ╔════════════ Error ═════════════╗
   ║      Operation cancelled       ║
   ║         Cannot execute         ║
   ║ D:\Downloads\fiddler4setup.exe ║
   ║               OK               ║
   ╚════════════════════════════════╝

I've checked what happens under debugger and here's how the struct looks like:

lpVerb = "open";
lpFile = <path to the .exe>;
lpParameters = "";
lpDirectory = <current directory>;
nShow = SW_SHOWNORMAL;
fMask = SEE_MASK_NOCLOSEPROCESS|SEE_MASK_NOASYNC|
   SEE_MASK_FLAG_NO_UI|SEE_MASK_NOZONECHECKS; // 0x800540

You can check the whole magic they do in the source code.

Igor Skochinsky
  • 24,629
  • 2
  • 72
  • 109
2

If ShellExecuteEx() is not returning an error, then there is nothing you can do to detect a UAC cancellation that is occuring outside of ShellExecuteEx's control.

What you should be doing is using CreateProcess() instead. That will return an error if UAC rejécts the new process. Don't use ShellExecuteEx() to launch an .exe file, unless you use the "runas" verb to force a UAC prompt.

Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
  • 6
    CreateProcess() simply fails (with ERROR_ELEVATION_REQUIRED) if the child process requires elevation. You have to use another API (e.g. ShellExecute()) in order to get the prompt. – Luke Oct 18 '11 at 16:10
  • Then the short answer is - there is no way to detect the cancellation if the API does not directly report it to you. – Remy Lebeau Oct 21 '11 at 01:20
  • You might be able to do it by setting the SEE_MASK_NOCLOSEPROCESS flag in the fMask field, but I'm not sure if it returns you a handle for an elevated process. Worth taking a look at, though. – Luke Oct 21 '11 at 14:27
  • If you do get a handle to the process, see if `GetExitCodeProcess()` tells you anything useful when the spawned process ends. – Remy Lebeau Oct 21 '11 at 20:02
0

Now the problem is that CreateProcess succeeds in both cases, when launching is cancelled and is not cancelled. The question is that how to detect when it is cancelled?

Probably not being able to detect whether elevated launch succeeded or failed is a security feature. Else you could probe the system for installed software you should not know about.

RedX
  • 14,749
  • 1
  • 53
  • 76
  • There are two major reasons I think this answer has no merit. 1) One cannot trigger the dialog if one does not have permission to list the contents of its parent folder and read the file. If one has even the first permission, one can see that the program is present without running it. 2) Putting aside the OP's apparent problem, when I cancel a UAC dialog, ShellExecuteEx does in fact report failure and GetLastError returns ERROR_CANCELLED. – Lexikos Feb 27 '21 at 22:55