Exit process if the primary thread is detached

Processes on Windows 10 that return rather than call `ExitProcess` may
have a 30 second delay before terminating.  This seems due to the
console creating a thread.  Detect a detached primary thread and
explicitly call `ExitProcess`.
This commit is contained in:
Jason Hood 2018-05-02 18:12:57 +10:00
parent 3c97a6e4e7
commit d7a2be9964
2 changed files with 43 additions and 11 deletions

40
ANSI.c
View File

@ -197,11 +197,12 @@
v1.83, 16 February, 2018: v1.83, 16 February, 2018:
create the flush thread on first use. create the flush thread on first use.
v1.84-wip, 17 February, 26 to 30 April, 2018: v1.84-wip, 17 February, 26 April to 2 May, 2018:
close the flush handles on detach; close the flush handles on detach;
dynamically load WINMM.DLL; dynamically load WINMM.DLL;
use sprintf/_snprintf/_snwprintf instead of wsprintf, avoiding USER32.DLL; use sprintf/_snprintf/_snwprintf instead of wsprintf, avoiding USER32.DLL;
replace bsearch (in procrva.c) with specific code. replace bsearch (in procrva.c) with specific code;
if the primary thread is detached exit the process.
*/ */
#include "ansicon.h" #include "ansicon.h"
@ -3859,6 +3860,19 @@ void OriginalAttr( PVOID lpReserved )
} }
// A Win10 process that returns (rather than calling ExitProcess) may have a 30
// second delay before terminating. This seems due to another thread being
// created for the console. If the primary thread is detached, wait for it to
// finish, then explicitly exit.
DWORD WINAPI exit_thread( LPVOID lpParameter )
{
DWORD rc;
WaitForSingleObject( lpParameter, 30000 );
GetExitCodeThread( lpParameter, &rc );
ExitProcess( rc );
}
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
// DllMain() // DllMain()
// Function called by the system when processes and threads are initialized // Function called by the system when processes and threads are initialized
@ -3874,6 +3888,7 @@ BOOL WINAPI DllMain( HINSTANCE hInstance, DWORD dwReason, LPVOID lpReserved )
TCHAR logstr[4]; TCHAR logstr[4];
typedef LONG (WINAPI *PNTQIT)( HANDLE, int, PVOID, ULONG, PULONG ); typedef LONG (WINAPI *PNTQIT)( HANDLE, int, PVOID, ULONG, PULONG );
static PNTQIT NtQueryInformationThread; static PNTQIT NtQueryInformationThread;
static DWORD primary_tid;
if (dwReason == DLL_PROCESS_ATTACH) if (dwReason == DLL_PROCESS_ATTACH)
{ {
@ -3917,11 +3932,16 @@ BOOL WINAPI DllMain( HINSTANCE hInstance, DWORD dwReason, LPVOID lpReserved )
NtQueryInformationThread = (PNTQIT)GetProcAddress( NtQueryInformationThread = (PNTQIT)GetProcAddress(
GetModuleHandle( L"ntdll.dll" ), "NtQueryInformationThread" ); GetModuleHandle( L"ntdll.dll" ), "NtQueryInformationThread" );
if (NtQueryInformationThread == NULL)
DisableThreadLibraryCalls( hInstance );
InitializeCriticalSection( &CritSect ); InitializeCriticalSection( &CritSect );
hFlushTimer = CreateWaitableTimer( NULL, FALSE, NULL ); hFlushTimer = CreateWaitableTimer( NULL, FALSE, NULL );
// If it's a static load, assume this is the primary thread.
if (lpReserved)
primary_tid = GetCurrentThreadId();
if (NtQueryInformationThread == NULL && primary_tid == 0)
DisableThreadLibraryCalls( hInstance );
} }
else if (dwReason == DLL_PROCESS_DETACH) else if (dwReason == DLL_PROCESS_DETACH)
{ {
@ -3964,7 +3984,17 @@ BOOL WINAPI DllMain( HINSTANCE hInstance, DWORD dwReason, LPVOID lpReserved )
else if (dwReason == DLL_THREAD_DETACH) else if (dwReason == DLL_THREAD_DETACH)
{ {
PVOID start; PVOID start;
if (NtQueryInformationThread( GetCurrentThread(), if (primary_tid && GetCurrentThreadId() == primary_tid)
{
HANDLE hThread;
DuplicateHandle( GetCurrentProcess(), GetCurrentThread(),
GetCurrentProcess(), &hThread,
0, FALSE, DUPLICATE_SAME_ACCESS );
CloseHandle( CreateThread( NULL, 4096, exit_thread, hThread, 0, NULL ) );
DEBUGSTR( 1, "Primary thread detached, exiting process" );
}
else if (NtQueryInformationThread &&
NtQueryInformationThread( GetCurrentThread(),
9 /* ThreadQuerySetWin32StartAddress */, 9 /* ThreadQuerySetWin32StartAddress */,
&start, sizeof(start), NULL ) == 0 &start, sizeof(start), NULL ) == 0
&& (start == Hooks[0].oldfunc || start == Hooks[1].oldfunc && (start == Hooks[0].oldfunc || start == Hooks[1].oldfunc

View File

@ -339,9 +339,11 @@ Version History
Legend: + added, - bug-fixed, * changed. Legend: + added, - bug-fixed, * changed.
1.84-wip - 30 April, 2018: 1.84-wip - 2 May, 2018:
- close the flush handles on detach; - close the flush handles on detach;
* remove dependency on USER32, dynamically load WINMM. * remove dependency on USER32, dynamically load WINMM;
* exit process if the primary thread is detached (for processes on Win10
that return, rather than call ExitProcess).
1.83 - 16 February, 2018: 1.83 - 16 February, 2018:
- create the flush thread on first use. - create the flush thread on first use.
@ -616,5 +618,5 @@ Distribution
in LICENSE.txt. in LICENSE.txt.
=========================== ========================
Jason Hood, 30 April, 2018. Jason Hood, 2 May, 2018.