[FORMATTING] Remove trailing whitespace. Addendum to 34593d93.
[reactos.git] / dll / win32 / kernel32 / client / debugger.c
1 /*
2 * PROJECT: ReactOS Win32 Base API
3 * LICENSE: GPL - See COPYING in the top level directory
4 * FILE: dll/win32/kernel32/client/debugger.c
5 * PURPOSE: Wrappers for the NT Debug Implementation
6 * PROGRAMMERS: Alex Ionescu (alex.ionescu@reactos.org)
7 */
8
9 /* INCLUDES *****************************************************************/
10
11 #include <k32.h>
12
13 #include <ndk/dbgkfuncs.h>
14
15 #define NDEBUG
16 #include <debug.h>
17
18 typedef struct _DBGSS_THREAD_DATA
19 {
20 struct _DBGSS_THREAD_DATA *Next;
21 HANDLE ThreadHandle;
22 HANDLE ProcessHandle;
23 DWORD ProcessId;
24 DWORD ThreadId;
25 BOOLEAN HandleMarked;
26 } DBGSS_THREAD_DATA, *PDBGSS_THREAD_DATA;
27
28 #define DbgSsSetThreadData(d) \
29 NtCurrentTeb()->DbgSsReserved[0] = d
30
31 #define DbgSsGetThreadData() \
32 ((PDBGSS_THREAD_DATA)NtCurrentTeb()->DbgSsReserved[0])
33
34 /* PRIVATE FUNCTIONS *********************************************************/
35
36 static
37 HANDLE
38 K32CreateDBMonMutex(void)
39 {
40 static SID_IDENTIFIER_AUTHORITY siaNTAuth = {SECURITY_NT_AUTHORITY};
41 static SID_IDENTIFIER_AUTHORITY siaWorldAuth = {SECURITY_WORLD_SID_AUTHORITY};
42 HANDLE hMutex;
43
44 /* SIDs to be used in the DACL */
45 PSID psidSystem = NULL;
46 PSID psidAdministrators = NULL;
47 PSID psidEveryone = NULL;
48
49 /* buffer for the DACL */
50 PVOID pDaclBuf = NULL;
51
52 /* minimum size of the DACL: an ACL descriptor and three ACCESS_ALLOWED_ACE
53 headers. We'll add the size of SIDs when we'll know it
54 */
55 SIZE_T nDaclBufSize =
56 sizeof(ACL) + (sizeof(ACCESS_ALLOWED_ACE) -
57 sizeof(((ACCESS_ALLOWED_ACE*)0)->SidStart)) * 3;
58
59 /* security descriptor of the mutex */
60 SECURITY_DESCRIPTOR sdMutexSecurity;
61
62 /* attributes of the mutex object we'll create */
63 SECURITY_ATTRIBUTES saMutexAttribs = {sizeof(saMutexAttribs),
64 &sdMutexSecurity,
65 TRUE};
66
67 NTSTATUS nErrCode;
68
69 /* first, try to open the mutex */
70 hMutex = OpenMutexW (SYNCHRONIZE | READ_CONTROL | MUTANT_QUERY_STATE,
71 TRUE,
72 L"DBWinMutex");
73
74 if (hMutex != NULL)
75 {
76 /* success */
77 return hMutex;
78 }
79 /* error other than the mutex not being found */
80 else if (GetLastError() != ERROR_FILE_NOT_FOUND)
81 {
82 /* failure */
83 return NULL;
84 }
85
86 /* if the mutex doesn't exist, create it */
87
88 /* first, set up the mutex security */
89 /* allocate the NT AUTHORITY\SYSTEM SID */
90 nErrCode = RtlAllocateAndInitializeSid(&siaNTAuth,
91 1,
92 SECURITY_LOCAL_SYSTEM_RID,
93 0,
94 0,
95 0,
96 0,
97 0,
98 0,
99 0,
100 &psidSystem);
101
102 /* failure */
103 if (!NT_SUCCESS(nErrCode)) goto l_Cleanup;
104
105 /* allocate the BUILTIN\Administrators SID */
106 nErrCode = RtlAllocateAndInitializeSid(&siaNTAuth,
107 2,
108 SECURITY_BUILTIN_DOMAIN_RID,
109 DOMAIN_ALIAS_RID_ADMINS,
110 0,
111 0,
112 0,
113 0,
114 0,
115 0,
116 &psidAdministrators);
117
118 /* failure */
119 if (!NT_SUCCESS(nErrCode)) goto l_Cleanup;
120
121 /* allocate the Everyone SID */
122 nErrCode = RtlAllocateAndInitializeSid(&siaWorldAuth,
123 1,
124 0,
125 0,
126 0,
127 0,
128 0,
129 0,
130 0,
131 0,
132 &psidEveryone);
133
134 /* failure */
135 if (!NT_SUCCESS(nErrCode)) goto l_Cleanup;
136
137 /* allocate space for the SIDs too */
138 nDaclBufSize += RtlLengthSid(psidSystem);
139 nDaclBufSize += RtlLengthSid(psidAdministrators);
140 nDaclBufSize += RtlLengthSid(psidEveryone);
141
142 /* allocate the buffer for the DACL */
143 pDaclBuf = GlobalAlloc(GMEM_FIXED, nDaclBufSize);
144
145 /* failure */
146 if (pDaclBuf == NULL) goto l_Cleanup;
147
148 /* create the DACL */
149 nErrCode = RtlCreateAcl(pDaclBuf, nDaclBufSize, ACL_REVISION);
150
151 /* failure */
152 if (!NT_SUCCESS(nErrCode)) goto l_Cleanup;
153
154 /* grant the minimum required access to Everyone */
155 nErrCode = RtlAddAccessAllowedAce(pDaclBuf,
156 ACL_REVISION,
157 SYNCHRONIZE |
158 READ_CONTROL |
159 MUTANT_QUERY_STATE,
160 psidEveryone);
161
162 /* failure */
163 if (!NT_SUCCESS(nErrCode)) goto l_Cleanup;
164
165 /* grant full access to BUILTIN\Administrators */
166 nErrCode = RtlAddAccessAllowedAce(pDaclBuf,
167 ACL_REVISION,
168 MUTANT_ALL_ACCESS,
169 psidAdministrators);
170
171 /* failure */
172 if (!NT_SUCCESS(nErrCode)) goto l_Cleanup;
173
174 /* grant full access to NT AUTHORITY\SYSTEM */
175 nErrCode = RtlAddAccessAllowedAce(pDaclBuf,
176 ACL_REVISION,
177 MUTANT_ALL_ACCESS,
178 psidSystem);
179
180 /* failure */
181 if (!NT_SUCCESS(nErrCode)) goto l_Cleanup;
182
183 /* create the security descriptor */
184 nErrCode = RtlCreateSecurityDescriptor(&sdMutexSecurity,
185 SECURITY_DESCRIPTOR_REVISION);
186
187 /* failure */
188 if (!NT_SUCCESS(nErrCode)) goto l_Cleanup;
189
190 /* set the descriptor's DACL to the ACL we created */
191 nErrCode = RtlSetDaclSecurityDescriptor(&sdMutexSecurity,
192 TRUE,
193 pDaclBuf,
194 FALSE);
195
196 /* failure */
197 if (!NT_SUCCESS(nErrCode)) goto l_Cleanup;
198
199 /* create the mutex */
200 hMutex = CreateMutexW(&saMutexAttribs, FALSE, L"DBWinMutex");
201
202 l_Cleanup:
203 /* free the buffers */
204 if (pDaclBuf) GlobalFree(pDaclBuf);
205 if (psidEveryone) RtlFreeSid(psidEveryone);
206 if (psidAdministrators) RtlFreeSid(psidAdministrators);
207 if (psidSystem) RtlFreeSid(psidSystem);
208
209 return hMutex;
210 }
211
212 VOID
213 WINAPI
214 SaveThreadHandle(IN DWORD dwProcessId,
215 IN DWORD dwThreadId,
216 IN HANDLE hThread)
217 {
218 PDBGSS_THREAD_DATA ThreadData;
219
220 /* Allocate a thread structure */
221 ThreadData = RtlAllocateHeap(RtlGetProcessHeap(),
222 0,
223 sizeof(DBGSS_THREAD_DATA));
224 if (!ThreadData) return;
225
226 /* Fill it out */
227 ThreadData->ThreadHandle = hThread;
228 ThreadData->ProcessId = dwProcessId;
229 ThreadData->ThreadId = dwThreadId;
230 ThreadData->ProcessHandle = NULL;
231 ThreadData->HandleMarked = FALSE;
232
233 /* Link it */
234 ThreadData->Next = DbgSsGetThreadData();
235 DbgSsSetThreadData(ThreadData);
236 }
237
238 VOID
239 WINAPI
240 SaveProcessHandle(IN DWORD dwProcessId,
241 IN HANDLE hProcess)
242 {
243 PDBGSS_THREAD_DATA ThreadData;
244
245 /* Allocate a thread structure */
246 ThreadData = RtlAllocateHeap(RtlGetProcessHeap(),
247 0,
248 sizeof(DBGSS_THREAD_DATA));
249 if (!ThreadData) return;
250
251 /* Fill it out */
252 ThreadData->ProcessHandle = hProcess;
253 ThreadData->ProcessId = dwProcessId;
254 ThreadData->ThreadId = 0;
255 ThreadData->ThreadHandle = NULL;
256 ThreadData->HandleMarked = FALSE;
257
258 /* Link it */
259 ThreadData->Next = DbgSsGetThreadData();
260 DbgSsSetThreadData(ThreadData);
261 }
262
263 VOID
264 WINAPI
265 MarkThreadHandle(IN DWORD dwThreadId)
266 {
267 PDBGSS_THREAD_DATA ThreadData;
268
269 /* Loop all thread data events */
270 for (ThreadData = DbgSsGetThreadData(); ThreadData; ThreadData = ThreadData->Next)
271 {
272 /* Check if this one matches */
273 if (ThreadData->ThreadId == dwThreadId)
274 {
275 /* Mark the structure and break out */
276 ThreadData->HandleMarked = TRUE;
277 break;
278 }
279 }
280 }
281
282 VOID
283 WINAPI
284 MarkProcessHandle(IN DWORD dwProcessId)
285 {
286 PDBGSS_THREAD_DATA ThreadData;
287
288 /* Loop all thread data events */
289 for (ThreadData = DbgSsGetThreadData(); ThreadData; ThreadData = ThreadData->Next)
290 {
291 /* Check if this one matches */
292 if ((ThreadData->ProcessId == dwProcessId) && !(ThreadData->ThreadId))
293 {
294 /* Mark the structure and break out */
295 ThreadData->HandleMarked = TRUE;
296 break;
297 }
298 }
299 }
300
301 VOID
302 WINAPI
303 RemoveHandles(IN DWORD dwProcessId,
304 IN DWORD dwThreadId)
305 {
306 PDBGSS_THREAD_DATA *ThreadData;
307 PDBGSS_THREAD_DATA ThisData;
308
309 /* Loop all thread data events */
310 ThreadData = (PDBGSS_THREAD_DATA*)NtCurrentTeb()->DbgSsReserved;
311 ThisData = *ThreadData;
312 while(ThisData)
313 {
314 /* Check if this one matches */
315 if ((ThisData->HandleMarked) &&
316 ((ThisData->ProcessId == dwProcessId) || (ThisData->ThreadId == dwThreadId)))
317 {
318 /* Close open handles */
319 if (ThisData->ThreadHandle) CloseHandle(ThisData->ThreadHandle);
320 if (ThisData->ProcessHandle) CloseHandle(ThisData->ProcessHandle);
321
322 /* Unlink the thread data */
323 *ThreadData = ThisData->Next;
324
325 /* Free it*/
326 RtlFreeHeap(RtlGetProcessHeap(), 0, ThisData);
327 }
328 else
329 {
330 /* Move to the next one */
331 ThreadData = &ThisData->Next;
332 }
333 ThisData = *ThreadData;
334 }
335 }
336
337 VOID
338 WINAPI
339 CloseAllProcessHandles(IN DWORD dwProcessId)
340 {
341 PDBGSS_THREAD_DATA *ThreadData;
342 PDBGSS_THREAD_DATA ThisData;
343
344 /* Loop all thread data events */
345 ThreadData = (PDBGSS_THREAD_DATA*)NtCurrentTeb()->DbgSsReserved;
346 ThisData = *ThreadData;
347 while(ThisData)
348 {
349 /* Check if this one matches */
350 if (ThisData->ProcessId == dwProcessId)
351 {
352 /* Close open handles */
353 if (ThisData->ThreadHandle) CloseHandle(ThisData->ThreadHandle);
354 if (ThisData->ProcessHandle) CloseHandle(ThisData->ProcessHandle);
355
356 /* Unlink the thread data */
357 *ThreadData = ThisData->Next;
358
359 /* Free it*/
360 RtlFreeHeap(RtlGetProcessHeap(), 0, ThisData);
361 }
362 else
363 {
364 /* Move to the next one */
365 ThreadData = &ThisData->Next;
366 }
367 ThisData = *ThreadData;
368 }
369 }
370
371 HANDLE
372 WINAPI
373 ProcessIdToHandle(IN DWORD dwProcessId)
374 {
375 NTSTATUS Status;
376 OBJECT_ATTRIBUTES ObjectAttributes;
377 HANDLE Handle;
378 CLIENT_ID ClientId;
379
380 /* If we don't have a PID, look it up */
381 if (dwProcessId == MAXDWORD) dwProcessId = (DWORD_PTR)CsrGetProcessId();
382
383 /* Open a handle to the process */
384 ClientId.UniqueThread = NULL;
385 ClientId.UniqueProcess = UlongToHandle(dwProcessId);
386 InitializeObjectAttributes(&ObjectAttributes, NULL, 0, NULL, NULL);
387 Status = NtOpenProcess(&Handle,
388 PROCESS_CREATE_THREAD | PROCESS_VM_OPERATION |
389 PROCESS_VM_WRITE | PROCESS_VM_READ |
390 PROCESS_SUSPEND_RESUME | PROCESS_QUERY_INFORMATION,
391 &ObjectAttributes,
392 &ClientId);
393 if (!NT_SUCCESS(Status))
394 {
395 /* Fail */
396 BaseSetLastNTError(Status);
397 return 0;
398 }
399
400 /* Return the handle */
401 return Handle;
402 }
403
404 /* PUBLIC FUNCTIONS **********************************************************/
405
406 /*
407 * @implemented
408 */
409 BOOL
410 WINAPI
411 CheckRemoteDebuggerPresent(IN HANDLE hProcess,
412 OUT PBOOL pbDebuggerPresent)
413 {
414 HANDLE DebugPort;
415 NTSTATUS Status;
416
417 /* Make sure we have an output and process*/
418 if (!(pbDebuggerPresent) || !(hProcess))
419 {
420 /* Fail */
421 SetLastError(ERROR_INVALID_PARAMETER);
422 return FALSE;
423 }
424
425 /* Check if the process has a debug object/port */
426 Status = NtQueryInformationProcess(hProcess,
427 ProcessDebugPort,
428 &DebugPort,
429 sizeof(DebugPort),
430 NULL);
431 if (NT_SUCCESS(Status))
432 {
433 /* Return the current state */
434 *pbDebuggerPresent = DebugPort != NULL;
435 return TRUE;
436 }
437
438 /* Otherwise, fail */
439 BaseSetLastNTError(Status);
440 return FALSE;
441 }
442
443 /*
444 * @implemented
445 */
446 BOOL
447 WINAPI
448 ContinueDebugEvent(IN DWORD dwProcessId,
449 IN DWORD dwThreadId,
450 IN DWORD dwContinueStatus)
451 {
452 CLIENT_ID ClientId;
453 NTSTATUS Status;
454
455 /* Set the Client ID */
456 ClientId.UniqueProcess = UlongToHandle(dwProcessId);
457 ClientId.UniqueThread = UlongToHandle(dwThreadId);
458
459 /* Continue debugging */
460 Status = DbgUiContinue(&ClientId, dwContinueStatus);
461 if (!NT_SUCCESS(Status))
462 {
463 /* Fail */
464 BaseSetLastNTError(Status);
465 return FALSE;
466 }
467
468 /* Remove the process/thread handles */
469 RemoveHandles(dwProcessId, dwThreadId);
470
471 /* Success */
472 return TRUE;
473 }
474
475 /*
476 * @implemented
477 */
478 BOOL
479 WINAPI
480 DebugActiveProcess(IN DWORD dwProcessId)
481 {
482 NTSTATUS Status, Status1;
483 HANDLE Handle;
484
485 /* Connect to the debugger */
486 Status = DbgUiConnectToDbg();
487 if (!NT_SUCCESS(Status))
488 {
489 BaseSetLastNTError(Status);
490 return FALSE;
491 }
492
493 /* Get the process handle */
494 Handle = ProcessIdToHandle(dwProcessId);
495 if (!Handle) return FALSE;
496
497 /* Now debug the process */
498 Status = DbgUiDebugActiveProcess(Handle);
499
500 /* Close the handle since we're done */
501 Status1 = NtClose(Handle);
502 ASSERT(NT_SUCCESS(Status1));
503
504 /* Check if debugging worked */
505 if (!NT_SUCCESS(Status))
506 {
507 /* Fail */
508 BaseSetLastNTError(Status);
509 return FALSE;
510 }
511
512 /* Success */
513 return TRUE;
514 }
515
516 /*
517 * @implemented
518 */
519 BOOL
520 WINAPI
521 DebugActiveProcessStop(IN DWORD dwProcessId)
522 {
523 NTSTATUS Status, Status1;
524 HANDLE Handle;
525
526 /* Get the process handle */
527 Handle = ProcessIdToHandle(dwProcessId);
528 if (!Handle) return FALSE;
529
530 /* Close all the process handles */
531 CloseAllProcessHandles(dwProcessId);
532
533 /* Now stop debugging the process */
534 Status = DbgUiStopDebugging(Handle);
535 Status1 = NtClose(Handle);
536 ASSERT(NT_SUCCESS(Status1));
537
538 /* Check for failure */
539 if (!NT_SUCCESS(Status))
540 {
541 /* Fail */
542 SetLastError(ERROR_ACCESS_DENIED);
543 return FALSE;
544 }
545
546 /* Success */
547 return TRUE;
548 }
549
550 /*
551 * @implemented
552 */
553 BOOL
554 WINAPI
555 DebugBreakProcess(IN HANDLE Process)
556 {
557 NTSTATUS Status;
558
559 /* Send the breakin request */
560 Status = DbgUiIssueRemoteBreakin(Process);
561 if (!NT_SUCCESS(Status))
562 {
563 /* Failure */
564 BaseSetLastNTError(Status);
565 return FALSE;
566 }
567
568 /* Success */
569 return TRUE;
570 }
571
572 /*
573 * @implemented
574 */
575 BOOL
576 WINAPI
577 DebugSetProcessKillOnExit(IN BOOL KillOnExit)
578 {
579 HANDLE Handle;
580 NTSTATUS Status;
581 ULONG State;
582
583 /* Get the debug object */
584 Handle = DbgUiGetThreadDebugObject();
585 if (!Handle)
586 {
587 /* Fail */
588 BaseSetLastNTError(STATUS_INVALID_HANDLE);
589 return FALSE;
590 }
591
592 /* Now set the kill-on-exit state */
593 State = KillOnExit != 0;
594 Status = NtSetInformationDebugObject(Handle,
595 DebugObjectKillProcessOnExitInformation,
596 &State,
597 sizeof(State),
598 NULL);
599 if (!NT_SUCCESS(Status))
600 {
601 /* Fail */
602 BaseSetLastNTError(Status);
603 return FALSE;
604 }
605
606 /* Success */
607 return TRUE;
608 }
609
610 /*
611 * @implemented
612 */
613 BOOL
614 WINAPI
615 IsDebuggerPresent(VOID)
616 {
617 return (BOOL)NtCurrentPeb()->BeingDebugged;
618 }
619
620 /*
621 * @implemented
622 */
623 BOOL
624 WINAPI
625 WaitForDebugEvent(IN LPDEBUG_EVENT lpDebugEvent,
626 IN DWORD dwMilliseconds)
627 {
628 LARGE_INTEGER WaitTime;
629 PLARGE_INTEGER Timeout;
630 DBGUI_WAIT_STATE_CHANGE WaitStateChange;
631 NTSTATUS Status;
632
633 /* Convert to NT Timeout */
634 Timeout = BaseFormatTimeOut(&WaitTime, dwMilliseconds);
635
636 /* Loop while we keep getting interrupted */
637 do
638 {
639 /* Call the native API */
640 Status = DbgUiWaitStateChange(&WaitStateChange, Timeout);
641 } while ((Status == STATUS_ALERTED) || (Status == STATUS_USER_APC));
642
643 /* Check if the wait failed */
644 if (!(NT_SUCCESS(Status)) || (Status == DBG_UNABLE_TO_PROVIDE_HANDLE))
645 {
646 /* Set the error code and quit */
647 BaseSetLastNTError(Status);
648 return FALSE;
649 }
650
651 /* Check if we timed out */
652 if (Status == STATUS_TIMEOUT)
653 {
654 /* Fail with a timeout error */
655 SetLastError(ERROR_SEM_TIMEOUT);
656 return FALSE;
657 }
658
659 /* Convert the structure */
660 Status = DbgUiConvertStateChangeStructure(&WaitStateChange, lpDebugEvent);
661 if (!NT_SUCCESS(Status))
662 {
663 /* Set the error code and quit */
664 BaseSetLastNTError(Status);
665 return FALSE;
666 }
667
668 /* Check what kind of event this was */
669 switch (lpDebugEvent->dwDebugEventCode)
670 {
671 /* New thread was created */
672 case CREATE_THREAD_DEBUG_EVENT:
673
674 /* Setup the thread data */
675 SaveThreadHandle(lpDebugEvent->dwProcessId,
676 lpDebugEvent->dwThreadId,
677 lpDebugEvent->u.CreateThread.hThread);
678 break;
679
680 /* New process was created */
681 case CREATE_PROCESS_DEBUG_EVENT:
682
683 /* Setup the process data */
684 SaveProcessHandle(lpDebugEvent->dwProcessId,
685 lpDebugEvent->u.CreateProcessInfo.hProcess);
686
687 /* Setup the thread data */
688 SaveThreadHandle(lpDebugEvent->dwProcessId,
689 lpDebugEvent->dwThreadId,
690 lpDebugEvent->u.CreateProcessInfo.hThread);
691 break;
692
693 /* Process was exited */
694 case EXIT_PROCESS_DEBUG_EVENT:
695
696 /* Mark the thread data as such and fall through */
697 MarkProcessHandle(lpDebugEvent->dwProcessId);
698
699 /* Thread was exited */
700 case EXIT_THREAD_DEBUG_EVENT:
701
702 /* Mark the thread data */
703 MarkThreadHandle(lpDebugEvent->dwThreadId);
704 break;
705
706 /* Nothing to do */
707 case EXCEPTION_DEBUG_EVENT:
708 case LOAD_DLL_DEBUG_EVENT:
709 case UNLOAD_DLL_DEBUG_EVENT:
710 case OUTPUT_DEBUG_STRING_EVENT:
711 case RIP_EVENT:
712 break;
713
714 /* Fail anything else */
715 default:
716 return FALSE;
717 }
718
719 /* Return success */
720 return TRUE;
721 }
722
723 /*
724 * @implemented
725 */
726 VOID
727 WINAPI
728 OutputDebugStringA(IN LPCSTR _OutputString)
729 {
730 _SEH2_TRY
731 {
732 ULONG_PTR a_nArgs[2];
733
734 a_nArgs[0] = (ULONG_PTR)(strlen(_OutputString) + 1);
735 a_nArgs[1] = (ULONG_PTR)_OutputString;
736
737 /* send the string to the user-mode debugger */
738 RaiseException(DBG_PRINTEXCEPTION_C, 0, 2, a_nArgs);
739 }
740 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
741 {
742 /* no user-mode debugger: try the systemwide debug message monitor, or the
743 kernel debugger as a last resort */
744
745 /* mutex used to synchronize invocations of OutputDebugString */
746 static HANDLE s_hDBMonMutex = NULL;
747 /* true if we already attempted to open/create the mutex */
748 static BOOL s_bDBMonMutexTriedOpen = FALSE;
749
750 /* local copy of the mutex handle */
751 volatile HANDLE hDBMonMutex = s_hDBMonMutex;
752 /* handle to the Section of the shared buffer */
753 volatile HANDLE hDBMonBuffer = NULL;
754
755 /* pointer to the mapped view of the shared buffer. It consist of the current
756 process id followed by the message string */
757 struct { DWORD ProcessId; CHAR Buffer[1]; } * pDBMonBuffer = NULL;
758
759 /* event: signaled by the debug message monitor when OutputDebugString can write
760 to the shared buffer */
761 volatile HANDLE hDBMonBufferReady = NULL;
762
763 /* event: to be signaled by OutputDebugString when it's done writing to the
764 shared buffer */
765 volatile HANDLE hDBMonDataReady = NULL;
766
767 /* mutex not opened, and no previous attempts to open/create it */
768 if (hDBMonMutex == NULL && !s_bDBMonMutexTriedOpen)
769 {
770 /* open/create the mutex */
771 hDBMonMutex = K32CreateDBMonMutex();
772 /* store the handle */
773 s_hDBMonMutex = hDBMonMutex;
774 }
775
776 _SEH2_TRY
777 {
778 volatile PCHAR a_cBuffer = NULL;
779
780 /* opening the mutex failed */
781 if (hDBMonMutex == NULL)
782 {
783 /* remember next time */
784 s_bDBMonMutexTriedOpen = TRUE;
785 }
786 /* opening the mutex succeeded */
787 else
788 {
789 do
790 {
791 /* synchronize with other invocations of OutputDebugString */
792 WaitForSingleObject(hDBMonMutex, INFINITE);
793
794 /* buffer of the system-wide debug message monitor */
795 hDBMonBuffer = OpenFileMappingW(SECTION_MAP_WRITE, FALSE, L"DBWIN_BUFFER");
796
797 /* couldn't open the buffer: send the string to the kernel debugger */
798 if (hDBMonBuffer == NULL) break;
799
800 /* map the buffer */
801 pDBMonBuffer = MapViewOfFile(hDBMonBuffer,
802 SECTION_MAP_READ | SECTION_MAP_WRITE,
803 0,
804 0,
805 0);
806
807 /* couldn't map the buffer: send the string to the kernel debugger */
808 if (pDBMonBuffer == NULL) break;
809
810 /* open the event signaling that the buffer can be accessed */
811 hDBMonBufferReady = OpenEventW(SYNCHRONIZE, FALSE, L"DBWIN_BUFFER_READY");
812
813 /* couldn't open the event: send the string to the kernel debugger */
814 if (hDBMonBufferReady == NULL) break;
815
816 /* open the event to be signaled when the buffer has been filled */
817 hDBMonDataReady = OpenEventW(EVENT_MODIFY_STATE, FALSE, L"DBWIN_DATA_READY");
818 }
819 while(0);
820
821 /* we couldn't connect to the system-wide debug message monitor: send the
822 string to the kernel debugger */
823 if (hDBMonDataReady == NULL) ReleaseMutex(hDBMonMutex);
824 }
825
826 _SEH2_TRY
827 {
828 /* size of the current output block */
829 volatile SIZE_T nRoundLen;
830
831 /* size of the remainder of the string */
832 volatile SIZE_T nOutputStringLen;
833
834 /* output the whole string */
835 nOutputStringLen = strlen(_OutputString);
836
837 do
838 {
839 /* we're connected to the debug monitor:
840 write the current block to the shared buffer */
841 if (hDBMonDataReady)
842 {
843 /* wait a maximum of 10 seconds for the debug monitor
844 to finish processing the shared buffer */
845 if (WaitForSingleObject(hDBMonBufferReady, 10000) != WAIT_OBJECT_0)
846 {
847 /* timeout or failure: give up */
848 break;
849 }
850
851 /* write the process id into the buffer */
852 pDBMonBuffer->ProcessId = GetCurrentProcessId();
853
854 /* write only as many bytes as they fit in the buffer */
855 if (nOutputStringLen > (PAGE_SIZE - sizeof(DWORD) - 1))
856 nRoundLen = PAGE_SIZE - sizeof(DWORD) - 1;
857 else
858 nRoundLen = nOutputStringLen;
859
860 /* copy the current block into the buffer */
861 memcpy(pDBMonBuffer->Buffer, _OutputString, nRoundLen);
862
863 /* null-terminate the current block */
864 pDBMonBuffer->Buffer[nRoundLen] = 0;
865
866 /* signal that the data contains meaningful data and can be read */
867 SetEvent(hDBMonDataReady);
868 }
869 /* else, send the current block to the kernel debugger */
870 else
871 {
872 /* output in blocks of 512 characters */
873 a_cBuffer = (CHAR*)HeapAlloc(GetProcessHeap(), 0, 512);
874
875 if (!a_cBuffer)
876 {
877 DbgPrint("OutputDebugStringA: Failed\n");
878 break;
879 }
880
881 /* write a maximum of 511 bytes */
882 if (nOutputStringLen > 510)
883 nRoundLen = 510;
884 else
885 nRoundLen = nOutputStringLen;
886
887 /* copy the current block */
888 memcpy(a_cBuffer, _OutputString, nRoundLen);
889
890 /* null-terminate the current block */
891 a_cBuffer[nRoundLen] = 0;
892
893 /* send the current block to the kernel debugger */
894 DbgPrint("%s", a_cBuffer);
895
896 if (a_cBuffer)
897 {
898 HeapFree(GetProcessHeap(), 0, a_cBuffer);
899 a_cBuffer = NULL;
900 }
901 }
902
903 /* move to the next block */
904 _OutputString += nRoundLen;
905 nOutputStringLen -= nRoundLen;
906 }
907 /* repeat until the string has been fully output */
908 while (nOutputStringLen > 0);
909 }
910 /* ignore access violations and let other exceptions fall through */
911 _SEH2_EXCEPT((_SEH2_GetExceptionCode() == STATUS_ACCESS_VIOLATION) ? EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH)
912 {
913 if (a_cBuffer)
914 HeapFree(GetProcessHeap(), 0, a_cBuffer);
915
916 /* string copied verbatim from Microsoft's kernel32.dll */
917 DbgPrint("\nOutputDebugString faulted during output\n");
918 }
919 _SEH2_END;
920 }
921 _SEH2_FINALLY
922 {
923 /* close all the still open resources */
924 if (hDBMonBufferReady) CloseHandle(hDBMonBufferReady);
925 if (pDBMonBuffer) UnmapViewOfFile(pDBMonBuffer);
926 if (hDBMonBuffer) CloseHandle(hDBMonBuffer);
927 if (hDBMonDataReady) CloseHandle(hDBMonDataReady);
928
929 /* leave the critical section */
930 if (hDBMonDataReady != NULL)
931 ReleaseMutex(hDBMonMutex);
932 }
933 _SEH2_END;
934 }
935 _SEH2_END;
936 }
937
938 /*
939 * @implemented
940 */
941 VOID
942 WINAPI
943 OutputDebugStringW(IN LPCWSTR OutputString)
944 {
945 UNICODE_STRING UnicodeString;
946 ANSI_STRING AnsiString;
947 NTSTATUS Status;
948
949 /* convert the string in ANSI */
950 RtlInitUnicodeString(&UnicodeString, OutputString);
951 Status = RtlUnicodeStringToAnsiString(&AnsiString, &UnicodeString, TRUE);
952
953 /* OutputDebugStringW always prints something, even if conversion fails */
954 if (!NT_SUCCESS(Status)) AnsiString.Buffer = "";
955
956 /* Output the converted string */
957 OutputDebugStringA(AnsiString.Buffer);
958
959 /* free the converted string */
960 if (NT_SUCCESS(Status)) RtlFreeAnsiString(&AnsiString);
961 }
962
963 /* EOF */