Reactos
1/*
2 * Win32 debugger functions
3 *
4 * Copyright (C) 1999 Alexandre Julliard
5 *
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
10 *
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19 */
20
21#include <stdio.h>
22#include <string.h>
23#include <stdlib.h>
24
25#include "ntstatus.h"
26#define WIN32_NO_STATUS
27#include "windef.h"
28#include "winbase.h"
29#include "winternl.h"
30#include "winnls.h"
31#include "wingdi.h"
32#include "winuser.h"
33#define PSAPI_VERSION 1 /* avoid K32 function remapping */
34#include "psapi.h"
35#include "werapi.h"
36
37#include "wine/exception.h"
38#include "wine/asm.h"
39#include "kernelbase.h"
40#include "wine/debug.h"
41
42WINE_DEFAULT_DEBUG_CHANNEL(seh);
43WINE_DECLARE_DEBUG_CHANNEL(winedbg);
44
45typedef INT (WINAPI *MessageBoxA_funcptr)(HWND,LPCSTR,LPCSTR,UINT);
46typedef INT (WINAPI *MessageBoxW_funcptr)(HWND,LPCWSTR,LPCWSTR,UINT);
47
48static PTOP_LEVEL_EXCEPTION_FILTER top_filter;
49
50void *dummy = RtlUnwind; /* force importing RtlUnwind from ntdll */
51
52/***********************************************************************
53 * CheckRemoteDebuggerPresent (kernelbase.@)
54 */
55BOOL WINAPI DECLSPEC_HOTPATCH CheckRemoteDebuggerPresent( HANDLE process, BOOL *present )
56{
57 DWORD_PTR port;
58
59 if (!process || !present)
60 {
61 SetLastError( ERROR_INVALID_PARAMETER );
62 return FALSE;
63 }
64 if (!set_ntstatus( NtQueryInformationProcess( process, ProcessDebugPort, &port, sizeof(port), NULL )))
65 return FALSE;
66 *present = !!port;
67 return TRUE;
68}
69
70
71/**********************************************************************
72 * ContinueDebugEvent (kernelbase.@)
73 */
74BOOL WINAPI DECLSPEC_HOTPATCH ContinueDebugEvent( DWORD pid, DWORD tid, DWORD status )
75{
76 CLIENT_ID id;
77
78 id.UniqueProcess = ULongToHandle( pid );
79 id.UniqueThread = ULongToHandle( tid );
80 return set_ntstatus( DbgUiContinue( &id, status ));
81}
82
83
84/**********************************************************************
85 * DebugActiveProcess (kernelbase.@)
86 */
87BOOL WINAPI DECLSPEC_HOTPATCH DebugActiveProcess( DWORD pid )
88{
89 HANDLE process;
90 NTSTATUS status;
91
92 if (!set_ntstatus( DbgUiConnectToDbg() )) return FALSE;
93 if (!(process = OpenProcess( PROCESS_VM_READ | PROCESS_VM_WRITE | PROCESS_SUSPEND_RESUME |
94 PROCESS_QUERY_INFORMATION | PROCESS_CREATE_THREAD, FALSE, pid )))
95 return FALSE;
96 status = DbgUiDebugActiveProcess( process );
97 NtClose( process );
98 return set_ntstatus( status );
99}
100
101
102/**********************************************************************
103 * DebugActiveProcessStop (kernelbase.@)
104 */
105BOOL WINAPI DECLSPEC_HOTPATCH DebugActiveProcessStop( DWORD pid )
106{
107 HANDLE process;
108 NTSTATUS status;
109
110 if (!(process = OpenProcess( PROCESS_VM_READ | PROCESS_VM_WRITE | PROCESS_SUSPEND_RESUME, FALSE, pid )))
111 return FALSE;
112 status = DbgUiStopDebugging( process );
113 NtClose( process );
114 return set_ntstatus( status );
115}
116
117
118/***********************************************************************
119 * DebugBreak (kernelbase.@)
120 */
121#ifdef __i386__
122__ASM_STDCALL_FUNC( DebugBreak, 0, "jmp " __ASM_STDCALL("DbgBreakPoint", 0) )
123#elif defined(__aarch64__)
124__ASM_GLOBAL_FUNC( DebugBreak, "brk #0xf000; ret" )
125#elif defined(__arm64ec__)
126void __attribute__((naked)) WINAPI DebugBreak(void) { asm( "brk #0xf000; ret" ); }
127#elif defined(__x86_64__)
128__ASM_GLOBAL_FUNC( DebugBreak, "jmp " __ASM_NAME("DbgBreakPoint") )
129#elif defined(__arm__)
130__ASM_GLOBAL_FUNC( DebugBreak, "udf #0xfe; bx lr" )
131#endif
132
133
134/**************************************************************************
135 * FatalAppExitA (kernelbase.@)
136 */
137void WINAPI DECLSPEC_HOTPATCH FatalAppExitA( UINT action, LPCSTR str )
138{
139 HMODULE mod = GetModuleHandleA( "user32.dll" );
140 MessageBoxA_funcptr pMessageBoxA = NULL;
141
142 if (mod) pMessageBoxA = (MessageBoxA_funcptr)GetProcAddress( mod, "MessageBoxA" );
143 if (pMessageBoxA) pMessageBoxA( 0, str, NULL, MB_SYSTEMMODAL | MB_OK );
144 else ERR( "%s\n", debugstr_a(str) );
145 RtlExitUserProcess( 1 );
146}
147
148
149/**************************************************************************
150 * FatalAppExitW (kernelbase.@)
151 */
152void WINAPI DECLSPEC_HOTPATCH FatalAppExitW( UINT action, LPCWSTR str )
153{
154 HMODULE mod = GetModuleHandleW( L"user32.dll" );
155 MessageBoxW_funcptr pMessageBoxW = NULL;
156
157 if (mod) pMessageBoxW = (MessageBoxW_funcptr)GetProcAddress( mod, "MessageBoxW" );
158 if (pMessageBoxW) pMessageBoxW( 0, str, NULL, MB_SYSTEMMODAL | MB_OK );
159 else ERR( "%s\n", debugstr_w(str) );
160 RtlExitUserProcess( 1 );
161}
162
163
164/***********************************************************************
165 * IsDebuggerPresent (kernelbase.@)
166 */
167BOOL WINAPI IsDebuggerPresent(void)
168{
169 return NtCurrentTeb()->Peb->BeingDebugged;
170}
171
172
173static LONG WINAPI debug_exception_handler( EXCEPTION_POINTERS *eptr )
174{
175 EXCEPTION_RECORD *rec = eptr->ExceptionRecord;
176 return (rec->ExceptionCode == DBG_PRINTEXCEPTION_C) ? EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH;
177}
178
179/***********************************************************************
180 * OutputDebugStringA (kernelbase.@)
181 */
182void WINAPI DECLSPEC_HOTPATCH OutputDebugStringA( LPCSTR str )
183{
184 static HANDLE DBWinMutex = NULL;
185 static BOOL mutex_inited = FALSE;
186 BOOL caught_by_dbg = TRUE;
187
188 if (!str) str = "";
189 WARN( "%s\n", debugstr_a(str) );
190
191 /* raise exception, WaitForDebugEvent() will generate a corresponding debug event */
192 __TRY
193 {
194 ULONG_PTR args[2];
195 args[0] = strlen(str) + 1;
196 args[1] = (ULONG_PTR)str;
197 RaiseException( DBG_PRINTEXCEPTION_C, 0, 2, args );
198 }
199 __EXCEPT(debug_exception_handler)
200 {
201 caught_by_dbg = FALSE;
202 }
203 __ENDTRY
204 if (caught_by_dbg) return;
205
206 /* send string to a system-wide monitor */
207 if (!mutex_inited)
208 {
209 /* first call to OutputDebugString, initialize mutex handle */
210 HANDLE mutex = CreateMutexExW( NULL, L"DBWinMutex", 0, SYNCHRONIZE );
211 if (mutex)
212 {
213 if (InterlockedCompareExchangePointer( &DBWinMutex, mutex, 0 ) != 0)
214 /* someone beat us here... */
215 CloseHandle( mutex );
216 }
217 mutex_inited = TRUE;
218 }
219
220 if (DBWinMutex)
221 {
222 HANDLE mapping;
223
224 mapping = OpenFileMappingW( FILE_MAP_WRITE, FALSE, L"DBWIN_BUFFER" );
225 if (mapping)
226 {
227 LPVOID buffer;
228 HANDLE eventbuffer, eventdata;
229
230 buffer = MapViewOfFile( mapping, FILE_MAP_WRITE, 0, 0, 0 );
231 eventbuffer = OpenEventW( SYNCHRONIZE, FALSE, L"DBWIN_BUFFER_READY" );
232 eventdata = OpenEventW( EVENT_MODIFY_STATE, FALSE, L"DBWIN_DATA_READY" );
233
234 if (buffer && eventbuffer && eventdata)
235 {
236 /* monitor is present, synchronize with other OutputDebugString invocations */
237 WaitForSingleObject( DBWinMutex, INFINITE );
238
239 /* acquire control over the buffer */
240 if (WaitForSingleObject( eventbuffer, 10000 ) == WAIT_OBJECT_0)
241 {
242 int str_len = strlen( str );
243 struct _mon_buffer_t
244 {
245 DWORD pid;
246 char buffer[1];
247 } *mon_buffer = (struct _mon_buffer_t*) buffer;
248
249 if (str_len > (4096 - sizeof(DWORD) - 1)) str_len = 4096 - sizeof(DWORD) - 1;
250 mon_buffer->pid = GetCurrentProcessId();
251 memcpy( mon_buffer->buffer, str, str_len );
252 mon_buffer->buffer[str_len] = 0;
253
254 /* signal data ready */
255 SetEvent( eventdata );
256 }
257 ReleaseMutex( DBWinMutex );
258 }
259
260 if (buffer) UnmapViewOfFile( buffer );
261 if (eventbuffer) CloseHandle( eventbuffer );
262 if (eventdata) CloseHandle( eventdata );
263 CloseHandle( mapping );
264 }
265 }
266}
267
268static LONG WINAPI debug_exception_handler_wide( EXCEPTION_POINTERS *eptr )
269{
270 EXCEPTION_RECORD *rec = eptr->ExceptionRecord;
271 return (rec->ExceptionCode == DBG_PRINTEXCEPTION_WIDE_C) ? EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH;
272}
273
274/***********************************************************************
275 * OutputDebugStringW (kernelbase.@)
276 */
277void WINAPI DECLSPEC_HOTPATCH OutputDebugStringW( LPCWSTR str )
278{
279 UNICODE_STRING strW;
280 STRING strA;
281
282 WARN( "%s\n", debugstr_w(str) );
283
284 RtlInitUnicodeString( &strW, str );
285 if (!RtlUnicodeStringToAnsiString( &strA, &strW, TRUE ))
286 {
287 BOOL exc_handled;
288
289 __TRY
290 {
291 ULONG_PTR args[4];
292 args[0] = wcslen(str) + 1;
293 args[1] = (ULONG_PTR)str;
294 args[2] = strlen(strA.Buffer) + 1;
295 args[3] = (ULONG_PTR)strA.Buffer;
296 RaiseException( DBG_PRINTEXCEPTION_WIDE_C, 0, 4, args );
297 exc_handled = TRUE;
298 }
299 __EXCEPT(debug_exception_handler_wide)
300 {
301 exc_handled = FALSE;
302 }
303 __ENDTRY
304
305 if (!exc_handled)
306 OutputDebugStringA( strA.Buffer );
307
308 RtlFreeAnsiString( &strA );
309 }
310}
311
312
313/*******************************************************************
314 * RaiseException (kernelbase.@)
315 */
316#ifdef __x86_64__
317#ifdef __arm64ec__
318void __attribute__((naked)) RaiseException( DWORD code, DWORD flags, DWORD count, const ULONG_PTR *args )
319{
320 asm( ".seh_proc RaiseException\n\t"
321 "stp x29, x30, [sp, #-0xb0]!\n\t"
322 ".seh_save_fplr_x 0xb0\n\t"
323 ".seh_endprologue\n\t"
324 "and w1, w1, #0x01\n\t" /* EXCEPTION_NONCONTINUABLE */
325 "stp w0, w1, [sp, #0x10]\n\t" /* ExceptionCode, ExceptionFlags */
326 "adr x4, RaiseException\n\t"
327 "stp xzr, x4, [sp, #0x18]\n\t" /* ExceptionRecord, ExceptionAddress */
328 "mov w5, #0x0f\n\t" /* EXCEPTION_MAXIMUM_PARAMETERS */
329 "cmp w2, w5\n\t"
330 "csel w2, w2, w5, lo\n\t"
331 "str x2, [sp, #0x28]\n\t" /* NumberParameters */
332 "cbz x3, 1f\n\t"
333 "lsl w2, w2, #3\n\t"
334 "add x0, sp, #0x30\n\t" /* ExceptionInformation */
335 "mov x1, x3\n\t" /* args */
336 "bl \"#memcpy\"\n"
337 "1:\tadd x0, sp, #0x10\n\t" /* rec */
338 "bl \"#RtlRaiseException\"\n\t"
339 "ldp x29, x30, [sp], #0xb0\n\t"
340 "ret\n\t"
341 ".seh_endproc" );
342}
343#else
344/* Some DRMs depend on RaiseException not altering non-volatile registers. */
345__ASM_GLOBAL_FUNC( RaiseException,
346 ".byte 0x48,0x8d,0xa4,0x24,0x00,0x00,0x00,0x00\n\t" /* hotpatch prolog */
347 "sub $0xc8,%rsp\n\t"
348 __ASM_SEH(".seh_stackalloc 0xc8\n\t")
349 __ASM_SEH(".seh_endprologue\n\t")
350 __ASM_CFI(".cfi_adjust_cfa_offset 0xc8\n\t")
351 "leaq 0x20(%rsp),%rax\n\t"
352 "movl %ecx,(%rax)\n\t" /* ExceptionCode */
353 "and $1,%edx\n\t"
354 "movl %edx,4(%rax)\n\t" /* ExceptionFlags */
355 "movq $0,8(%rax)\n\t" /* ExceptionRecord */
356 "leaq " __ASM_NAME("RaiseException") "(%rip),%rcx\n\t"
357 "movq %rcx,0x10(%rax)\n\t" /* ExceptionAddress */
358 "movq %rax,%rcx\n\t"
359 "movl $0,0x18(%rcx)\n\t" /* NumberParameters */
360 "testl %r8d,%r8d\n\t"
361 "jz 2f\n\t"
362 "testq %r9,%r9\n\t"
363 "jz 2f\n\t"
364 "movl $15,%edx\n\t"
365 "cmp %edx,%r8d\n\t"
366 "cmovb %r8d,%edx\n\t"
367 "movl %edx,0x18(%rcx)\n\t" /* NumberParameters */
368 "leaq 0x20(%rcx),%rax\n" /* ExceptionInformation */
369 "1:\tmovq (%r9),%r8\n\t"
370 "movq %r8,(%rax)\n\t"
371 "decl %edx\n\t"
372 "jz 2f\n\t"
373 "addq $8,%rax\n\t"
374 "addq $8,%r9\n\t"
375 "jmp 1b\n"
376 "2:\tcall " __ASM_NAME("RtlRaiseException") "\n\t"
377 "add $0xc8,%rsp\n\t"
378 __ASM_CFI(".cfi_adjust_cfa_offset -0xc8\n\t")
379 "ret" )
380#endif /* __arm64ec__ */
381C_ASSERT( offsetof(EXCEPTION_RECORD, ExceptionCode) == 0 );
382C_ASSERT( offsetof(EXCEPTION_RECORD, ExceptionFlags) == 4 );
383C_ASSERT( offsetof(EXCEPTION_RECORD, ExceptionRecord) == 8 );
384C_ASSERT( offsetof(EXCEPTION_RECORD, ExceptionAddress) == 0x10 );
385C_ASSERT( offsetof(EXCEPTION_RECORD, NumberParameters) == 0x18 );
386C_ASSERT( offsetof(EXCEPTION_RECORD, ExceptionInformation) == 0x20 );
387#else /* __x86_64__ */
388void WINAPI DECLSPEC_HOTPATCH RaiseException( DWORD code, DWORD flags, DWORD count, const ULONG_PTR *args )
389{
390 EXCEPTION_RECORD record;
391
392 record.ExceptionCode = code;
393 record.ExceptionFlags = flags & EXCEPTION_NONCONTINUABLE;
394 record.ExceptionRecord = NULL;
395 record.ExceptionAddress = RaiseException;
396 if (count && args)
397 {
398 if (count > EXCEPTION_MAXIMUM_PARAMETERS) count = EXCEPTION_MAXIMUM_PARAMETERS;
399 record.NumberParameters = count;
400 memcpy( record.ExceptionInformation, args, count * sizeof(*args) );
401 }
402 else record.NumberParameters = 0;
403
404 RtlRaiseException( &record );
405}
406#endif
407
408#ifdef __i386__
409__ASM_STDCALL_IMPORT(RaiseException,16)
410#else
411__ASM_GLOBAL_IMPORT(RaiseException)
412#endif
413
414/*******************************************************************
415 * RaiseFailFastException (kernelbase.@)
416 */
417void WINAPI DECLSPEC_HOTPATCH RaiseFailFastException( EXCEPTION_RECORD *record, CONTEXT *context, DWORD flags )
418{
419 FIXME( "(%p, %p, %ld) stub\n", record, context, flags );
420 TerminateProcess( GetCurrentProcess(), STATUS_FAIL_FAST_EXCEPTION );
421}
422
423/***********************************************************************
424 * SetUnhandledExceptionFilter (kernelbase.@)
425 */
426LPTOP_LEVEL_EXCEPTION_FILTER WINAPI DECLSPEC_HOTPATCH SetUnhandledExceptionFilter(
427 LPTOP_LEVEL_EXCEPTION_FILTER filter )
428{
429 return InterlockedExchangePointer( (void **)&top_filter, filter );
430}
431
432
433/*******************************************************************
434 * format_exception_msg
435 */
436static void format_exception_msg( const EXCEPTION_POINTERS *ptr, char *buffer, int size )
437{
438 const EXCEPTION_RECORD *rec = ptr->ExceptionRecord;
439 int len;
440
441 switch(rec->ExceptionCode)
442 {
443 case EXCEPTION_INT_DIVIDE_BY_ZERO:
444 len = snprintf( buffer, size, "Unhandled division by zero" );
445 break;
446 case EXCEPTION_INT_OVERFLOW:
447 len = snprintf( buffer, size, "Unhandled overflow" );
448 break;
449 case EXCEPTION_ARRAY_BOUNDS_EXCEEDED:
450 len = snprintf( buffer, size, "Unhandled array bounds" );
451 break;
452 case EXCEPTION_ILLEGAL_INSTRUCTION:
453 len = snprintf( buffer, size, "Unhandled illegal instruction" );
454 break;
455 case EXCEPTION_STACK_OVERFLOW:
456 len = snprintf( buffer, size, "Unhandled stack overflow" );
457 break;
458 case EXCEPTION_PRIV_INSTRUCTION:
459 len = snprintf( buffer, size, "Unhandled privileged instruction" );
460 break;
461 case EXCEPTION_ACCESS_VIOLATION:
462 if (rec->NumberParameters == 2)
463 len = snprintf( buffer, size, "Unhandled page fault on %s access to %p",
464 rec->ExceptionInformation[0] == EXCEPTION_WRITE_FAULT ? "write" :
465 rec->ExceptionInformation[0] == EXCEPTION_EXECUTE_FAULT ? "execute" : "read",
466 (void *)rec->ExceptionInformation[1]);
467 else
468 len = snprintf( buffer, size, "Unhandled page fault");
469 break;
470 case EXCEPTION_DATATYPE_MISALIGNMENT:
471 len = snprintf( buffer, size, "Unhandled alignment" );
472 break;
473 case CONTROL_C_EXIT:
474 len = snprintf( buffer, size, "Unhandled ^C");
475 break;
476 case STATUS_POSSIBLE_DEADLOCK:
477 len = snprintf( buffer, size, "Critical section %p wait failed",
478 (void *)rec->ExceptionInformation[0]);
479 break;
480 case EXCEPTION_WINE_STUB:
481 if ((ULONG_PTR)rec->ExceptionInformation[1] >> 16)
482 len = snprintf( buffer, size, "Unimplemented function %s.%s called",
483 (char *)rec->ExceptionInformation[0], (char *)rec->ExceptionInformation[1] );
484 else
485 len = snprintf( buffer, size, "Unimplemented function %s.%Id called",
486 (char *)rec->ExceptionInformation[0], rec->ExceptionInformation[1] );
487 break;
488 case EXCEPTION_WINE_ASSERTION:
489 len = snprintf( buffer, size, "Assertion failed" );
490 break;
491 default:
492 len = snprintf( buffer, size, "Unhandled exception 0x%08lx in thread %lx",
493 rec->ExceptionCode, GetCurrentThreadId());
494 break;
495 }
496 if (len < 0 || len >= size) return;
497 snprintf( buffer + len, size - len, " at address %p", ptr->ExceptionRecord->ExceptionAddress );
498}
499
500
501/******************************************************************
502 * start_debugger
503 *
504 * Does the effective debugger startup according to 'format'
505 */
506static BOOL start_debugger( EXCEPTION_POINTERS *epointers, HANDLE event )
507{
508 OBJECT_ATTRIBUTES attr;
509 UNICODE_STRING nameW = RTL_CONSTANT_STRING( L"\\Registry\\Machine\\Software\\Microsoft\\Windows NT\\CurrentVersion\\AeDebug" );
510 WCHAR *cmdline, *env, *p, *format = NULL;
511 HANDLE dbg_key;
512 DWORD autostart = TRUE;
513 PROCESS_INFORMATION info;
514 STARTUPINFOW startup;
515 BOOL ret = FALSE;
516 char buffer[256];
517
518 format_exception_msg( epointers, buffer, sizeof(buffer) );
519 MESSAGE( "wine: %s (thread %04lx), starting debugger...\n", buffer, GetCurrentThreadId() );
520
521 attr.Length = sizeof(attr);
522 attr.RootDirectory = 0;
523 attr.ObjectName = &nameW;
524 attr.Attributes = 0;
525 attr.SecurityDescriptor = NULL;
526 attr.SecurityQualityOfService = NULL;
527
528 if (!NtOpenKey( &dbg_key, KEY_READ, &attr ))
529 {
530 KEY_VALUE_PARTIAL_INFORMATION *info;
531 DWORD format_size = 0;
532
533 RtlInitUnicodeString( &nameW, L"Debugger" );
534 if (NtQueryValueKey( dbg_key, &nameW, KeyValuePartialInformation,
535 NULL, 0, &format_size ) == STATUS_BUFFER_TOO_SMALL)
536 {
537 char *data = HeapAlloc( GetProcessHeap(), 0, format_size );
538 NtQueryValueKey( dbg_key, &nameW, KeyValuePartialInformation,
539 data, format_size, &format_size );
540 info = (KEY_VALUE_PARTIAL_INFORMATION *)data;
541 format = HeapAlloc( GetProcessHeap(), 0, info->DataLength + sizeof(WCHAR) );
542 memcpy( format, info->Data, info->DataLength );
543 format[info->DataLength / sizeof(WCHAR)] = 0;
544
545 if (info->Type == REG_EXPAND_SZ)
546 {
547 WCHAR *tmp;
548
549 format_size = ExpandEnvironmentStringsW( format, NULL, 0 );
550 tmp = HeapAlloc( GetProcessHeap(), 0, format_size * sizeof(WCHAR));
551 ExpandEnvironmentStringsW( format, tmp, format_size );
552 HeapFree( GetProcessHeap(), 0, format );
553 format = tmp;
554 }
555 HeapFree( GetProcessHeap(), 0, data );
556 }
557
558 RtlInitUnicodeString( &nameW, L"Auto" );
559 if (!NtQueryValueKey( dbg_key, &nameW, KeyValuePartialInformation,
560 buffer, sizeof(buffer)-sizeof(WCHAR), &format_size ))
561 {
562 info = (KEY_VALUE_PARTIAL_INFORMATION *)buffer;
563 if (info->Type == REG_DWORD) memcpy( &autostart, info->Data, sizeof(DWORD) );
564 else if (info->Type == REG_SZ)
565 {
566 WCHAR *str = (WCHAR *)info->Data;
567 str[info->DataLength/sizeof(WCHAR)] = 0;
568 autostart = wcstol( str, NULL, 10 );
569 }
570 }
571
572 NtClose( dbg_key );
573 }
574
575 if (format)
576 {
577 size_t format_size = lstrlenW( format ) + 2*20;
578 cmdline = HeapAlloc( GetProcessHeap(), 0, format_size * sizeof(WCHAR) );
579 swprintf( cmdline, format_size, format, GetCurrentProcessId(), HandleToLong(event) );
580 HeapFree( GetProcessHeap(), 0, format );
581 }
582 else
583 {
584 cmdline = HeapAlloc( GetProcessHeap(), 0, 80 * sizeof(WCHAR) );
585 swprintf( cmdline, 80, L"winedbg --auto %ld %ld", GetCurrentProcessId(), HandleToLong(event) );
586 }
587
588 if (!autostart)
589 {
590 HMODULE mod = GetModuleHandleA( "user32.dll" );
591 MessageBoxA_funcptr pMessageBoxA = NULL;
592
593 if (mod) pMessageBoxA = (void *)GetProcAddress( mod, "MessageBoxA" );
594 if (pMessageBoxA)
595 {
596 static const char msg[] = ".\nDo you wish to debug it?";
597
598 format_exception_msg( epointers, buffer, sizeof(buffer) - sizeof(msg) );
599 strcat( buffer, msg );
600 if (pMessageBoxA( 0, buffer, "Exception raised", MB_YESNO | MB_ICONHAND ) == IDNO)
601 {
602 TRACE( "Killing process\n" );
603 goto exit;
604 }
605 }
606 }
607
608 /* make WINEDEBUG empty in the environment */
609 env = GetEnvironmentStringsW();
610 if (!TRACE_ON(winedbg))
611 {
612 for (p = env; *p; p += lstrlenW(p) + 1)
613 {
614 if (!wcsncmp( p, L"WINEDEBUG=", 10 ))
615 {
616 WCHAR *next = p + lstrlenW(p);
617 WCHAR *end = next + 1;
618 while (*end) end += lstrlenW(end) + 1;
619 memmove( p + 10, next, end + 1 - next );
620 break;
621 }
622 }
623 }
624
625 TRACE( "Starting debugger %s\n", debugstr_w(cmdline) );
626 memset( &startup, 0, sizeof(startup) );
627 startup.cb = sizeof(startup);
628 startup.dwFlags = STARTF_USESHOWWINDOW;
629 startup.wShowWindow = SW_SHOWNORMAL;
630 ret = CreateProcessW( NULL, cmdline, NULL, NULL, TRUE, CREATE_UNICODE_ENVIRONMENT, env, NULL, &startup, &info );
631 FreeEnvironmentStringsW( env );
632
633 if (ret)
634 {
635 /* wait for debugger to come up... */
636 HANDLE handles[2];
637 CloseHandle( info.hThread );
638 handles[0] = event;
639 handles[1] = info.hProcess;
640 WaitForMultipleObjects( 2, handles, FALSE, INFINITE );
641 CloseHandle( info.hProcess );
642 }
643 else ERR( "Couldn't start debugger %s (%ld)\n"
644 "Read the Wine Developers Guide on how to set up winedbg or another debugger\n",
645 debugstr_w(cmdline), GetLastError() );
646exit:
647 HeapFree(GetProcessHeap(), 0, cmdline);
648 return ret;
649}
650
651/******************************************************************
652 * start_debugger_atomic
653 *
654 * starts the debugger in an atomic way:
655 * - either the debugger is not started and it is started
656 * - or the debugger has already been started by another thread
657 * - or the debugger couldn't be started
658 *
659 * returns TRUE for the two first conditions, FALSE for the last
660 */
661static BOOL start_debugger_atomic( EXCEPTION_POINTERS *epointers )
662{
663 static HANDLE once;
664
665 if (once == 0)
666 {
667 OBJECT_ATTRIBUTES attr;
668 HANDLE event;
669
670 attr.Length = sizeof(attr);
671 attr.RootDirectory = 0;
672 attr.Attributes = OBJ_INHERIT;
673 attr.ObjectName = NULL;
674 attr.SecurityDescriptor = NULL;
675 attr.SecurityQualityOfService = NULL;
676
677 /* ask for manual reset, so that once the debugger is started,
678 * every thread will know it */
679 NtCreateEvent( &event, EVENT_ALL_ACCESS, &attr, NotificationEvent, FALSE );
680 if (InterlockedCompareExchangePointer( &once, event, 0 ) == 0)
681 {
682 /* ok, our event has been set... we're the winning thread */
683 BOOL ret = start_debugger( epointers, once );
684
685 if (!ret)
686 {
687 /* so that the other threads won't be stuck */
688 NtSetEvent( once, NULL );
689 }
690 return ret;
691 }
692
693 /* someone beat us here... */
694 CloseHandle( event );
695 }
696
697 /* and wait for the winner to have actually created the debugger */
698 WaitForSingleObject( once, INFINITE );
699 /* in fact, here, we only know that someone has tried to start the debugger,
700 * we'll know by reposting the exception if it has actually attached
701 * to the current process */
702 return TRUE;
703}
704
705
706/*******************************************************************
707 * check_resource_write
708 *
709 * Check if the exception is a write attempt to the resource data.
710 * If yes, we unprotect the resources to let broken apps continue
711 * (Windows does this too).
712 */
713static BOOL check_resource_write( void *addr )
714{
715 DWORD old_prot;
716 void *rsrc;
717 DWORD size;
718 MEMORY_BASIC_INFORMATION info;
719
720 if (!VirtualQuery( addr, &info, sizeof(info) )) return FALSE;
721 if (info.State == MEM_FREE || !(info.Type & MEM_IMAGE)) return FALSE;
722 if (!(rsrc = RtlImageDirectoryEntryToData( info.AllocationBase, TRUE,
723 IMAGE_DIRECTORY_ENTRY_RESOURCE, &size )))
724 return FALSE;
725 if (addr < rsrc || (char *)addr >= (char *)rsrc + size) return FALSE;
726 TRACE( "Broken app is writing to the resource data, enabling work-around\n" );
727 VirtualProtect( rsrc, size, PAGE_READWRITE, &old_prot );
728 return TRUE;
729}
730
731
732/*******************************************************************
733 * UnhandledExceptionFilter (kernelbase.@)
734 */
735LONG WINAPI UnhandledExceptionFilter( EXCEPTION_POINTERS *epointers )
736{
737 const EXCEPTION_RECORD *rec = epointers->ExceptionRecord;
738
739 if (rec->ExceptionCode == EXCEPTION_ACCESS_VIOLATION && rec->NumberParameters >= 2)
740 {
741 switch (rec->ExceptionInformation[0])
742 {
743 case EXCEPTION_WRITE_FAULT:
744 if (check_resource_write( (void *)rec->ExceptionInformation[1] ))
745 return EXCEPTION_CONTINUE_EXECUTION;
746 break;
747 }
748 }
749
750 if (!NtCurrentTeb()->Peb->BeingDebugged)
751 {
752 if (rec->ExceptionCode == CONTROL_C_EXIT)
753 {
754 /* do not launch the debugger on ^C, simply terminate the process */
755 TerminateProcess( GetCurrentProcess(), 1 );
756 }
757
758 if (top_filter)
759 {
760 LONG ret = top_filter( epointers );
761 if (ret != EXCEPTION_CONTINUE_SEARCH) return ret;
762 }
763
764 if ((GetErrorMode() & SEM_NOGPFAULTERRORBOX) ||
765 !start_debugger_atomic( epointers ) || !NtCurrentTeb()->Peb->BeingDebugged)
766 return EXCEPTION_EXECUTE_HANDLER;
767 }
768 return EXCEPTION_CONTINUE_SEARCH;
769}
770
771
772/***********************************************************************
773 * WerGetFlags (kernelbase.@)
774 */
775HRESULT WINAPI /* DECLSPEC_HOTPATCH */ WerGetFlags( HANDLE process, DWORD *flags )
776{
777 FIXME( "(%p, %p) stub\n", process, flags );
778 return E_NOTIMPL;
779}
780
781
782/***********************************************************************
783 * WerRegisterFile (kernelbase.@)
784 */
785HRESULT WINAPI /* DECLSPEC_HOTPATCH */ WerRegisterFile( const WCHAR *file, WER_REGISTER_FILE_TYPE type,
786 DWORD flags )
787{
788 FIXME( "(%s, %d, %ld) stub\n", debugstr_w(file), type, flags );
789 return E_NOTIMPL;
790}
791
792
793/***********************************************************************
794 * WerRegisterMemoryBlock (kernelbase.@)
795 */
796HRESULT WINAPI /* DECLSPEC_HOTPATCH */ WerRegisterMemoryBlock( void *block, DWORD size )
797{
798 FIXME( "(%p %ld) stub\n", block, size );
799 return E_NOTIMPL;
800}
801
802
803/***********************************************************************
804 * WerRegisterRuntimeExceptionModule (kernelbase.@)
805 */
806HRESULT WINAPI /* DECLSPEC_HOTPATCH */ WerRegisterRuntimeExceptionModule( const WCHAR *dll, void *context )
807{
808 FIXME( "(%s, %p) stub\n", debugstr_w(dll), context );
809 return S_OK;
810}
811
812
813/***********************************************************************
814 * WerSetFlags (kernelbase.@)
815 */
816HRESULT WINAPI /* DECLSPEC_HOTPATCH */ WerSetFlags( DWORD flags )
817{
818 FIXME("(%ld) stub\n", flags);
819 return S_OK;
820}
821
822
823/***********************************************************************
824 * WerUnregisterFile (kernelbase.@)
825 */
826HRESULT WINAPI /* DECLSPEC_HOTPATCH */ WerUnregisterFile( const WCHAR *file )
827{
828 FIXME( "(%s) stub\n", debugstr_w(file) );
829 return E_NOTIMPL;
830}
831
832
833/***********************************************************************
834 * WerUnregisterMemoryBlock (kernelbase.@)
835 */
836HRESULT WINAPI /* DECLSPEC_HOTPATCH */ WerUnregisterMemoryBlock( void *block )
837{
838 FIXME( "(%p) stub\n", block );
839 return E_NOTIMPL;
840}
841
842
843/***********************************************************************
844 * WerUnregisterRuntimeExceptionModule (kernelbase.@)
845 */
846HRESULT WINAPI /* DECLSPEC_HOTPATCH */ WerUnregisterRuntimeExceptionModule( const WCHAR *dll, void *context )
847{
848 FIXME( "(%s, %p) stub\n", debugstr_w(dll), context );
849 return S_OK;
850}
851
852
853/***********************************************************************
854 * psapi functions
855 ***********************************************************************/
856
857
858typedef struct _LDR_DATA_TABLE_ENTRY32
859{
860 LIST_ENTRY32 InLoadOrderModuleList;
861 LIST_ENTRY32 InMemoryOrderModuleList;
862 LIST_ENTRY32 InInitializationOrderModuleList;
863 DWORD BaseAddress;
864 DWORD EntryPoint;
865 ULONG SizeOfImage;
866 UNICODE_STRING32 FullDllName;
867 UNICODE_STRING32 BaseDllName;
868} LDR_DATA_TABLE_ENTRY32;
869
870struct module_iterator
871{
872 HANDLE process;
873 LIST_ENTRY *head;
874 LIST_ENTRY *current;
875 BOOL wow64;
876 LDR_DATA_TABLE_ENTRY ldr_module;
877 LDR_DATA_TABLE_ENTRY32 ldr_module32;
878};
879
880
881static BOOL init_module_iterator_wow64( struct module_iterator *iter, HANDLE process )
882{
883 PEB_LDR_DATA32 *ldr_data32_ptr;
884 DWORD ldr_data32, first_module;
885 PEB32 *peb32;
886
887 iter->wow64 = TRUE;
888 if (!set_ntstatus( NtQueryInformationProcess( process, ProcessWow64Information,
889 &peb32, sizeof(peb32), NULL )))
890 return FALSE;
891 if (!ReadProcessMemory( process, &peb32->LdrData, &ldr_data32, sizeof(ldr_data32), NULL ))
892 return FALSE;
893 ldr_data32_ptr = (PEB_LDR_DATA32 *)(DWORD_PTR) ldr_data32;
894 if (!ReadProcessMemory( process, &ldr_data32_ptr->InLoadOrderModuleList.Flink,
895 &first_module, sizeof(first_module), NULL ))
896 return FALSE;
897 iter->head = (LIST_ENTRY *)&ldr_data32_ptr->InLoadOrderModuleList;
898 iter->current = (LIST_ENTRY *)(DWORD_PTR)first_module;
899 iter->process = process;
900 return TRUE;
901}
902
903
904static BOOL init_module_iterator( struct module_iterator *iter, HANDLE process )
905{
906 PROCESS_BASIC_INFORMATION pbi;
907 PPEB_LDR_DATA ldr_data;
908
909 iter->wow64 = FALSE;
910 if (!set_ntstatus( NtQueryInformationProcess( process, ProcessBasicInformation,
911 &pbi, sizeof(pbi), NULL )))
912 return FALSE;
913
914 /* read address of LdrData from PEB */
915 if (!ReadProcessMemory( process, &pbi.PebBaseAddress->LdrData, &ldr_data, sizeof(ldr_data), NULL ))
916 return FALSE;
917
918 /* This happens when running "old" wow64 configuration. Mark it as such. */
919 if (!ldr_data)
920 {
921 SetLastError( ERROR_EMPTY );
922 return FALSE;
923 }
924 /* read address of first module from LdrData */
925 if (!ReadProcessMemory( process, &ldr_data->InLoadOrderModuleList.Flink,
926 &iter->current, sizeof(iter->current), NULL ))
927 return FALSE;
928
929 iter->head = &ldr_data->InLoadOrderModuleList;
930 iter->process = process;
931 return TRUE;
932}
933
934
935static int module_iterator_next( struct module_iterator *iter )
936{
937 if (iter->current == iter->head) return 0;
938
939 if (is_win64 && iter->wow64)
940 {
941 LIST_ENTRY32 *entry32 = (LIST_ENTRY32 *)iter->current;
942
943 if (!ReadProcessMemory( iter->process,
944 CONTAINING_RECORD(entry32, LDR_DATA_TABLE_ENTRY32, InLoadOrderModuleList),
945 &iter->ldr_module32, sizeof(iter->ldr_module32), NULL ))
946 return -1;
947 iter->current = (LIST_ENTRY *)(DWORD_PTR)iter->ldr_module32.InLoadOrderModuleList.Flink;
948 return 1;
949 }
950
951 if (!ReadProcessMemory( iter->process,
952 CONTAINING_RECORD(iter->current, LDR_DATA_TABLE_ENTRY, InLoadOrderLinks),
953 &iter->ldr_module, sizeof(iter->ldr_module), NULL ))
954 return -1;
955
956 iter->current = iter->ldr_module.InLoadOrderLinks.Flink;
957 return 1;
958}
959
960
961static BOOL get_ldr_module( HANDLE process, HMODULE module, LDR_DATA_TABLE_ENTRY *ldr_module )
962{
963 struct module_iterator iter;
964 INT ret;
965
966 if (!init_module_iterator( &iter, process )) return FALSE;
967
968 while ((ret = module_iterator_next( &iter )) > 0)
969 /* When hModule is NULL we return the process image - which will be
970 * the first module since our iterator uses InLoadOrderModuleList */
971 if (!module || module == iter.ldr_module.DllBase)
972 {
973 *ldr_module = iter.ldr_module;
974 return TRUE;
975 }
976
977 if (ret == 0) SetLastError( ERROR_INVALID_HANDLE );
978 return FALSE;
979}
980
981
982static BOOL get_ldr_module32( HANDLE process, HMODULE module, LDR_DATA_TABLE_ENTRY32 *ldr_module )
983{
984 struct module_iterator iter;
985 INT ret;
986
987#ifdef _WIN64
988 if ((ULONG_PTR)module >> 32)
989 {
990 SetLastError( ERROR_INVALID_HANDLE );
991 return FALSE;
992 }
993#endif
994 if (!init_module_iterator_wow64( &iter, process )) return FALSE;
995
996 while ((ret = module_iterator_next( &iter )) > 0)
997 /* When hModule is NULL we return the process image - which will be
998 * the first module since our iterator uses InLoadOrderModuleList */
999 if (!module || (DWORD)(DWORD_PTR)module == iter.ldr_module32.BaseAddress)
1000 {
1001 *ldr_module = iter.ldr_module32;
1002 return TRUE;
1003 }
1004
1005 if (ret == 0) SetLastError( ERROR_INVALID_HANDLE );
1006 return FALSE;
1007}
1008
1009
1010/***********************************************************************
1011 * EmptyWorkingSet (kernelbase.@)
1012 * K32EmptyWorkingSet (kernelbase.@)
1013 */
1014BOOL WINAPI DECLSPEC_HOTPATCH EmptyWorkingSet( HANDLE process )
1015{
1016 return SetProcessWorkingSetSizeEx( process, (SIZE_T)-1, (SIZE_T)-1, 0 );
1017}
1018
1019
1020/***********************************************************************
1021 * EnumDeviceDrivers (kernelbase.@)
1022 * K32EnumDeviceDrivers (kernelbase.@)
1023 */
1024BOOL WINAPI EnumDeviceDrivers( void **image_base, DWORD count, DWORD *needed )
1025{
1026 FIXME( "(%p, %ld, %p): stub\n", image_base, count, needed );
1027 if (needed) *needed = 0;
1028 return TRUE;
1029}
1030
1031
1032/***********************************************************************
1033 * EnumPageFilesA (kernelbase.@)
1034 * K32EnumPageFilesA (kernelbase.@)
1035 */
1036BOOL WINAPI /* DECLSPEC_HOTPATCH */ EnumPageFilesA( PENUM_PAGE_FILE_CALLBACKA callback, void *context )
1037{
1038 FIXME( "(%p, %p) stub\n", callback, context );
1039 return FALSE;
1040}
1041
1042
1043/***********************************************************************
1044 * EnumPageFilesW (kernelbase.@)
1045 * K32EnumPageFilesW (kernelbase.@)
1046 */
1047BOOL WINAPI /* DECLSPEC_HOTPATCH */ EnumPageFilesW( PENUM_PAGE_FILE_CALLBACKW callback, void *context )
1048{
1049 FIXME( "(%p, %p) stub\n", callback, context );
1050 return FALSE;
1051}
1052
1053
1054/***********************************************************************
1055 * EnumProcessModules (kernelbase.@)
1056 * K32EnumProcessModules (kernelbase.@)
1057 */
1058BOOL WINAPI DECLSPEC_HOTPATCH EnumProcessModules( HANDLE process, HMODULE *module,
1059 DWORD count, DWORD *needed )
1060{
1061 return EnumProcessModulesEx( process, module, count, needed, LIST_MODULES_DEFAULT );
1062}
1063
1064
1065struct module_push
1066{
1067 HMODULE *module;
1068 unsigned count;
1069 unsigned size;
1070};
1071
1072static void module_push( struct module_push *mp, HMODULE module )
1073{
1074 if (mp->count >= sizeof(HMODULE))
1075 {
1076 *mp->module++ = module;
1077 mp->count -= sizeof(HMODULE);
1078 }
1079 mp->size += sizeof(HMODULE);
1080}
1081
1082static void module_push_iter( struct module_push *mp, struct module_iterator *iter )
1083{
1084 if (is_win64 && iter->wow64)
1085 module_push( mp, (HMODULE) (DWORD_PTR)iter->ldr_module32.BaseAddress );
1086 else
1087 module_push( mp, iter->ldr_module.DllBase );
1088}
1089
1090static int module_push_all( struct module_push *mp, struct module_iterator *iter )
1091{
1092 int ret;
1093
1094 while ((ret = module_iterator_next( iter )) > 0)
1095 module_push_iter( mp, iter );
1096
1097 return ret;
1098}
1099
1100/***********************************************************************
1101 * EnumProcessModulesEx (kernelbase.@)
1102 * K32EnumProcessModulesEx (kernelbase.@)
1103 */
1104BOOL WINAPI EnumProcessModulesEx( HANDLE process, HMODULE *module, DWORD count,
1105 DWORD *needed, DWORD filter )
1106{
1107 struct module_push mp = {module, count, 0};
1108 unsigned list_mode;
1109 BOOL target_wow64;
1110 INT ret = 0;
1111
1112 TRACE( "(%p, %p, %ld, %p, %ld)\n", process, module, count, needed, filter );
1113
1114 if (process != GetCurrentProcess())
1115 {
1116 if (!IsWow64Process( process, &target_wow64 )) return FALSE;
1117 }
1118 else target_wow64 = is_wow64;
1119
1120 if (filter & ~LIST_MODULES_ALL)
1121 {
1122 SetLastError( ERROR_INVALID_PARAMETER );
1123 return FALSE;
1124 }
1125 list_mode = filter & LIST_MODULES_ALL;
1126 /* Can't access 64bit process from (wow64) 32bit */
1127 if (is_wow64 && !target_wow64)
1128 {
1129 SetLastError( ERROR_PARTIAL_COPY );
1130 return FALSE;
1131 }
1132 if (count && !module)
1133 {
1134 SetLastError( ERROR_NOACCESS );
1135 return FALSE;
1136 }
1137
1138 if (process == GetCurrentProcess())
1139 {
1140 if (!(is_win64 && list_mode == LIST_MODULES_32BIT))
1141 {
1142 PPEB_LDR_DATA ldr_data = NtCurrentTeb()->Peb->LdrData;
1143 PLIST_ENTRY head = &ldr_data->InLoadOrderModuleList;
1144 PLIST_ENTRY entry = head->Flink;
1145
1146 while (entry != head)
1147 {
1148 LDR_DATA_TABLE_ENTRY *ldr = CONTAINING_RECORD( entry, LDR_DATA_TABLE_ENTRY, InLoadOrderLinks );
1149 module_push( &mp, ldr->DllBase );
1150 entry = entry->Flink;
1151 }
1152 }
1153 }
1154 else
1155 {
1156 struct module_iterator iter;
1157
1158 if (is_win64 && target_wow64 && (list_mode & LIST_MODULES_32BIT))
1159 {
1160 if (!init_module_iterator_wow64( &iter, process ) || module_push_all( &mp, &iter ) < 0)
1161 return FALSE;
1162 }
1163 if (!(is_win64 && list_mode == LIST_MODULES_32BIT))
1164 {
1165 if (init_module_iterator( &iter, process ))
1166 {
1167 if (is_win64 && target_wow64 && (list_mode & LIST_MODULES_64BIT))
1168 /* Don't add main module twice in _ALL mode */
1169 ret = module_iterator_next( &iter );
1170 if (ret >= 0) ret = module_push_all( &mp, &iter );
1171 }
1172 else if (GetLastError() == ERROR_EMPTY)
1173 {
1174 /* We're running on "old" wow configuration.
1175 * Fallback to PEB32 to get at least main module if requested.
1176 */
1177 if (list_mode == LIST_MODULES_DEFAULT)
1178 {
1179 if (init_module_iterator_wow64( &iter, process ) && module_iterator_next( &iter ) > 0)
1180 module_push_iter( &mp, &iter );
1181 else
1182 ret = -1;
1183 }
1184 }
1185 else
1186 return FALSE;
1187 }
1188 }
1189
1190 if (!needed)
1191 {
1192 SetLastError( ERROR_NOACCESS );
1193 return FALSE;
1194 }
1195 *needed = mp.size;
1196 return ret == 0;
1197}
1198
1199
1200/***********************************************************************
1201 * EnumProcesses (kernelbase.@)
1202 * K32EnumProcesses (kernelbase.@)
1203 */
1204BOOL WINAPI EnumProcesses( DWORD *ids, DWORD count, DWORD *used )
1205{
1206 SYSTEM_PROCESS_INFORMATION *spi;
1207 ULONG size = 0x4000;
1208 void *buf = NULL;
1209 NTSTATUS status;
1210
1211 do
1212 {
1213 size *= 2;
1214 HeapFree( GetProcessHeap(), 0, buf );
1215 if (!(buf = HeapAlloc( GetProcessHeap(), 0, size ))) return FALSE;
1216 status = NtQuerySystemInformation( SystemProcessInformation, buf, size, NULL );
1217 } while (status == STATUS_INFO_LENGTH_MISMATCH);
1218
1219 if (!set_ntstatus( status ))
1220 {
1221 HeapFree( GetProcessHeap(), 0, buf );
1222 return FALSE;
1223 }
1224 spi = buf;
1225 for (*used = 0; count >= sizeof(DWORD); count -= sizeof(DWORD))
1226 {
1227 *ids++ = HandleToUlong( spi->UniqueProcessId );
1228 *used += sizeof(DWORD);
1229 if (spi->NextEntryOffset == 0) break;
1230 spi = (SYSTEM_PROCESS_INFORMATION *)(((PCHAR)spi) + spi->NextEntryOffset);
1231 }
1232 HeapFree( GetProcessHeap(), 0, buf );
1233 return TRUE;
1234}
1235
1236
1237/***********************************************************************
1238 * GetDeviceDriverBaseNameA (kernelbase.@)
1239 * K32GetDeviceDriverBaseNameA (kernelbase.@)
1240 */
1241DWORD WINAPI DECLSPEC_HOTPATCH GetDeviceDriverBaseNameA( void *image_base, char *name, DWORD size )
1242{
1243 FIXME( "(%p, %p, %ld): stub\n", image_base, name, size );
1244 if (name && size) name[0] = 0;
1245 return 0;
1246}
1247
1248
1249/***********************************************************************
1250 * GetDeviceDriverBaseNameW (kernelbase.@)
1251 * K32GetDeviceDriverBaseNameW (kernelbase.@)
1252 */
1253DWORD WINAPI DECLSPEC_HOTPATCH GetDeviceDriverBaseNameW( void *image_base, WCHAR *name, DWORD size )
1254{
1255 FIXME( "(%p, %p, %ld): stub\n", image_base, name, size );
1256 if (name && size) name[0] = 0;
1257 return 0;
1258}
1259
1260
1261/***********************************************************************
1262 * GetDeviceDriverFileNameA (kernelbase.@)
1263 * K32GetDeviceDriverFileNameA (kernelbase.@)
1264 */
1265DWORD WINAPI DECLSPEC_HOTPATCH GetDeviceDriverFileNameA( void *image_base, char *name, DWORD size )
1266{
1267 FIXME( "(%p, %p, %ld): stub\n", image_base, name, size );
1268 if (name && size) name[0] = 0;
1269 return 0;
1270}
1271
1272
1273/***********************************************************************
1274 * GetDeviceDriverFileNameW (kernelbase.@)
1275 * K32GetDeviceDriverFileNameW (kernelbase.@)
1276 */
1277DWORD WINAPI DECLSPEC_HOTPATCH GetDeviceDriverFileNameW( void *image_base, WCHAR *name, DWORD size )
1278{
1279 FIXME( "(%p, %p, %ld): stub\n", image_base, name, size );
1280 if (name && size) name[0] = 0;
1281 return 0;
1282}
1283
1284
1285/***********************************************************************
1286 * GetMappedFileNameA (kernelbase.@)
1287 * K32GetMappedFileNameA (kernelbase.@)
1288 */
1289DWORD WINAPI DECLSPEC_HOTPATCH GetMappedFileNameA( HANDLE process, void *addr, char *name, DWORD size )
1290{
1291 WCHAR nameW[MAX_PATH];
1292 DWORD len;
1293
1294 if (size && !name)
1295 {
1296 SetLastError( ERROR_INVALID_PARAMETER );
1297 return 0;
1298 }
1299 if (!GetMappedFileNameW( process, addr, nameW, MAX_PATH )) return 0;
1300 if (!size)
1301 {
1302 SetLastError( ERROR_INSUFFICIENT_BUFFER );
1303 return 0;
1304 }
1305 len = file_name_WtoA( nameW, wcslen(nameW), name, size );
1306 name[min(len, size - 1)] = 0;
1307 return len;
1308}
1309
1310
1311/***********************************************************************
1312 * GetMappedFileNameW (kernelbase.@)
1313 * K32GetMappedFileNameW (kernelbase.@)
1314 */
1315DWORD WINAPI DECLSPEC_HOTPATCH GetMappedFileNameW( HANDLE process, void *addr, WCHAR *name, DWORD size )
1316{
1317 ULONG_PTR buffer[(sizeof(MEMORY_SECTION_NAME) + MAX_PATH * sizeof(WCHAR)) / sizeof(ULONG_PTR)];
1318 MEMORY_SECTION_NAME *mem = (MEMORY_SECTION_NAME *)buffer;
1319 DWORD len;
1320
1321 if (size && !name)
1322 {
1323 SetLastError( ERROR_INVALID_PARAMETER );
1324 return 0;
1325 }
1326 if (!set_ntstatus( NtQueryVirtualMemory( process, addr, MemoryMappedFilenameInformation,
1327 mem, sizeof(buffer), NULL )))
1328 return 0;
1329
1330 len = mem->SectionFileName.Length / sizeof(WCHAR);
1331 memcpy( name, mem->SectionFileName.Buffer, min( mem->SectionFileName.Length, size * sizeof(WCHAR) ));
1332 if (len >= size) SetLastError( ERROR_INSUFFICIENT_BUFFER );
1333 name[min(len, size - 1)] = 0;
1334 return len;
1335}
1336
1337
1338/***********************************************************************
1339 * GetModuleBaseNameA (kernelbase.@)
1340 * K32GetModuleBaseNameA (kernelbase.@)
1341 */
1342DWORD WINAPI DECLSPEC_HOTPATCH GetModuleBaseNameA( HANDLE process, HMODULE module,
1343 char *name, DWORD size )
1344{
1345 WCHAR *name_w;
1346 DWORD len, ret = 0;
1347
1348 if (!name || !size)
1349 {
1350 SetLastError( ERROR_INVALID_PARAMETER );
1351 return 0;
1352 }
1353 if (!(name_w = HeapAlloc( GetProcessHeap(), 0, sizeof(WCHAR) * size ))) return 0;
1354
1355 len = GetModuleBaseNameW( process, module, name_w, size );
1356 TRACE( "%ld, %s\n", len, debugstr_w(name_w) );
1357 if (len)
1358 {
1359 ret = WideCharToMultiByte( CP_ACP, 0, name_w, len, name, size, NULL, NULL );
1360 if (ret < size) name[ret] = 0;
1361 }
1362 HeapFree( GetProcessHeap(), 0, name_w );
1363 return ret;
1364}
1365
1366
1367/***********************************************************************
1368 * GetModuleBaseNameW (kernelbase.@)
1369 * K32GetModuleBaseNameW (kernelbase.@)
1370 */
1371DWORD WINAPI DECLSPEC_HOTPATCH GetModuleBaseNameW( HANDLE process, HMODULE module,
1372 WCHAR *name, DWORD size )
1373{
1374 BOOL wow64, found = FALSE;
1375
1376 if (!IsWow64Process( process, &wow64 )) return 0;
1377
1378 if (is_win64 && wow64)
1379 {
1380 LDR_DATA_TABLE_ENTRY32 ldr_module32;
1381
1382 if (get_ldr_module32(process, module, &ldr_module32))
1383 {
1384 size = min( ldr_module32.BaseDllName.Length / sizeof(WCHAR), size );
1385 if (ReadProcessMemory( process, (void *)(DWORD_PTR)ldr_module32.BaseDllName.Buffer,
1386 name, size * sizeof(WCHAR), NULL ))
1387 found = TRUE;
1388 }
1389 }
1390 if (!found)
1391 {
1392 LDR_DATA_TABLE_ENTRY ldr_module;
1393
1394 if (!get_ldr_module( process, module, &ldr_module )) return 0;
1395 size = min( ldr_module.BaseDllName.Length / sizeof(WCHAR), size );
1396 if (!ReadProcessMemory( process, ldr_module.BaseDllName.Buffer,
1397 name, size * sizeof(WCHAR), NULL ))
1398 return 0;
1399 }
1400 name[size] = 0;
1401 return size;
1402}
1403
1404
1405/***********************************************************************
1406 * GetModuleFileNameExA (kernelbase.@)
1407 * K32GetModuleFileNameExA (kernelbase.@)
1408 */
1409DWORD WINAPI DECLSPEC_HOTPATCH GetModuleFileNameExA( HANDLE process, HMODULE module,
1410 char *name, DWORD size )
1411{
1412 WCHAR *ptr;
1413 DWORD len;
1414
1415 TRACE( "(process=%p, module=%p, %p, %ld)\n", process, module, name, size );
1416
1417 if (!name || !size)
1418 {
1419 SetLastError( ERROR_INVALID_PARAMETER );
1420 return 0;
1421 }
1422 if (process == GetCurrentProcess())
1423 {
1424 len = GetModuleFileNameA( module, name, size );
1425 name[size - 1] = '\0';
1426 return len;
1427 }
1428
1429 if (!(ptr = HeapAlloc( GetProcessHeap(), 0, size * sizeof(WCHAR) ))) return 0;
1430 len = GetModuleFileNameExW( process, module, ptr, size );
1431 if (!len)
1432 {
1433 name[0] = 0;
1434 }
1435 else
1436 {
1437 if (!WideCharToMultiByte( CP_ACP, 0, ptr, -1, name, size, NULL, NULL ))
1438 {
1439 name[size - 1] = 0;
1440 len = size;
1441 }
1442 else if (len < size) len = strlen( name );
1443 }
1444 HeapFree( GetProcessHeap(), 0, ptr );
1445 return len;
1446}
1447
1448
1449/***********************************************************************
1450 * GetModuleFileNameExW (kernelbase.@)
1451 * K32GetModuleFileNameExW (kernelbase.@)
1452 */
1453DWORD WINAPI DECLSPEC_HOTPATCH GetModuleFileNameExW( HANDLE process, HMODULE module,
1454 WCHAR *name, DWORD size )
1455{
1456 BOOL wow64, found = FALSE;
1457 DWORD len = 0;
1458
1459 if (!size) return 0;
1460
1461 if (!IsWow64Process( process, &wow64 )) return 0;
1462
1463 if (is_win64 && wow64)
1464 {
1465 LDR_DATA_TABLE_ENTRY32 ldr_module32;
1466
1467 if (get_ldr_module32( process, module, &ldr_module32 ))
1468 {
1469 len = ldr_module32.FullDllName.Length / sizeof(WCHAR);
1470 if (ReadProcessMemory( process, (void *)(DWORD_PTR)ldr_module32.FullDllName.Buffer,
1471 name, min( len, size ) * sizeof(WCHAR), NULL ))
1472 found = TRUE;
1473 }
1474 }
1475 if (!found)
1476 {
1477 LDR_DATA_TABLE_ENTRY ldr_module;
1478
1479 if (!get_ldr_module(process, module, &ldr_module)) return 0;
1480 len = ldr_module.FullDllName.Length / sizeof(WCHAR);
1481 if (!ReadProcessMemory( process, ldr_module.FullDllName.Buffer,
1482 name, min( len, size ) * sizeof(WCHAR), NULL ))
1483 return 0;
1484 }
1485
1486 if (len < size)
1487 {
1488 name[len] = 0;
1489 return len;
1490 }
1491 else
1492 {
1493 name[size - 1] = 0;
1494 return size;
1495 }
1496}
1497
1498
1499/***********************************************************************
1500 * GetModuleInformation (kernelbase.@)
1501 * K32GetModuleInformation (kernelbase.@)
1502 */
1503BOOL WINAPI GetModuleInformation( HANDLE process, HMODULE module, MODULEINFO *modinfo, DWORD count )
1504{
1505 BOOL wow64, found = FALSE;
1506
1507 if (count < sizeof(MODULEINFO))
1508 {
1509 SetLastError( ERROR_INSUFFICIENT_BUFFER );
1510 return FALSE;
1511 }
1512
1513 if (!IsWow64Process( process, &wow64 )) return FALSE;
1514
1515 if (is_win64 && wow64)
1516 {
1517 LDR_DATA_TABLE_ENTRY32 ldr_module32;
1518
1519 if (get_ldr_module32( process, module, &ldr_module32 ))
1520 {
1521 modinfo->lpBaseOfDll = (void *)(DWORD_PTR)ldr_module32.BaseAddress;
1522 modinfo->SizeOfImage = ldr_module32.SizeOfImage;
1523 modinfo->EntryPoint = (void *)(DWORD_PTR)ldr_module32.EntryPoint;
1524 found = TRUE;
1525 }
1526 }
1527 if (!found)
1528 {
1529 LDR_DATA_TABLE_ENTRY ldr_module;
1530
1531 if (!get_ldr_module( process, module, &ldr_module )) return FALSE;
1532 modinfo->lpBaseOfDll = ldr_module.DllBase;
1533 modinfo->SizeOfImage = ldr_module.SizeOfImage;
1534 modinfo->EntryPoint = ldr_module.EntryPoint;
1535 }
1536 return TRUE;
1537}
1538
1539
1540/***********************************************************************
1541 * GetPerformanceInfo (kernelbase.@)
1542 * K32GetPerformanceInfo (kernelbase.@)
1543 */
1544BOOL WINAPI DECLSPEC_HOTPATCH GetPerformanceInfo( PPERFORMANCE_INFORMATION info, DWORD size )
1545{
1546 SYSTEM_PERFORMANCE_INFORMATION perf;
1547 SYSTEM_BASIC_INFORMATION basic;
1548 SYSTEM_PROCESS_INFORMATION *process, *spi;
1549 DWORD info_size;
1550 NTSTATUS status;
1551
1552 TRACE( "(%p, %ld)\n", info, size );
1553
1554 if (size < sizeof(*info))
1555 {
1556 SetLastError( ERROR_BAD_LENGTH );
1557 return FALSE;
1558 }
1559
1560 status = NtQuerySystemInformation( SystemPerformanceInformation, &perf, sizeof(perf), NULL );
1561 if (!set_ntstatus( status )) return FALSE;
1562 status = NtQuerySystemInformation( SystemBasicInformation, &basic, sizeof(basic), NULL );
1563 if (!set_ntstatus( status )) return FALSE;
1564
1565 info->cb = sizeof(*info);
1566 info->CommitTotal = perf.TotalCommittedPages;
1567 info->CommitLimit = perf.TotalCommitLimit;
1568 info->CommitPeak = perf.PeakCommitment;
1569 info->PhysicalTotal = basic.MmNumberOfPhysicalPages;
1570 info->PhysicalAvailable = perf.AvailablePages;
1571 info->SystemCache = 0;
1572 info->KernelTotal = perf.PagedPoolUsage + perf.NonPagedPoolUsage;
1573 info->KernelPaged = perf.PagedPoolUsage;
1574 info->KernelNonpaged = perf.NonPagedPoolUsage;
1575 info->PageSize = basic.PageSize;
1576
1577 /* fields from SYSTEM_PROCESS_INFORMATION */
1578 NtQuerySystemInformation( SystemProcessInformation, NULL, 0, &info_size );
1579 for (;;)
1580 {
1581 process = HeapAlloc( GetProcessHeap(), 0, info_size );
1582 if (!process)
1583 {
1584 SetLastError( ERROR_OUTOFMEMORY );
1585 return FALSE;
1586 }
1587 status = NtQuerySystemInformation( SystemProcessInformation, process, info_size, &info_size );
1588 if (!status) break;
1589 HeapFree( GetProcessHeap(), 0, process );
1590 if (status != STATUS_INFO_LENGTH_MISMATCH)
1591 {
1592 SetLastError( RtlNtStatusToDosError( status ) );
1593 return FALSE;
1594 }
1595 }
1596 info->HandleCount = info->ProcessCount = info->ThreadCount = 0;
1597 spi = process;
1598 for (;;)
1599 {
1600 info->ProcessCount++;
1601 info->HandleCount += spi->HandleCount;
1602 info->ThreadCount += spi->dwThreadCount;
1603 if (spi->NextEntryOffset == 0) break;
1604 spi = (SYSTEM_PROCESS_INFORMATION *)((char *)spi + spi->NextEntryOffset);
1605 }
1606 HeapFree( GetProcessHeap(), 0, process );
1607 return TRUE;
1608}
1609
1610
1611/***********************************************************************
1612 * GetProcessImageFileNameA (kernelbase.@)
1613 * K32GetProcessImageFileNameA (kernelbase.@)
1614 */
1615DWORD WINAPI DECLSPEC_HOTPATCH GetProcessImageFileNameA( HANDLE process, char *file, DWORD size )
1616{
1617 return QueryFullProcessImageNameA( process, PROCESS_NAME_NATIVE, file, &size ) ? size : 0;
1618}
1619
1620
1621/***********************************************************************
1622 * GetProcessImageFileNameW (kernelbase.@)
1623 * K32GetProcessImageFileNameW (kernelbase.@)
1624 */
1625DWORD WINAPI DECLSPEC_HOTPATCH GetProcessImageFileNameW( HANDLE process, WCHAR *file, DWORD size )
1626{
1627 return QueryFullProcessImageNameW( process, PROCESS_NAME_NATIVE, file, &size ) ? size : 0;
1628}
1629
1630
1631/***********************************************************************
1632 * GetProcessMemoryInfo (kernelbase.@)
1633 * K32GetProcessMemoryInfo (kernelbase.@)
1634 */
1635BOOL WINAPI DECLSPEC_HOTPATCH GetProcessMemoryInfo( HANDLE process, PROCESS_MEMORY_COUNTERS *pmc,
1636 DWORD count )
1637{
1638 VM_COUNTERS vmc;
1639
1640 if (count < sizeof(PROCESS_MEMORY_COUNTERS))
1641 {
1642 SetLastError( ERROR_INSUFFICIENT_BUFFER );
1643 return FALSE;
1644 }
1645
1646 if (!set_ntstatus( NtQueryInformationProcess( process, ProcessVmCounters, &vmc, sizeof(vmc), NULL )))
1647 return FALSE;
1648
1649 pmc->cb = sizeof(PROCESS_MEMORY_COUNTERS);
1650 pmc->PageFaultCount = vmc.PageFaultCount;
1651 pmc->PeakWorkingSetSize = vmc.PeakWorkingSetSize;
1652 pmc->WorkingSetSize = vmc.WorkingSetSize;
1653 pmc->QuotaPeakPagedPoolUsage = vmc.QuotaPeakPagedPoolUsage;
1654 pmc->QuotaPagedPoolUsage = vmc.QuotaPagedPoolUsage;
1655 pmc->QuotaPeakNonPagedPoolUsage = vmc.QuotaPeakNonPagedPoolUsage;
1656 pmc->QuotaNonPagedPoolUsage = vmc.QuotaNonPagedPoolUsage;
1657 pmc->PagefileUsage = vmc.PagefileUsage;
1658 pmc->PeakPagefileUsage = vmc.PeakPagefileUsage;
1659 return TRUE;
1660}
1661
1662
1663/***********************************************************************
1664 * GetWsChanges (kernelbase.@)
1665 * K32GetWsChanges (kernelbase.@)
1666 */
1667BOOL WINAPI DECLSPEC_HOTPATCH GetWsChanges( HANDLE process, PSAPI_WS_WATCH_INFORMATION *info, DWORD size )
1668{
1669 TRACE( "(%p, %p, %ld)\n", process, info, size );
1670 return set_ntstatus( NtQueryInformationProcess( process, ProcessWorkingSetWatch, info, size, NULL ));
1671}
1672
1673
1674/***********************************************************************
1675 * GetWsChangesEx (kernelbase.@)
1676 * K32GetWsChangesEx (kernelbase.@)
1677 */
1678BOOL WINAPI DECLSPEC_HOTPATCH GetWsChangesEx( HANDLE process, PSAPI_WS_WATCH_INFORMATION_EX *info,
1679 DWORD *size )
1680{
1681 FIXME( "(%p, %p, %p)\n", process, info, size );
1682 SetLastError( ERROR_CALL_NOT_IMPLEMENTED );
1683 return FALSE;
1684}
1685
1686
1687/***********************************************************************
1688 * InitializeProcessForWsWatch (kernelbase.@)
1689 * K32InitializeProcessForWsWatch (kernelbase.@)
1690 */
1691BOOL WINAPI /* DECLSPEC_HOTPATCH */ InitializeProcessForWsWatch( HANDLE process )
1692{
1693 FIXME( "(process=%p): stub\n", process );
1694 return TRUE;
1695}
1696
1697
1698/***********************************************************************
1699 * QueryWorkingSet (kernelbase.@)
1700 * K32QueryWorkingSet (kernelbase.@)
1701 */
1702BOOL WINAPI DECLSPEC_HOTPATCH QueryWorkingSet( HANDLE process, void *buffer, DWORD size )
1703{
1704 TRACE( "(%p, %p, %ld)\n", process, buffer, size );
1705 return set_ntstatus( NtQueryVirtualMemory( process, NULL, MemoryWorkingSetInformation,
1706 buffer, size, NULL ));
1707}
1708
1709
1710/***********************************************************************
1711 * QueryWorkingSetEx (kernelbase.@)
1712 * K32QueryWorkingSetEx (kernelbase.@)
1713 */
1714BOOL WINAPI QueryWorkingSetEx( HANDLE process, void *buffer, DWORD size )
1715{
1716 TRACE( "(%p, %p, %ld)\n", process, buffer, size );
1717 return set_ntstatus( NtQueryVirtualMemory( process, NULL, MemoryWorkingSetExInformation,
1718 buffer, size, NULL ));
1719}
1720
1721
1722/******************************************************************
1723 * QueryFullProcessImageNameA (kernelbase.@)
1724 */
1725BOOL WINAPI DECLSPEC_HOTPATCH QueryFullProcessImageNameA( HANDLE process, DWORD flags,
1726 char *name, DWORD *size )
1727{
1728 BOOL ret;
1729 DWORD sizeW = *size;
1730 WCHAR *nameW = HeapAlloc( GetProcessHeap(), 0, *size * sizeof(WCHAR) );
1731
1732 ret = QueryFullProcessImageNameW( process, flags, nameW, &sizeW );
1733 if (ret) ret = (WideCharToMultiByte( CP_ACP, 0, nameW, -1, name, *size, NULL, NULL) > 0);
1734 if (ret) *size = strlen( name );
1735 HeapFree( GetProcessHeap(), 0, nameW );
1736 return ret;
1737}
1738
1739
1740/******************************************************************
1741 * QueryFullProcessImageNameW (kernelbase.@)
1742 */
1743BOOL WINAPI DECLSPEC_HOTPATCH QueryFullProcessImageNameW( HANDLE process, DWORD flags,
1744 WCHAR *name, DWORD *size )
1745{
1746 BYTE buffer[sizeof(UNICODE_STRING) + MAX_PATH*sizeof(WCHAR)]; /* this buffer should be enough */
1747 UNICODE_STRING *dynamic_buffer = NULL;
1748 UNICODE_STRING *result = NULL;
1749 NTSTATUS status;
1750 DWORD needed;
1751
1752 /* FIXME: Use ProcessImageFileName for the PROCESS_NAME_NATIVE case */
1753 status = NtQueryInformationProcess( process, ProcessImageFileNameWin32, buffer,
1754 sizeof(buffer) - sizeof(WCHAR), &needed );
1755 if (status == STATUS_INFO_LENGTH_MISMATCH)
1756 {
1757 dynamic_buffer = HeapAlloc( GetProcessHeap(), 0, needed + sizeof(WCHAR) );
1758 status = NtQueryInformationProcess( process, ProcessImageFileNameWin32, dynamic_buffer,
1759 needed, &needed );
1760 result = dynamic_buffer;
1761 }
1762 else
1763 result = (UNICODE_STRING *)buffer;
1764
1765 if (status) goto cleanup;
1766
1767 if (flags & PROCESS_NAME_NATIVE && result->Length > 2 * sizeof(WCHAR))
1768 {
1769 WCHAR drive[3];
1770 WCHAR device[1024];
1771 DWORD ntlen, devlen;
1772
1773 if (result->Buffer[1] != ':' || result->Buffer[0] < 'A' || result->Buffer[0] > 'Z')
1774 {
1775 /* We cannot convert it to an NT device path so fail */
1776 status = STATUS_NO_SUCH_DEVICE;
1777 goto cleanup;
1778 }
1779
1780 /* Find this drive's NT device path */
1781 drive[0] = result->Buffer[0];
1782 drive[1] = ':';
1783 drive[2] = 0;
1784 if (!QueryDosDeviceW(drive, device, ARRAY_SIZE(device)))
1785 {
1786 status = STATUS_NO_SUCH_DEVICE;
1787 goto cleanup;
1788 }
1789
1790 devlen = lstrlenW(device);
1791 ntlen = devlen + (result->Length/sizeof(WCHAR) - 2);
1792 if (ntlen + 1 > *size)
1793 {
1794 status = STATUS_BUFFER_TOO_SMALL;
1795 goto cleanup;
1796 }
1797 *size = ntlen;
1798
1799 memcpy( name, device, devlen * sizeof(*device) );
1800 memcpy( name + devlen, result->Buffer + 2, result->Length - 2 * sizeof(WCHAR) );
1801 name[*size] = 0;
1802 TRACE( "NT path: %s\n", debugstr_w(name) );
1803 }
1804 else
1805 {
1806 if (result->Length/sizeof(WCHAR) + 1 > *size)
1807 {
1808 status = STATUS_BUFFER_TOO_SMALL;
1809 goto cleanup;
1810 }
1811
1812 *size = result->Length/sizeof(WCHAR);
1813 memcpy( name, result->Buffer, result->Length );
1814 name[*size] = 0;
1815 }
1816
1817cleanup:
1818 HeapFree( GetProcessHeap(), 0, dynamic_buffer );
1819 return set_ntstatus( status );
1820}