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)
9 /* INCLUDES *****************************************************************/
13 #include <ndk/dbgkfuncs.h>
18 typedef struct _DBGSS_THREAD_DATA
20 struct _DBGSS_THREAD_DATA
*Next
;
26 } DBGSS_THREAD_DATA
, *PDBGSS_THREAD_DATA
;
28 #define DbgSsSetThreadData(d) \
29 NtCurrentTeb()->DbgSsReserved[0] = d
31 #define DbgSsGetThreadData() \
32 ((PDBGSS_THREAD_DATA)NtCurrentTeb()->DbgSsReserved[0])
34 /* PRIVATE FUNCTIONS *********************************************************/
38 K32CreateDBMonMutex(void)
40 static SID_IDENTIFIER_AUTHORITY siaNTAuth
= {SECURITY_NT_AUTHORITY
};
41 static SID_IDENTIFIER_AUTHORITY siaWorldAuth
= {SECURITY_WORLD_SID_AUTHORITY
};
44 /* SIDs to be used in the DACL */
45 PSID psidSystem
= NULL
;
46 PSID psidAdministrators
= NULL
;
47 PSID psidEveryone
= NULL
;
49 /* buffer for the DACL */
50 PVOID pDaclBuf
= NULL
;
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
56 sizeof(ACL
) + (sizeof(ACCESS_ALLOWED_ACE
) -
57 sizeof(((ACCESS_ALLOWED_ACE
*)0)->SidStart
)) * 3;
59 /* security descriptor of the mutex */
60 SECURITY_DESCRIPTOR sdMutexSecurity
;
62 /* attributes of the mutex object we'll create */
63 SECURITY_ATTRIBUTES saMutexAttribs
= {sizeof(saMutexAttribs
),
69 /* first, try to open the mutex */
70 hMutex
= OpenMutexW (SYNCHRONIZE
| READ_CONTROL
| MUTANT_QUERY_STATE
,
79 /* error other than the mutex not being found */
80 else if (GetLastError() != ERROR_FILE_NOT_FOUND
)
86 /* if the mutex doesn't exist, create it */
88 /* first, set up the mutex security */
89 /* allocate the NT AUTHORITY\SYSTEM SID */
90 nErrCode
= RtlAllocateAndInitializeSid(&siaNTAuth
,
92 SECURITY_LOCAL_SYSTEM_RID
,
103 if (!NT_SUCCESS(nErrCode
)) goto l_Cleanup
;
105 /* allocate the BUILTIN\Administrators SID */
106 nErrCode
= RtlAllocateAndInitializeSid(&siaNTAuth
,
108 SECURITY_BUILTIN_DOMAIN_RID
,
109 DOMAIN_ALIAS_RID_ADMINS
,
116 &psidAdministrators
);
119 if (!NT_SUCCESS(nErrCode
)) goto l_Cleanup
;
121 /* allocate the Everyone SID */
122 nErrCode
= RtlAllocateAndInitializeSid(&siaWorldAuth
,
135 if (!NT_SUCCESS(nErrCode
)) goto l_Cleanup
;
137 /* allocate space for the SIDs too */
138 nDaclBufSize
+= RtlLengthSid(psidSystem
);
139 nDaclBufSize
+= RtlLengthSid(psidAdministrators
);
140 nDaclBufSize
+= RtlLengthSid(psidEveryone
);
142 /* allocate the buffer for the DACL */
143 pDaclBuf
= GlobalAlloc(GMEM_FIXED
, nDaclBufSize
);
146 if (pDaclBuf
== NULL
) goto l_Cleanup
;
148 /* create the DACL */
149 nErrCode
= RtlCreateAcl(pDaclBuf
, nDaclBufSize
, ACL_REVISION
);
152 if (!NT_SUCCESS(nErrCode
)) goto l_Cleanup
;
154 /* grant the minimum required access to Everyone */
155 nErrCode
= RtlAddAccessAllowedAce(pDaclBuf
,
163 if (!NT_SUCCESS(nErrCode
)) goto l_Cleanup
;
165 /* grant full access to BUILTIN\Administrators */
166 nErrCode
= RtlAddAccessAllowedAce(pDaclBuf
,
172 if (!NT_SUCCESS(nErrCode
)) goto l_Cleanup
;
174 /* grant full access to NT AUTHORITY\SYSTEM */
175 nErrCode
= RtlAddAccessAllowedAce(pDaclBuf
,
181 if (!NT_SUCCESS(nErrCode
)) goto l_Cleanup
;
183 /* create the security descriptor */
184 nErrCode
= RtlCreateSecurityDescriptor(&sdMutexSecurity
,
185 SECURITY_DESCRIPTOR_REVISION
);
188 if (!NT_SUCCESS(nErrCode
)) goto l_Cleanup
;
190 /* set the descriptor's DACL to the ACL we created */
191 nErrCode
= RtlSetDaclSecurityDescriptor(&sdMutexSecurity
,
197 if (!NT_SUCCESS(nErrCode
)) goto l_Cleanup
;
199 /* create the mutex */
200 hMutex
= CreateMutexW(&saMutexAttribs
, FALSE
, L
"DBWinMutex");
203 /* free the buffers */
204 if (pDaclBuf
) GlobalFree(pDaclBuf
);
205 if (psidEveryone
) RtlFreeSid(psidEveryone
);
206 if (psidAdministrators
) RtlFreeSid(psidAdministrators
);
207 if (psidSystem
) RtlFreeSid(psidSystem
);
214 SaveThreadHandle(IN DWORD dwProcessId
,
218 PDBGSS_THREAD_DATA ThreadData
;
220 /* Allocate a thread structure */
221 ThreadData
= RtlAllocateHeap(RtlGetProcessHeap(),
223 sizeof(DBGSS_THREAD_DATA
));
224 if (!ThreadData
) return;
227 ThreadData
->ThreadHandle
= hThread
;
228 ThreadData
->ProcessId
= dwProcessId
;
229 ThreadData
->ThreadId
= dwThreadId
;
230 ThreadData
->ProcessHandle
= NULL
;
231 ThreadData
->HandleMarked
= FALSE
;
234 ThreadData
->Next
= DbgSsGetThreadData();
235 DbgSsSetThreadData(ThreadData
);
240 SaveProcessHandle(IN DWORD dwProcessId
,
243 PDBGSS_THREAD_DATA ThreadData
;
245 /* Allocate a thread structure */
246 ThreadData
= RtlAllocateHeap(RtlGetProcessHeap(),
248 sizeof(DBGSS_THREAD_DATA
));
249 if (!ThreadData
) return;
252 ThreadData
->ProcessHandle
= hProcess
;
253 ThreadData
->ProcessId
= dwProcessId
;
254 ThreadData
->ThreadId
= 0;
255 ThreadData
->ThreadHandle
= NULL
;
256 ThreadData
->HandleMarked
= FALSE
;
259 ThreadData
->Next
= DbgSsGetThreadData();
260 DbgSsSetThreadData(ThreadData
);
265 MarkThreadHandle(IN DWORD dwThreadId
)
267 PDBGSS_THREAD_DATA ThreadData
;
269 /* Loop all thread data events */
270 for (ThreadData
= DbgSsGetThreadData(); ThreadData
; ThreadData
= ThreadData
->Next
)
272 /* Check if this one matches */
273 if (ThreadData
->ThreadId
== dwThreadId
)
275 /* Mark the structure and break out */
276 ThreadData
->HandleMarked
= TRUE
;
284 MarkProcessHandle(IN DWORD dwProcessId
)
286 PDBGSS_THREAD_DATA ThreadData
;
288 /* Loop all thread data events */
289 for (ThreadData
= DbgSsGetThreadData(); ThreadData
; ThreadData
= ThreadData
->Next
)
291 /* Check if this one matches */
292 if ((ThreadData
->ProcessId
== dwProcessId
) && !(ThreadData
->ThreadId
))
294 /* Mark the structure and break out */
295 ThreadData
->HandleMarked
= TRUE
;
303 RemoveHandles(IN DWORD dwProcessId
,
306 PDBGSS_THREAD_DATA
*ThreadData
;
307 PDBGSS_THREAD_DATA ThisData
;
309 /* Loop all thread data events */
310 ThreadData
= (PDBGSS_THREAD_DATA
*)NtCurrentTeb()->DbgSsReserved
;
311 ThisData
= *ThreadData
;
314 /* Check if this one matches */
315 if ((ThisData
->HandleMarked
) &&
316 ((ThisData
->ProcessId
== dwProcessId
) || (ThisData
->ThreadId
== dwThreadId
)))
318 /* Close open handles */
319 if (ThisData
->ThreadHandle
) CloseHandle(ThisData
->ThreadHandle
);
320 if (ThisData
->ProcessHandle
) CloseHandle(ThisData
->ProcessHandle
);
322 /* Unlink the thread data */
323 *ThreadData
= ThisData
->Next
;
326 RtlFreeHeap(RtlGetProcessHeap(), 0, ThisData
);
330 /* Move to the next one */
331 ThreadData
= &ThisData
->Next
;
333 ThisData
= *ThreadData
;
339 CloseAllProcessHandles(IN DWORD dwProcessId
)
341 PDBGSS_THREAD_DATA
*ThreadData
;
342 PDBGSS_THREAD_DATA ThisData
;
344 /* Loop all thread data events */
345 ThreadData
= (PDBGSS_THREAD_DATA
*)NtCurrentTeb()->DbgSsReserved
;
346 ThisData
= *ThreadData
;
349 /* Check if this one matches */
350 if (ThisData
->ProcessId
== dwProcessId
)
352 /* Close open handles */
353 if (ThisData
->ThreadHandle
) CloseHandle(ThisData
->ThreadHandle
);
354 if (ThisData
->ProcessHandle
) CloseHandle(ThisData
->ProcessHandle
);
356 /* Unlink the thread data */
357 *ThreadData
= ThisData
->Next
;
360 RtlFreeHeap(RtlGetProcessHeap(), 0, ThisData
);
364 /* Move to the next one */
365 ThreadData
= &ThisData
->Next
;
367 ThisData
= *ThreadData
;
373 ProcessIdToHandle(IN DWORD dwProcessId
)
376 OBJECT_ATTRIBUTES ObjectAttributes
;
380 /* If we don't have a PID, look it up */
381 if (dwProcessId
== MAXDWORD
) dwProcessId
= (DWORD_PTR
)CsrGetProcessId();
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
,
393 if (!NT_SUCCESS(Status
))
396 BaseSetLastNTError(Status
);
400 /* Return the handle */
404 /* PUBLIC FUNCTIONS **********************************************************/
411 CheckRemoteDebuggerPresent(IN HANDLE hProcess
,
412 OUT PBOOL pbDebuggerPresent
)
417 /* Make sure we have an output and process*/
418 if (!(pbDebuggerPresent
) || !(hProcess
))
421 SetLastError(ERROR_INVALID_PARAMETER
);
425 /* Check if the process has a debug object/port */
426 Status
= NtQueryInformationProcess(hProcess
,
431 if (NT_SUCCESS(Status
))
433 /* Return the current state */
434 *pbDebuggerPresent
= DebugPort
!= NULL
;
438 /* Otherwise, fail */
439 BaseSetLastNTError(Status
);
448 ContinueDebugEvent(IN DWORD dwProcessId
,
450 IN DWORD dwContinueStatus
)
455 /* Set the Client ID */
456 ClientId
.UniqueProcess
= UlongToHandle(dwProcessId
);
457 ClientId
.UniqueThread
= UlongToHandle(dwThreadId
);
459 /* Continue debugging */
460 Status
= DbgUiContinue(&ClientId
, dwContinueStatus
);
461 if (!NT_SUCCESS(Status
))
464 BaseSetLastNTError(Status
);
468 /* Remove the process/thread handles */
469 RemoveHandles(dwProcessId
, dwThreadId
);
480 DebugActiveProcess(IN DWORD dwProcessId
)
482 NTSTATUS Status
, Status1
;
485 /* Connect to the debugger */
486 Status
= DbgUiConnectToDbg();
487 if (!NT_SUCCESS(Status
))
489 BaseSetLastNTError(Status
);
493 /* Get the process handle */
494 Handle
= ProcessIdToHandle(dwProcessId
);
495 if (!Handle
) return FALSE
;
497 /* Now debug the process */
498 Status
= DbgUiDebugActiveProcess(Handle
);
500 /* Close the handle since we're done */
501 Status1
= NtClose(Handle
);
502 ASSERT(NT_SUCCESS(Status1
));
504 /* Check if debugging worked */
505 if (!NT_SUCCESS(Status
))
508 BaseSetLastNTError(Status
);
521 DebugActiveProcessStop(IN DWORD dwProcessId
)
523 NTSTATUS Status
, Status1
;
526 /* Get the process handle */
527 Handle
= ProcessIdToHandle(dwProcessId
);
528 if (!Handle
) return FALSE
;
530 /* Close all the process handles */
531 CloseAllProcessHandles(dwProcessId
);
533 /* Now stop debugging the process */
534 Status
= DbgUiStopDebugging(Handle
);
535 Status1
= NtClose(Handle
);
536 ASSERT(NT_SUCCESS(Status1
));
538 /* Check for failure */
539 if (!NT_SUCCESS(Status
))
542 SetLastError(ERROR_ACCESS_DENIED
);
555 DebugBreakProcess(IN HANDLE Process
)
559 /* Send the breakin request */
560 Status
= DbgUiIssueRemoteBreakin(Process
);
561 if (!NT_SUCCESS(Status
))
564 BaseSetLastNTError(Status
);
577 DebugSetProcessKillOnExit(IN BOOL KillOnExit
)
583 /* Get the debug object */
584 Handle
= DbgUiGetThreadDebugObject();
588 BaseSetLastNTError(STATUS_INVALID_HANDLE
);
592 /* Now set the kill-on-exit state */
593 State
= KillOnExit
!= 0;
594 Status
= NtSetInformationDebugObject(Handle
,
595 DebugObjectKillProcessOnExitInformation
,
599 if (!NT_SUCCESS(Status
))
602 BaseSetLastNTError(Status
);
615 IsDebuggerPresent(VOID
)
617 return (BOOL
)NtCurrentPeb()->BeingDebugged
;
625 WaitForDebugEvent(IN LPDEBUG_EVENT lpDebugEvent
,
626 IN DWORD dwMilliseconds
)
628 LARGE_INTEGER WaitTime
;
629 PLARGE_INTEGER Timeout
;
630 DBGUI_WAIT_STATE_CHANGE WaitStateChange
;
633 /* Convert to NT Timeout */
634 Timeout
= BaseFormatTimeOut(&WaitTime
, dwMilliseconds
);
636 /* Loop while we keep getting interrupted */
639 /* Call the native API */
640 Status
= DbgUiWaitStateChange(&WaitStateChange
, Timeout
);
641 } while ((Status
== STATUS_ALERTED
) || (Status
== STATUS_USER_APC
));
643 /* Check if the wait failed */
644 if (!(NT_SUCCESS(Status
)) || (Status
== DBG_UNABLE_TO_PROVIDE_HANDLE
))
646 /* Set the error code and quit */
647 BaseSetLastNTError(Status
);
651 /* Check if we timed out */
652 if (Status
== STATUS_TIMEOUT
)
654 /* Fail with a timeout error */
655 SetLastError(ERROR_SEM_TIMEOUT
);
659 /* Convert the structure */
660 Status
= DbgUiConvertStateChangeStructure(&WaitStateChange
, lpDebugEvent
);
661 if (!NT_SUCCESS(Status
))
663 /* Set the error code and quit */
664 BaseSetLastNTError(Status
);
668 /* Check what kind of event this was */
669 switch (lpDebugEvent
->dwDebugEventCode
)
671 /* New thread was created */
672 case CREATE_THREAD_DEBUG_EVENT
:
674 /* Setup the thread data */
675 SaveThreadHandle(lpDebugEvent
->dwProcessId
,
676 lpDebugEvent
->dwThreadId
,
677 lpDebugEvent
->u
.CreateThread
.hThread
);
680 /* New process was created */
681 case CREATE_PROCESS_DEBUG_EVENT
:
683 /* Setup the process data */
684 SaveProcessHandle(lpDebugEvent
->dwProcessId
,
685 lpDebugEvent
->u
.CreateProcessInfo
.hProcess
);
687 /* Setup the thread data */
688 SaveThreadHandle(lpDebugEvent
->dwProcessId
,
689 lpDebugEvent
->dwThreadId
,
690 lpDebugEvent
->u
.CreateProcessInfo
.hThread
);
693 /* Process was exited */
694 case EXIT_PROCESS_DEBUG_EVENT
:
696 /* Mark the thread data as such and fall through */
697 MarkProcessHandle(lpDebugEvent
->dwProcessId
);
699 /* Thread was exited */
700 case EXIT_THREAD_DEBUG_EVENT
:
702 /* Mark the thread data */
703 MarkThreadHandle(lpDebugEvent
->dwThreadId
);
707 case EXCEPTION_DEBUG_EVENT
:
708 case LOAD_DLL_DEBUG_EVENT
:
709 case UNLOAD_DLL_DEBUG_EVENT
:
710 case OUTPUT_DEBUG_STRING_EVENT
:
714 /* Fail anything else */
728 OutputDebugStringA(IN LPCSTR _OutputString
)
732 ULONG_PTR a_nArgs
[2];
734 a_nArgs
[0] = (ULONG_PTR
)(strlen(_OutputString
) + 1);
735 a_nArgs
[1] = (ULONG_PTR
)_OutputString
;
737 /* send the string to the user-mode debugger */
738 RaiseException(DBG_PRINTEXCEPTION_C
, 0, 2, a_nArgs
);
740 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
742 /* no user-mode debugger: try the systemwide debug message monitor, or the
743 kernel debugger as a last resort */
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
;
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
;
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
;
759 /* event: signaled by the debug message monitor when OutputDebugString can write
760 to the shared buffer */
761 volatile HANDLE hDBMonBufferReady
= NULL
;
763 /* event: to be signaled by OutputDebugString when it's done writing to the
765 volatile HANDLE hDBMonDataReady
= NULL
;
767 /* mutex not opened, and no previous attempts to open/create it */
768 if (hDBMonMutex
== NULL
&& !s_bDBMonMutexTriedOpen
)
770 /* open/create the mutex */
771 hDBMonMutex
= K32CreateDBMonMutex();
772 /* store the handle */
773 s_hDBMonMutex
= hDBMonMutex
;
778 volatile PCHAR a_cBuffer
= NULL
;
780 /* opening the mutex failed */
781 if (hDBMonMutex
== NULL
)
783 /* remember next time */
784 s_bDBMonMutexTriedOpen
= TRUE
;
786 /* opening the mutex succeeded */
791 /* synchronize with other invocations of OutputDebugString */
792 WaitForSingleObject(hDBMonMutex
, INFINITE
);
794 /* buffer of the system-wide debug message monitor */
795 hDBMonBuffer
= OpenFileMappingW(SECTION_MAP_WRITE
, FALSE
, L
"DBWIN_BUFFER");
797 /* couldn't open the buffer: send the string to the kernel debugger */
798 if (hDBMonBuffer
== NULL
) break;
801 pDBMonBuffer
= MapViewOfFile(hDBMonBuffer
,
802 SECTION_MAP_READ
| SECTION_MAP_WRITE
,
807 /* couldn't map the buffer: send the string to the kernel debugger */
808 if (pDBMonBuffer
== NULL
) break;
810 /* open the event signaling that the buffer can be accessed */
811 hDBMonBufferReady
= OpenEventW(SYNCHRONIZE
, FALSE
, L
"DBWIN_BUFFER_READY");
813 /* couldn't open the event: send the string to the kernel debugger */
814 if (hDBMonBufferReady
== NULL
) break;
816 /* open the event to be signaled when the buffer has been filled */
817 hDBMonDataReady
= OpenEventW(EVENT_MODIFY_STATE
, FALSE
, L
"DBWIN_DATA_READY");
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
);
828 /* size of the current output block */
829 volatile SIZE_T nRoundLen
;
831 /* size of the remainder of the string */
832 volatile SIZE_T nOutputStringLen
;
834 /* output the whole string */
835 nOutputStringLen
= strlen(_OutputString
);
839 /* we're connected to the debug monitor:
840 write the current block to the shared buffer */
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
)
847 /* timeout or failure: give up */
851 /* write the process id into the buffer */
852 pDBMonBuffer
->ProcessId
= GetCurrentProcessId();
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;
858 nRoundLen
= nOutputStringLen
;
860 /* copy the current block into the buffer */
861 memcpy(pDBMonBuffer
->Buffer
, _OutputString
, nRoundLen
);
863 /* null-terminate the current block */
864 pDBMonBuffer
->Buffer
[nRoundLen
] = 0;
866 /* signal that the data contains meaningful data and can be read */
867 SetEvent(hDBMonDataReady
);
869 /* else, send the current block to the kernel debugger */
872 /* output in blocks of 512 characters */
873 a_cBuffer
= (CHAR
*)HeapAlloc(GetProcessHeap(), 0, 512);
877 DbgPrint("OutputDebugStringA: Failed\n");
881 /* write a maximum of 511 bytes */
882 if (nOutputStringLen
> 510)
885 nRoundLen
= nOutputStringLen
;
887 /* copy the current block */
888 memcpy(a_cBuffer
, _OutputString
, nRoundLen
);
890 /* null-terminate the current block */
891 a_cBuffer
[nRoundLen
] = 0;
893 /* send the current block to the kernel debugger */
894 DbgPrint("%s", a_cBuffer
);
898 HeapFree(GetProcessHeap(), 0, a_cBuffer
);
903 /* move to the next block */
904 _OutputString
+= nRoundLen
;
905 nOutputStringLen
-= nRoundLen
;
907 /* repeat until the string has been fully output */
908 while (nOutputStringLen
> 0);
910 /* ignore access violations and let other exceptions fall through */
911 _SEH2_EXCEPT((_SEH2_GetExceptionCode() == STATUS_ACCESS_VIOLATION
) ? EXCEPTION_EXECUTE_HANDLER
: EXCEPTION_CONTINUE_SEARCH
)
914 HeapFree(GetProcessHeap(), 0, a_cBuffer
);
916 /* string copied verbatim from Microsoft's kernel32.dll */
917 DbgPrint("\nOutputDebugString faulted during output\n");
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
);
929 /* leave the critical section */
930 if (hDBMonDataReady
!= NULL
)
931 ReleaseMutex(hDBMonMutex
);
943 OutputDebugStringW(IN LPCWSTR OutputString
)
945 UNICODE_STRING UnicodeString
;
946 ANSI_STRING AnsiString
;
949 /* convert the string in ANSI */
950 RtlInitUnicodeString(&UnicodeString
, OutputString
);
951 Status
= RtlUnicodeStringToAnsiString(&AnsiString
, &UnicodeString
, TRUE
);
953 /* OutputDebugStringW always prints something, even if conversion fails */
954 if (!NT_SUCCESS(Status
)) AnsiString
.Buffer
= "";
956 /* Output the converted string */
957 OutputDebugStringA(AnsiString
.Buffer
);
959 /* free the converted string */
960 if (NT_SUCCESS(Status
)) RtlFreeAnsiString(&AnsiString
);