Hello! This patch implements the ToolHelpHook and NotifyRegister notifications, NFY_STARTTASK, NFY_STARTDLL, NFY_LOADSEG, NFY_FREESEG, NFY_EXITTASK and NFY_DELMODULE, needed to fix bug 575 (SmartForms installer). I expect there to be problems with this patch (some of it is probably a bit over my head) so eyes are requested, please! I already know that the number and order of the segment messages (NFY_LOADSEG and FREESEG) doesn't exactly match those sent in Windows, and I'm not totally sure of the affect this patch will have on 16-bit memory and threading details in wine. Well anyway, here it goes... Changelog: Implemented NotifyRegister notifications, NFY_STARTTASK, NFY_STARTDLL, NFY_LOADSEG, NFY_FREESEG, NFY_EXITTASK and NFY_DELMODULE Modified Files: wine/dlls/kernel/toolhelp.c wine/dlls/kernel/toolhelp.spec wine/dlls/ntdll/Makefile.in wine/include/toolhelp.h wine/loader/task.c wine/loader/ne/module.c wine/loader/ne/segment.c wine/scheduler/thread.c License: GPL, X11 Josh Thielen
Index: wine/dlls/kernel/toolhelp.c =================================================================== RCS file: /home/wine/wine/dlls/kernel/toolhelp.c,v retrieving revision 1.17 diff -u -r1.17 toolhelp.c --- wine/dlls/kernel/toolhelp.c 31 May 2002 23:25:48 -0000 1.17 +++ wine/dlls/kernel/toolhelp.c 18 Aug 2002 21:20:32 -0000 @@ -27,18 +27,15 @@ #include "wine/winbase16.h" #include "winerror.h" #include "local.h" +#include "stackframe.h" #include "tlhelp32.h" #include "toolhelp.h" #include "wine/server.h" #include "wine/debug.h" +#include "miscemu.h" WINE_DEFAULT_DEBUG_CHANNEL(toolhelp); - -/* FIXME: to make this work, we have to call back all these registered - * functions from all over the WINE code. Someone with more knowledge than - * me please do that. -Marcus - */ static struct notify { HTASK16 htask; @@ -46,9 +43,96 @@ WORD wFlags; } *notifys = NULL; -static int nrofnotifys = 0; +static int num_notifys = 0; +/* ### start build ### */ +extern WORD CALLBACK TOOLHELP_CallTo16_word_wl(FARPROC16,WORD,DWORD); +/* ### stop build ### */ + +FARPROC16 ToolHelpHookProc = NULL; + +FARPROC16 WINAPI ToolHelpHook16(FARPROC16 lpfnNotifyHandler); + +void NotifyRegisterHook(CONTEXT86 *context) +{ + WORD wID = NFY_UNKNOWN; + DWORD dwData = 0; + NFYLOADSEG nfyls; + NFYSTARTDLL nfysd; + TEB *teb = NtCurrentTeb(); + HANDLE16 handle = 0; + LPVOID p; + int i; + + /* see INT 41h */ + switch(context->Eax) + { + case 0x50: + wID = NFY_LOADSEG; + nfyls.dwSize = sizeof(nfyls); + nfyls.wSelector = CX_reg(context); + nfyls.wSegNum = BX_reg(context); + nfyls.wType = SI_reg(context); + nfyls.wcInstance = DX_reg(context); + nfyls.lpstrModuleName = CTX_SEG_OFF_TO_LIN(context, + context->SegEs, context->Edi); + handle = GlobalAlloc16(0, sizeof(nfyls)); + p = GlobalLock16(handle); + memcpy(p, &nfyls, sizeof(nfyls)); + GlobalUnlock16(handle); + dwData = K32WOWGlobalLock16(handle); + break; + + case 0x52: + wID = NFY_FREESEG; + dwData = context->Ebx; /* selector of freed segment */ + break; + + case 0x59: + wID = NFY_STARTTASK; + dwData = (DWORD) CTX_SEG_OFF_TO_LIN(context, + context->SegCs, context->Esi); + break; + + case 0x62: + wID = NFY_EXITTASK; + dwData = *(BYTE *)MapSL((SEGPTR)teb->cur_stack + 6); + stack16_pop(6); + NotifyUnRegister16(GetCurrentTask()); + break; + + case 0x64: + wID = NFY_STARTDLL; + nfysd.dwSize = sizeof(NFYSTARTDLL); + nfysd.hModule = SI_reg(context); + nfysd.wCS = context->Ecx; + nfysd.wIP = context->Ebx; + handle = GlobalAlloc16(0, sizeof(nfysd)); + p = GlobalLock16(handle); + memcpy(p, &nfysd, sizeof(nfysd)); + GlobalUnlock16(handle); + dwData = K32WOWGlobalLock16(handle); + break; + + case 0x65: + wID = NFY_DELMODULE; + dwData = context->SegEs; + break; + + default: + wID = NFY_UNKNOWN; + } -static FARPROC16 HookNotify = NULL; + /* Call the registered notification handlers */ + for(i = 0; i < num_notifys; i++) + { + TOOLHELP_CallTo16_word_wl(notifys[i].lpfnCallback, wID, dwData); + } + + if(handle) { + GlobalUnlock16(handle); + GlobalFree16(handle); + } +} /*********************************************************************** * NotifyRegister (TOOLHELP.73) @@ -56,48 +140,75 @@ BOOL16 WINAPI NotifyRegister16( HTASK16 htask, FARPROC16 lpfnCallback, WORD wFlags ) { - int i; + int i; - FIXME("(%x,%lx,%x), semi-stub.\n", + FIXME("(%x,%lx,%x), not all notifications are implemented.\n", htask, (DWORD)lpfnCallback, wFlags ); + if(!num_notifys) + { + FARPROC16 proc = GetProcAddress16(GetModuleHandle16("TOOLHELP"), + "NotifyRegisterHook"); + ToolHelpHook16(proc); + } + if (!htask) htask = GetCurrentTask(); - for (i=0;i<nrofnotifys;i++) - if (notifys[i].htask==htask) + for (i = 0; i < num_notifys; i++) + { + if (notifys[i].htask == htask) + { break; - if (i==nrofnotifys) { - if (notifys==NULL) - notifys=(struct notify*)HeapAlloc( GetProcessHeap(), 0, - sizeof(struct notify) ); - else - notifys=(struct notify*)HeapReAlloc( GetProcessHeap(), 0, notifys, - sizeof(struct notify)*(nrofnotifys+1)); + } + } + if (i == num_notifys) + { + if (notifys == NULL) + { + notifys = (struct notify*)HeapAlloc( GetProcessHeap(), 0, + sizeof(struct notify) ); + } + else + { + notifys = (struct notify*)HeapReAlloc( GetProcessHeap(), 0, + notifys, sizeof(struct notify) * (num_notifys + 1)); + } if (!notifys) return FALSE; - nrofnotifys++; + num_notifys++; } - notifys[i].htask=htask; - notifys[i].lpfnCallback=lpfnCallback; - notifys[i].wFlags=wFlags; + notifys[i].htask = htask; + notifys[i].lpfnCallback = lpfnCallback; + notifys[i].wFlags = wFlags; return TRUE; } /*********************************************************************** - * NotifyUnregister (TOOLHELP.74) + * NotifyUnRegister (TOOLHELP.74) */ -BOOL16 WINAPI NotifyUnregister16( HTASK16 htask ) +BOOL16 WINAPI NotifyUnRegister16( HTASK16 htask ) { int i; - FIXME("(%x), semi-stub.\n", htask ); if (!htask) htask = GetCurrentTask(); - for (i=nrofnotifys;i--;) - if (notifys[i].htask==htask) + for (i = 0; i < num_notifys; i++) + { + if (notifys[i].htask == htask) + { break; - if (i==-1) + } + } + if (i == num_notifys) + { return FALSE; - memcpy(notifys+i,notifys+(i+1),sizeof(struct notify)*(nrofnotifys-i-1)); - notifys=(struct notify*)HeapReAlloc( GetProcessHeap(), 0, notifys, - (nrofnotifys-1)*sizeof(struct notify)); - nrofnotifys--; + } + memcpy(notifys+i, notifys+i+1, sizeof(struct notify)*(num_notifys-i-1)); + notifys = (struct notify *) HeapReAlloc( GetProcessHeap(), 0, notifys, + (num_notifys-1) * sizeof(struct notify)); + num_notifys--; + + if(!num_notifys) + { + /* remove hook proc */ + ToolHelpHook16(0); + } return TRUE; } @@ -191,9 +302,9 @@ { FARPROC16 tmp; - FIXME("(%p), stub.\n", lpfnNotifyHandler); - tmp = HookNotify; - HookNotify = lpfnNotifyHandler; + tmp = ToolHelpHookProc; + ToolHelpHookProc = lpfnNotifyHandler; + TRACE("installed tool help hook function %p\n", ToolHelpHookProc); /* just return previously installed notification function */ return tmp; } Index: wine/dlls/kernel/toolhelp.spec =================================================================== RCS file: /home/wine/wine/dlls/kernel/toolhelp.spec,v retrieving revision 1.5 diff -u -r1.5 toolhelp.spec --- wine/dlls/kernel/toolhelp.spec 21 Jun 2002 19:15:47 -0000 1.5 +++ wine/dlls/kernel/toolhelp.spec 18 Aug 2002 21:20:32 -0000 @@ -25,7 +25,7 @@ 71 pascal16 SystemHeapInfo(ptr) SystemHeapInfo16 72 pascal16 MemManInfo(ptr) MemManInfo16 73 pascal16 NotifyRegister(word segptr word) NotifyRegister16 -74 pascal16 NotifyUnregister(word) NotifyUnregister16 +74 pascal16 NotifyUnRegister(word) NotifyUnRegister16 75 pascal16 InterruptRegister(word segptr) InterruptRegister16 76 pascal16 InterruptUnRegister(word) InterruptUnRegister16 77 pascal16 TerminateApp(word word) TerminateApp16 @@ -38,3 +38,5 @@ 84 pascal16 Local32Info(ptr word) Local32Info16 85 pascal16 Local32First(ptr word) Local32First16 86 pascal16 Local32Next(ptr) Local32Next16 +# wine-specific +100 pascal16 -register NotifyRegisterHook() NotifyRegisterHook Index: wine/dlls/ntdll/Makefile.in =================================================================== RCS file: /home/wine/wine/dlls/ntdll/Makefile.in,v retrieving revision 1.31 diff -u -r1.31 Makefile.in --- wine/dlls/ntdll/Makefile.in 1 Aug 2002 18:36:58 -0000 1.31 +++ wine/dlls/ntdll/Makefile.in 18 Aug 2002 21:20:37 -0000 @@ -7,6 +7,7 @@ EXTRALIBS = $(LIBUNICODE) C_SRCS = \ + $(TOPOBJDIR)/dlls/kernel/toolhelp.c \ $(TOPOBJDIR)/files/change.c \ $(TOPOBJDIR)/files/directory.c \ $(TOPOBJDIR)/files/dos_fs.c \ =================================================================== RCS file: /home/wine/wine/include/task.h,v retrieving revision 1.30 diff -u -r1.30 task.h --- wine/include/task.h 31 May 2002 23:06:49 -0000 1.30 +++ wine/include/task.h 18 Aug 2002 21:21:20 -0000 @@ -162,7 +162,7 @@ extern void TASK_CreateMainTask(void); extern HTASK16 TASK_SpawnTask( struct _NE_MODULE *pModule, WORD cmdShow, LPCSTR cmdline, BYTE len, HANDLE *hThread ); -extern void TASK_ExitTask(void); +extern void TASK_ExitTask(DWORD code); extern HTASK16 TASK_GetNextTask( HTASK16 hTask ); extern TDB *TASK_GetPtr( HTASK16 hTask ); extern TDB *TASK_GetCurrent(void); Index: wine/include/toolhelp.h =================================================================== RCS file: /home/wine/wine/include/toolhelp.h,v retrieving revision 1.13 diff -u -r1.13 toolhelp.h --- wine/include/toolhelp.h 10 Mar 2002 00:02:34 -0000 1.13 +++ wine/include/toolhelp.h 18 Aug 2002 21:21:21 -0000 @@ -328,6 +328,7 @@ #define NF_RIP 2 /* get debugerrors of system */ BOOL16 WINAPI NotifyRegister16(HTASK16 htask,FARPROC16 lpfnCallback,WORD wFlags); +BOOL16 WINAPI NotifyUnRegister16(HTASK16 hTask); #define NFY_UNKNOWN 0 #define NFY_LOADSEG 1 @@ -338,6 +339,7 @@ WORD wSegNum; WORD wType; /* bit 0 set if this is a code segment */ WORD wcInstance; /* only valid for data segment */ + LPCSTR lpstrModuleName; } NFYLOADSEG; /* called when freeing a segment. LOWORD(dwData) is the freed selector */ #define NFY_FREESEG 2 @@ -385,7 +387,7 @@ #define NFY_INCHAR 10 /* output debugstring (pointed to by dwData) */ -#define NFY_OUTSTRING 11 +#define NFY_OUTSTR 11 /* log errors */ #define NFY_LOGERROR 12 Index: wine/loader/task.c =================================================================== RCS file: /home/wine/wine/loader/task.c,v retrieving revision 1.120 diff -u -r1.120 task.c --- wine/loader/task.c 28 Jul 2002 23:48:28 -0000 1.120 +++ wine/loader/task.c 18 Aug 2002 21:21:30 -0000 @@ -56,6 +56,7 @@ /* Min. number of thunks allocated when creating a new segment */ #define MIN_THUNKS 32 +extern FARPROC16 ToolHelpHookProc; static THHOOK DefaultThhook; THHOOK *pThhook = &DefaultThhook; @@ -338,10 +339,7 @@ pTask->pdb.cmdLine[0] = len; memcpy( pTask->pdb.cmdLine + 1, cmdline, len ); /* pTask->pdb.cmdLine[len+1] = 0; */ - - TRACE("module='%s' cmdline='%.*s' task=%04x\n", name, len, cmdline, hTask ); - - /* Get the compatibility flags */ + /* Get the compatibility flags */ pTask->compat_flags = GetProfileIntA( "Compatibility", name, 0 ); @@ -468,10 +466,28 @@ /*********************************************************************** * TASK_ExitTask */ -void TASK_ExitTask(void) +void TASK_ExitTask(DWORD code) { TDB *pTask; DWORD lockCount; + CONTEXT86 context; + SEGPTR ptr; + + /* send notification */ + if(ToolHelpHookProc) + { + TRACE_(toolhelp)("Sending termination notification\n"); + memset(&context, 0, sizeof(context)); + context.SegCs = SELECTOROF(ToolHelpHookProc); + context.Eip = OFFSETOF(ToolHelpHookProc); + context.Ebp = OFFSETOF(NtCurrentTeb()->cur_stack) + + (WORD)&((STACK16FRAME*)0)->bp; + context.Eax = 0x62; + ptr = stack16_push(6); + *(BYTE *)MapSL(ptr) = LOBYTE(code); + context.Esp -= 6; + wine_call_to_16_regs_short( &context, 0 ); + } /* Enter the Win16Lock to protect global data structures */ _EnterWin16Lock(); @@ -516,10 +532,10 @@ if ( hLockedTask == pTask->hSelf ) hLockedTask = 0; - TASK_DeleteTask( pTask->hSelf ); - /* ... and completely release the Win16Lock, just in case. */ ReleaseThunkLock( &lockCount ); + + TASK_DeleteTask( pTask->hSelf ); } @@ -1535,6 +1555,7 @@ ERR( "%s\n", debugstr_a(str) ); } done: + NotifyUnRegister16(pTask->hSelf); ExitThread(0xff); } Index: wine/loader/ne/module.c =================================================================== RCS file: /home/wine/wine/loader/ne/module.c,v retrieving revision 1.116 diff -u -r1.116 module.c --- wine/loader/ne/module.c 31 Jul 2002 19:26:04 -0000 1.116 +++ wine/loader/ne/module.c 18 Aug 2002 21:21:36 -0000 @@ -42,6 +42,7 @@ WINE_DEFAULT_DEBUG_CHANNEL(module); WINE_DECLARE_DEBUG_CHANNEL(loaddll); +WINE_DECLARE_DEBUG_CHANNEL(toolhelp); /* * Segment table entry @@ -56,6 +57,7 @@ #define hFirstModule (pThhook->hExeHead) +extern FARPROC16 ToolHelpHookProc; static NE_MODULE *pCachedModule = 0; /* Module cached by NE_OpenFile */ static HINSTANCE16 NE_LoadModule( LPCSTR name, BOOL lib_only ); @@ -1187,6 +1189,22 @@ sp -= sizeof(STACK16FRAME); pTask->teb->cur_stack = MAKESEGPTR( GlobalHandleToSel16(hInstance), sp ); + /* send notification */ + if(ToolHelpHookProc) + { + TRACE_(toolhelp)("Sending start task notification\n"); + pSegTable = NE_SEG_TABLE( pModule ); + memset(&context, 0, sizeof(context)); + context.SegCs = SELECTOROF(ToolHelpHookProc); + context.Eip = OFFSETOF(ToolHelpHookProc); + context.Ebp = OFFSETOF(NtCurrentTeb()->cur_stack) + + (WORD)&((STACK16FRAME*)0)->bp; + context.Eax = 0x59; + context.Ecx = pModule->cs; + context.Ebx = pModule->ip; + wine_call_to_16_regs_short( &context, 0 ); + } + /* Registers at initialization must be: * ax zero * bx stack size in bytes @@ -1245,6 +1263,38 @@ return NE_CallTo16_word_w( WEP, WEP_FREE_DLL ); } +/********************************************************************** + * NE_FreeModuleSegments + * + * Does not actually free the segment memory. Simply sends the free + * segment notification for each segment in the module. + */ +void NE_FreeModuleSegments(NE_MODULE *pModule) +{ + SEGTABLEENTRY *pSegTable, *pSeg; + CONTEXT86 context; + int i; + + pSegTable = NE_SEG_TABLE( pModule ); + for(i = 0; i < pModule->seg_count; i++) { + memset(&context, 0, sizeof(context)); + pSeg = pSegTable + i; + + /* send notification */ + if(ToolHelpHookProc) { + TRACE_(toolhelp)("sending free segment notification\n"); + memset(&context, 0, sizeof(context)); + context.SegCs = SELECTOROF(ToolHelpHookProc); + context.Eip = OFFSETOF(ToolHelpHookProc); + context.Ebp = OFFSETOF(NtCurrentTeb()->cur_stack) + + (WORD)&((STACK16FRAME*)0)->bp; + context.Eax = 0x52; + context.Ebx = GlobalHandleToSel16( pSeg->hSeg ); + context.SegEs = pModule->self; + wine_call_to_16_regs_short(&context, 0); + } + } +} /********************************************************************** * NE_FreeModule @@ -1256,6 +1306,7 @@ HMODULE16 *hPrevModule; NE_MODULE *pModule; HMODULE16 *pModRef; + CONTEXT86 context; int i; if (!(pModule = NE_GetPtr( hModule ))) return FALSE; @@ -1266,6 +1317,22 @@ if (((INT16)(--pModule->count)) > 0 ) return TRUE; else pModule->count = 0; + /* send notifications */ + if(ToolHelpHookProc) { + TRACE_(toolhelp)("sending toolhelp notification to hook proc %p\n", ToolHelpHookProc); + memset(&context, 0, sizeof(context)); + context.SegCs = SELECTOROF(ToolHelpHookProc); + context.Eip = OFFSETOF(ToolHelpHookProc); + context.Ebp = OFFSETOF(NtCurrentTeb()->cur_stack) + + (WORD)&((STACK16FRAME*)0)->bp; + context.Eax = 0x65; + context.Ebx = GlobalHandleToSel16( pModule->self ); + context.SegEs = pModule->self; + wine_call_to_16_regs_short(&context, 0); + } + + NE_FreeModuleSegments(pModule); + if (pModule->flags & NE_FFLAGS_BUILTIN) return FALSE; /* Can't free built-in module */ Index: wine/loader/ne/segment.c =================================================================== RCS file: /home/wine/wine/loader/ne/segment.c,v retrieving revision 1.46 diff -u -r1.46 segment.c --- wine/loader/ne/segment.c 31 Jul 2002 17:20:01 -0000 1.46 +++ wine/loader/ne/segment.c 18 Aug 2002 21:21:40 -0000 @@ -42,6 +42,7 @@ WINE_DECLARE_DEBUG_CHANNEL(fixup); WINE_DECLARE_DEBUG_CHANNEL(module); WINE_DECLARE_DEBUG_CHANNEL(segment); +WINE_DECLARE_DEBUG_CHANNEL(toolhelp); /* * Relocation table entry @@ -76,6 +77,7 @@ #define SEL(x) ((x)|1) +extern FARPROC16 ToolHelpHookProc; static void NE_FixupSegmentPrologs(NE_MODULE *pModule, WORD segnum); /* ### start build ### */ @@ -113,14 +115,17 @@ FARPROC16 address = 0; HANDLE hf; DWORD res; + LPSTR lpstrModuleName; struct relocation_entry_s *rep, *reloc_entries; BYTE *func_name; int size; char* mem; - char buffer[256]; int ordinal, additive; - unsigned short *sp; + unsigned short *sp; + CONTEXT86 context; + + lpstrModuleName = (char *) pModule + pModule->name_table + 1, pSegTable = NE_SEG_TABLE( pModule ); pSeg = pSegTable + segnum - 1; @@ -143,6 +148,27 @@ hf = NE_OpenFile( pModule ); TRACE_(module)("Loading segment %d, hSeg=%04x, flags=%04x\n", segnum, pSeg->hSeg, pSeg->flags ); + + /* send notification */ + if(ToolHelpHookProc) + { + TRACE_(toolhelp)("sending segment load notification\n"); + /* send segment load notification */ + memset(&context, 0, sizeof(context)); + context.SegCs = SELECTOROF(ToolHelpHookProc); + context.Eip = OFFSETOF(ToolHelpHookProc); + context.Ebp = OFFSETOF(NtCurrentTeb()->cur_stack) + + (WORD)&((STACK16FRAME*)0)->bp; + context.Eax = 0x50; + context.Ebx = segnum; + context.Ecx = GlobalHandleToSel16(pSeg->hSeg); + context.Edx = pModule->self; + context.SegDs = SELECTOROF(lpstrModuleName); + context.Edi = OFFSETOF(lpstrModuleName); + context.Esi = pSeg->flags; + wine_call_to_16_regs_short(&context, 0); + } + SetFilePointer( hf, pSeg->filepos << pModule->alignment, NULL, SEEK_SET ); if (pSeg->size) size = pSeg->size; else size = pSeg->minsize ? pSeg->minsize : 0x10000; @@ -644,9 +670,6 @@ /* Call USER signal handler for Win3.1 compatibility. */ TASK_CallTaskSignalProc( USIG16_DLL_LOAD, pModule->self ); - if (!pModule->cs) return TRUE; /* no initialization code */ - - /* Registers at initialization must be: * cx heap size * di library instance @@ -656,6 +679,32 @@ memset( &context, 0, sizeof(context) ); + /* send notification */ + if(ToolHelpHookProc) + { + TRACE_(toolhelp)("sending start DLL notification\n"); + memset(&context, 0, sizeof(context)); + context.SegCs = SELECTOROF(ToolHelpHookProc); + context.Eip = OFFSETOF(ToolHelpHookProc); + context.Ebp = OFFSETOF(NtCurrentTeb()->cur_stack) + + (WORD)&((STACK16FRAME*)0)->bp; + context.Eax = 0x64; + if (!pModule->cs) + { + context.Ecx = 0; + context.Ebx = 0; + } + else + { + context.Ecx = SEL(pSegTable[pModule->cs-1].hSeg); + context.Ebx = pModule->ip; + } + context.Esi = pModule->self; + wine_call_to_16_regs_short(&context, 0); + } + + if (!pModule->cs) return TRUE; /* no initialization code */ + NE_GetDLLInitParams( pModule, &hInst, &ds, &heap ); context.Ecx = heap; @@ -667,12 +716,13 @@ context.Eip = pModule->ip; context.Ebp = OFFSETOF(NtCurrentTeb()->cur_stack) + (WORD)&((STACK16FRAME*)0)->bp; - pModule->cs = 0; /* Don't initialize it twice */ + TRACE_(dll)("Calling LibMain, cs:ip=%04lx:%04lx ds=%04lx di=%04x cx=%04x\n", context.SegCs, context.Eip, context.SegDs, LOWORD(context.Edi), LOWORD(context.Ecx) ); wine_call_to_16_regs_short( &context, 0 ); + return TRUE; } Index: wine/scheduler/thread.c =================================================================== RCS file: /home/wine/wine/scheduler/thread.c,v retrieving revision 1.119 diff -u -r1.119 thread.c --- wine/scheduler/thread.c 31 Jul 2002 19:26:05 -0000 1.119 +++ wine/scheduler/thread.c 18 Aug 2002 21:21:54 -0000 @@ -377,7 +377,8 @@ else { MODULE_DllThreadDetach( NULL ); - if (!(NtCurrentTeb()->tibflags & TEBF_WIN32)) TASK_ExitTask(); + TRACE("Exit code=%08x\n", code); + if (!(NtCurrentTeb()->tibflags & TEBF_WIN32)) TASK_ExitTask(code); SYSDEPS_ExitThread( code ); } }