This issue tracker has been migrated to GitHub, and is currently read-only.
For more information, see the GitHub FAQs in the Python's Developer Guide.

Author eryksun
Recipients Ivan.Pozdeev, eryksun, paul.moore, steve.dower, tim.golden, zach.ware
Date 2016-01-26.04:23:20
SpamBayes Score -1.0
Marked as misclassified Yes
Message-id <1453782201.5.0.210485589702.issue26189@psf.upfronthosting.co.za>
In-reply-to
Content
> An .lnk is launched with ShellExecute which returns control
> immediately upon successful launch

cmd calls ShellExecuteEx, not ShellExecute, and it uses the flags SEE_MASK_NO_CONSOLE (0x8000, don't create a new console) and SEE_MASK_NOCLOSEPROCESS (0x0040, return a process handle if possible). 

Here's a walk-through with a debugger attached to cmd while executing a LNK shortcut to "C:\Program Files\Python27\python.exe": 

    C:\Temp>.\python.lnk -c "import sys,time;time.sleep(60);sys.exit(42)"
    Breakpoint 0 hit
    SHELL32!ShellExecuteExW:
    00007ff9`a5710e20 48895c2408      mov     qword ptr [rsp+8],rbx
                                        ss:000000a3`eb3af3a0=000000a3eb597ca0
    0:000> ; as /x info @rcx
    0:000> ; as /x sz @@(*((unsigned long *)${info}))
    0:000> bd 2,3,4; pt; be 2,3,4

ShellExecuteEx returns the process handle:

    0:000> ?? *((void **)(${info} + ${sz} - 8)); * hProcess
    void * 0x00000000`000002fc
    0:000> !handle 2fc
    Handle 2fc
      Type          Process
    0:000> g

cmd uses the handle to read the ImageSubsystem type from the process environment block (PEB), for which 3 is a console process and 2 is a GUI process.

    Breakpoint 1 hit
    cmd!GetProcessSubsystemType:
    00007ff7`a133faf4 48895c2410      mov     qword ptr [rsp+10h],rbx
                                        ss:000000a3`eb3af458=000000a3eb585640
    0:000> g
    Breakpoint 2 hit
    KERNELBASE!ReadProcessMemory:
    00007ff9`a49ac230 4883ec48        sub     rsp,48h
    0:000> as /x buf @r8
    0:000> pt
    KERNELBASE!ReadProcessMemory+0x2b:
    00007ff9`a49ac25b c3              ret

    0:000> ?? ((ntdll!_PEB *)${buf})->ImageSubsystem
    unsigned long 3
    0:000> g

Since it's a console process, cmd waits and queries the exit code.

    Breakpoint 3 hit    
    KERNELBASE!WaitForSingleObject:
    00007ff9`a49840c0 4533c0          xor     r8d,r8d
    0:000> r rcx
    rcx=00000000000002fc
    0:000> g

    Breakpoint 4 hit
    KERNELBASE!GetExitCodeProcess:
    00007ff9`a49c46d0 4053            push    rbx
    0:000> as /x rc @rdx
    0:000> pt
    KERNELBASE!GetExitCodeProcess+0x3a:
    00007ff9`a49c470a c3              ret
    0:000> ?? *((unsigned long *)${rc})
    unsigned long 0x2a
    0:000> ? 0x2a
    Evaluate expression: 42 = 00000000`0000002a

It sets the exit code in the 'hidden' environment variable "=ExitCode" as a unsigned hexadecimal number.

    C:\Temp>echo %=ExitCode%
    0000002A

You can also query the signed value using the pseudo environment variable "errorlevel".

    C:\Temp>echo %errorlevel%
    42
History
Date User Action Args
2016-01-26 04:23:21eryksunsetrecipients: + eryksun, paul.moore, tim.golden, zach.ware, steve.dower, Ivan.Pozdeev
2016-01-26 04:23:21eryksunsetmessageid: <1453782201.5.0.210485589702.issue26189@psf.upfronthosting.co.za>
2016-01-26 04:23:21eryksunlinkissue26189 messages
2016-01-26 04:23:20eryksuncreate