2 * PROJECT: ReactOS Win32 Base API
3 * LICENSE: GPL - See COPYING in the top level directory
4 * FILE: dll/win32/kernel32/debug/debugger.c
5 * PURPOSE: Wrappers for the NT Debug Implementation
6 * PROGRAMMERS: Alex Ionescu (alex.ionescu@reactos.org)
9 /* INCLUDES *****************************************************************/
15 typedef struct _DBGSS_THREAD_DATA
17 struct _DBGSS_THREAD_DATA
*Next
;
23 } DBGSS_THREAD_DATA
, *PDBGSS_THREAD_DATA
;
25 #define DbgSsSetThreadData(d) \
26 NtCurrentTeb()->DbgSsReserved[0] = d
28 #define DbgSsGetThreadData() \
29 ((PDBGSS_THREAD_DATA)NtCurrentTeb()->DbgSsReserved[0])
31 /* PRIVATE FUNCTIONS *********************************************************/
35 K32CreateDBMonMutex(void)
37 static SID_IDENTIFIER_AUTHORITY siaNTAuth
= {SECURITY_NT_AUTHORITY
};
38 static SID_IDENTIFIER_AUTHORITY siaWorldAuth
= {SECURITY_WORLD_SID_AUTHORITY
};
41 /* SIDs to be used in the DACL */
42 PSID psidSystem
= NULL
;
43 PSID psidAdministrators
= NULL
;
44 PSID psidEveryone
= NULL
;
46 /* buffer for the DACL */
47 PVOID pDaclBuf
= NULL
;
49 /* minimum size of the DACL: an ACL descriptor and three ACCESS_ALLOWED_ACE
50 headers. We'll add the size of SIDs when we'll know it
53 sizeof(ACL
) + (sizeof(ACCESS_ALLOWED_ACE
) -
54 sizeof(((ACCESS_ALLOWED_ACE
*)0)->SidStart
)) * 3;
56 /* security descriptor of the mutex */
57 SECURITY_DESCRIPTOR sdMutexSecurity
;
59 /* attributes of the mutex object we'll create */
60 SECURITY_ATTRIBUTES saMutexAttribs
= {sizeof(saMutexAttribs
),
66 /* first, try to open the mutex */
67 hMutex
= OpenMutexW (SYNCHRONIZE
| READ_CONTROL
| MUTANT_QUERY_STATE
,
76 /* error other than the mutex not being found */
77 else if(GetLastError() != ERROR_FILE_NOT_FOUND
)
83 /* if the mutex doesn't exist, create it */
85 /* first, set up the mutex security */
86 /* allocate the NT AUTHORITY\SYSTEM SID */
87 nErrCode
= RtlAllocateAndInitializeSid(&siaNTAuth
,
89 SECURITY_LOCAL_SYSTEM_RID
,
100 if(!NT_SUCCESS(nErrCode
)) goto l_Cleanup
;
102 /* allocate the BUILTIN\Administrators SID */
103 nErrCode
= RtlAllocateAndInitializeSid(&siaNTAuth
,
105 SECURITY_BUILTIN_DOMAIN_RID
,
106 DOMAIN_ALIAS_RID_ADMINS
,
113 &psidAdministrators
);
116 if(!NT_SUCCESS(nErrCode
)) goto l_Cleanup
;
118 /* allocate the Everyone SID */
119 nErrCode
= RtlAllocateAndInitializeSid(&siaWorldAuth
,
132 if(!NT_SUCCESS(nErrCode
)) goto l_Cleanup
;
134 /* allocate space for the SIDs too */
135 nDaclBufSize
+= RtlLengthSid(psidSystem
);
136 nDaclBufSize
+= RtlLengthSid(psidAdministrators
);
137 nDaclBufSize
+= RtlLengthSid(psidEveryone
);
139 /* allocate the buffer for the DACL */
140 pDaclBuf
= GlobalAlloc(GMEM_FIXED
, nDaclBufSize
);
143 if(pDaclBuf
== NULL
) goto l_Cleanup
;
145 /* create the DACL */
146 nErrCode
= RtlCreateAcl(pDaclBuf
, nDaclBufSize
, ACL_REVISION
);
149 if(!NT_SUCCESS(nErrCode
)) goto l_Cleanup
;
151 /* grant the minimum required access to Everyone */
152 nErrCode
= RtlAddAccessAllowedAce(pDaclBuf
,
160 if(!NT_SUCCESS(nErrCode
)) goto l_Cleanup
;
162 /* grant full access to BUILTIN\Administrators */
163 nErrCode
= RtlAddAccessAllowedAce(pDaclBuf
,
169 if(!NT_SUCCESS(nErrCode
)) goto l_Cleanup
;
171 /* grant full access to NT AUTHORITY\SYSTEM */
172 nErrCode
= RtlAddAccessAllowedAce(pDaclBuf
,
178 if(!NT_SUCCESS(nErrCode
)) goto l_Cleanup
;
180 /* create the security descriptor */
181 nErrCode
= RtlCreateSecurityDescriptor(&sdMutexSecurity
,
182 SECURITY_DESCRIPTOR_REVISION
);
185 if(!NT_SUCCESS(nErrCode
)) goto l_Cleanup
;
187 /* set the descriptor's DACL to the ACL we created */
188 nErrCode
= RtlSetDaclSecurityDescriptor(&sdMutexSecurity
,
194 if(!NT_SUCCESS(nErrCode
)) goto l_Cleanup
;
196 /* create the mutex */
197 hMutex
= CreateMutexW(&saMutexAttribs
, FALSE
, L
"DBWinMutex");
200 /* free the buffers */
201 if(pDaclBuf
) GlobalFree(pDaclBuf
);
202 if(psidEveryone
) RtlFreeSid(psidEveryone
);
203 if(psidAdministrators
) RtlFreeSid(psidAdministrators
);
204 if(psidSystem
) RtlFreeSid(psidSystem
);
211 SaveThreadHandle(IN DWORD dwProcessId
,
215 PDBGSS_THREAD_DATA ThreadData
;
217 /* Allocate a thread structure */
218 ThreadData
= RtlAllocateHeap(RtlGetProcessHeap(),
220 sizeof(DBGSS_THREAD_DATA
));
221 if (!ThreadData
) return;
224 ThreadData
->ThreadHandle
= hThread
;
225 ThreadData
->ProcessId
= dwProcessId
;
226 ThreadData
->ThreadId
= dwThreadId
;
227 ThreadData
->ProcessHandle
= NULL
;
228 ThreadData
->HandleMarked
= FALSE
;
231 ThreadData
->Next
= DbgSsGetThreadData();
232 DbgSsSetThreadData(ThreadData
);
237 SaveProcessHandle(IN DWORD dwProcessId
,
240 PDBGSS_THREAD_DATA ThreadData
;
242 /* Allocate a thread structure */
243 ThreadData
= RtlAllocateHeap(RtlGetProcessHeap(),
245 sizeof(DBGSS_THREAD_DATA
));
246 if (!ThreadData
) return;
249 ThreadData
->ProcessHandle
= hProcess
;
250 ThreadData
->ProcessId
= dwProcessId
;
251 ThreadData
->ThreadId
= 0;
252 ThreadData
->ThreadHandle
= NULL
;
253 ThreadData
->HandleMarked
= FALSE
;
256 ThreadData
->Next
= DbgSsGetThreadData();
257 DbgSsSetThreadData(ThreadData
);
262 MarkThreadHandle(IN DWORD dwThreadId
)
264 PDBGSS_THREAD_DATA ThreadData
;
266 /* Loop all thread data events */
267 for (ThreadData
= DbgSsGetThreadData(); ThreadData
; ThreadData
= ThreadData
->Next
)
269 /* Check if this one matches */
270 if (ThreadData
->ThreadId
== dwThreadId
)
272 /* Mark the structure and break out */
273 ThreadData
->HandleMarked
= TRUE
;
281 MarkProcessHandle(IN DWORD dwProcessId
)
283 PDBGSS_THREAD_DATA ThreadData
;
285 /* Loop all thread data events */
286 for (ThreadData
= DbgSsGetThreadData(); ThreadData
; ThreadData
= ThreadData
->Next
)
288 /* Check if this one matches */
289 if ((ThreadData
->ProcessId
== dwProcessId
) && !(ThreadData
->ThreadId
))
291 /* Mark the structure and break out */
292 ThreadData
->HandleMarked
= TRUE
;
300 RemoveHandles(IN DWORD dwProcessId
,
303 PDBGSS_THREAD_DATA
*ThreadData
;
304 PDBGSS_THREAD_DATA ThisData
;
306 /* Loop all thread data events */
307 ThreadData
= (PDBGSS_THREAD_DATA
*)NtCurrentTeb()->DbgSsReserved
;
308 ThisData
= *ThreadData
;
311 /* Check if this one matches */
312 if ((ThisData
->HandleMarked
) &&
313 ((ThisData
->ProcessId
== dwProcessId
) || (ThisData
->ThreadId
== dwThreadId
)))
315 /* Close open handles */
316 if (ThisData
->ThreadHandle
) CloseHandle(ThisData
->ThreadHandle
);
317 if (ThisData
->ProcessHandle
) CloseHandle(ThisData
->ProcessHandle
);
319 /* Unlink the thread data */
320 *ThreadData
= ThisData
->Next
;
323 RtlFreeHeap(RtlGetProcessHeap(), 0, ThisData
);
327 /* Move to the next one */
328 ThreadData
= &ThisData
->Next
;
330 ThisData
= *ThreadData
;
336 CloseAllProcessHandles(IN DWORD dwProcessId
)
338 PDBGSS_THREAD_DATA
*ThreadData
;
339 PDBGSS_THREAD_DATA ThisData
;
341 /* Loop all thread data events */
342 ThreadData
= (PDBGSS_THREAD_DATA
*)NtCurrentTeb()->DbgSsReserved
;
343 ThisData
= *ThreadData
;
346 /* Check if this one matches */
347 if (ThisData
->ProcessId
== dwProcessId
)
349 /* Close open handles */
350 if (ThisData
->ThreadHandle
) CloseHandle(ThisData
->ThreadHandle
);
351 if (ThisData
->ProcessHandle
) CloseHandle(ThisData
->ProcessHandle
);
353 /* Unlink the thread data */
354 *ThreadData
= ThisData
->Next
;
357 RtlFreeHeap(RtlGetProcessHeap(), 0, ThisData
);
361 /* Move to the next one */
362 ThreadData
= &ThisData
->Next
;
364 ThisData
= *ThreadData
;
370 ProcessIdToHandle(IN DWORD dwProcessId
)
373 OBJECT_ATTRIBUTES ObjectAttributes
;
377 /* If we don't have a PID, look it up */
378 if (dwProcessId
== MAXDWORD
) dwProcessId
= (DWORD_PTR
)CsrGetProcessId();
380 /* Open a handle to the process */
381 ClientId
.UniqueThread
= NULL
;
382 ClientId
.UniqueProcess
= UlongToHandle(dwProcessId
);
383 InitializeObjectAttributes(&ObjectAttributes
, NULL
, 0, NULL
, NULL
);
384 Status
= NtOpenProcess(&Handle
,
385 PROCESS_CREATE_THREAD
| PROCESS_VM_OPERATION
|
386 PROCESS_VM_WRITE
| PROCESS_VM_READ
|
387 PROCESS_SUSPEND_RESUME
| PROCESS_QUERY_INFORMATION
,
390 if (!NT_SUCCESS(Status
))
393 BaseSetLastNTError(Status
);
397 /* Return the handle */
401 /* PUBLIC FUNCTIONS **********************************************************/
408 CheckRemoteDebuggerPresent(IN HANDLE hProcess
,
409 OUT PBOOL pbDebuggerPresent
)
414 /* Make sure we have an output and process*/
415 if (!(pbDebuggerPresent
) || !(hProcess
))
418 SetLastError(ERROR_INVALID_PARAMETER
);
422 /* Check if the process has a debug object/port */
423 Status
= NtQueryInformationProcess(hProcess
,
428 if (NT_SUCCESS(Status
))
430 /* Return the current state */
431 *pbDebuggerPresent
= DebugPort
!= NULL
;
435 /* Otherwise, fail */
436 BaseSetLastNTError(Status
);
445 ContinueDebugEvent(IN DWORD dwProcessId
,
447 IN DWORD dwContinueStatus
)
452 /* Set the Client ID */
453 ClientId
.UniqueProcess
= (HANDLE
)dwProcessId
;
454 ClientId
.UniqueThread
= (HANDLE
)dwThreadId
;
456 /* Continue debugging */
457 Status
= DbgUiContinue(&ClientId
, dwContinueStatus
);
458 if (!NT_SUCCESS(Status
))
461 BaseSetLastNTError(Status
);
465 /* Remove the process/thread handles */
466 RemoveHandles(dwProcessId
, dwThreadId
);
477 DebugActiveProcess(IN DWORD dwProcessId
)
479 NTSTATUS Status
, Status1
;
482 /* Connect to the debugger */
483 Status
= DbgUiConnectToDbg();
484 if (!NT_SUCCESS(Status
))
486 BaseSetLastNTError(Status
);
490 /* Get the process handle */
491 Handle
= ProcessIdToHandle(dwProcessId
);
492 if (!Handle
) return FALSE
;
494 /* Now debug the process */
495 Status
= DbgUiDebugActiveProcess(Handle
);
497 /* Close the handle since we're done */
498 Status1
= NtClose(Handle
);
499 ASSERT(NT_SUCCESS(Status1
));
501 /* Check if debugging worked */
502 if (!NT_SUCCESS(Status
))
505 BaseSetLastNTError(Status
);
518 DebugActiveProcessStop(IN DWORD dwProcessId
)
520 NTSTATUS Status
, Status1
;
523 /* Get the process handle */
524 Handle
= ProcessIdToHandle(dwProcessId
);
525 if (!Handle
) return FALSE
;
527 /* Close all the process handles */
528 CloseAllProcessHandles(dwProcessId
);
530 /* Now stop debgging the process */
531 Status
= DbgUiStopDebugging(Handle
);
532 Status1
= NtClose(Handle
);
533 ASSERT(NT_SUCCESS(Status1
));
535 /* Check for failure */
536 if (!NT_SUCCESS(Status
))
539 SetLastError(ERROR_ACCESS_DENIED
);
552 DebugBreakProcess(IN HANDLE Process
)
556 /* Send the breakin request */
557 Status
= DbgUiIssueRemoteBreakin(Process
);
558 if(!NT_SUCCESS(Status
))
561 BaseSetLastNTError(Status
);
574 DebugSetProcessKillOnExit(IN BOOL KillOnExit
)
580 /* Get the debug object */
581 Handle
= DbgUiGetThreadDebugObject();
585 BaseSetLastNTError(STATUS_INVALID_HANDLE
);
589 /* Now set the kill-on-exit state */
590 State
= KillOnExit
!= 0;
591 Status
= NtSetInformationDebugObject(Handle
,
592 DebugObjectKillProcessOnExitInformation
,
596 if (!NT_SUCCESS(Status
))
599 BaseSetLastNTError(Status
);
612 IsDebuggerPresent(VOID
)
614 return (BOOL
)NtCurrentPeb()->BeingDebugged
;
622 WaitForDebugEvent(IN LPDEBUG_EVENT lpDebugEvent
,
623 IN DWORD dwMilliseconds
)
625 LARGE_INTEGER WaitTime
;
626 PLARGE_INTEGER Timeout
;
627 DBGUI_WAIT_STATE_CHANGE WaitStateChange
;
630 /* Convert to NT Timeout */
631 Timeout
= BaseFormatTimeOut(&WaitTime
, dwMilliseconds
);
633 /* Loop while we keep getting interrupted */
636 /* Call the native API */
637 Status
= DbgUiWaitStateChange(&WaitStateChange
, Timeout
);
638 } while ((Status
== STATUS_ALERTED
) || (Status
== STATUS_USER_APC
));
640 /* Check if the wait failed */
641 if (!(NT_SUCCESS(Status
)) || (Status
== DBG_UNABLE_TO_PROVIDE_HANDLE
))
643 /* Set the error code and quit */
644 BaseSetLastNTError(Status
);
648 /* Check if we timed out */
649 if (Status
== STATUS_TIMEOUT
)
651 /* Fail with a timeout error */
652 SetLastError(ERROR_SEM_TIMEOUT
);
656 /* Convert the structure */
657 Status
= DbgUiConvertStateChangeStructure(&WaitStateChange
, lpDebugEvent
);
658 if (!NT_SUCCESS(Status
))
660 /* Set the error code and quit */
661 BaseSetLastNTError(Status
);
665 /* Check what kind of event this was */
666 switch (lpDebugEvent
->dwDebugEventCode
)
668 /* New thread was created */
669 case CREATE_THREAD_DEBUG_EVENT
:
671 /* Setup the thread data */
672 SaveThreadHandle(lpDebugEvent
->dwProcessId
,
673 lpDebugEvent
->dwThreadId
,
674 lpDebugEvent
->u
.CreateThread
.hThread
);
677 /* New process was created */
678 case CREATE_PROCESS_DEBUG_EVENT
:
680 /* Setup the process data */
681 SaveProcessHandle(lpDebugEvent
->dwProcessId
,
682 lpDebugEvent
->u
.CreateProcessInfo
.hProcess
);
684 /* Setup the thread data */
685 SaveThreadHandle(lpDebugEvent
->dwProcessId
,
686 lpDebugEvent
->dwThreadId
,
687 lpDebugEvent
->u
.CreateProcessInfo
.hThread
);
690 /* Process was exited */
691 case EXIT_PROCESS_DEBUG_EVENT
:
693 /* Mark the thread data as such and fall through */
694 MarkProcessHandle(lpDebugEvent
->dwProcessId
);
696 /* Thread was exited */
697 case EXIT_THREAD_DEBUG_EVENT
:
699 /* Mark the thread data */
700 MarkThreadHandle(lpDebugEvent
->dwThreadId
);
704 case EXCEPTION_DEBUG_EVENT
:
705 case LOAD_DLL_DEBUG_EVENT
:
706 case UNLOAD_DLL_DEBUG_EVENT
:
707 case OUTPUT_DEBUG_STRING_EVENT
:
711 /* Fail anything else */
725 OutputDebugStringA(IN LPCSTR _OutputString
)
729 ULONG_PTR a_nArgs
[2];
731 a_nArgs
[0] = (ULONG_PTR
)(strlen(_OutputString
) + 1);
732 a_nArgs
[1] = (ULONG_PTR
)_OutputString
;
734 /* send the string to the user-mode debugger */
735 RaiseException(DBG_PRINTEXCEPTION_C
, 0, 2, a_nArgs
);
737 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
739 /* no user-mode debugger: try the systemwide debug message monitor, or the
740 kernel debugger as a last resort */
742 /* mutex used to synchronize invocations of OutputDebugString */
743 static HANDLE s_hDBMonMutex
= NULL
;
744 /* true if we already attempted to open/create the mutex */
745 static BOOL s_bDBMonMutexTriedOpen
= FALSE
;
747 /* local copy of the mutex handle */
748 volatile HANDLE hDBMonMutex
= s_hDBMonMutex
;
749 /* handle to the Section of the shared buffer */
750 volatile HANDLE hDBMonBuffer
= NULL
;
752 /* pointer to the mapped view of the shared buffer. It consist of the current
753 process id followed by the message string */
754 struct { DWORD ProcessId
; CHAR Buffer
[1]; } * pDBMonBuffer
= NULL
;
756 /* event: signaled by the debug message monitor when OutputDebugString can write
757 to the shared buffer */
758 volatile HANDLE hDBMonBufferReady
= NULL
;
760 /* event: to be signaled by OutputDebugString when it's done writing to the
762 volatile HANDLE hDBMonDataReady
= NULL
;
764 /* mutex not opened, and no previous attempts to open/create it */
765 if(hDBMonMutex
== NULL
&& !s_bDBMonMutexTriedOpen
)
767 /* open/create the mutex */
768 hDBMonMutex
= K32CreateDBMonMutex();
769 /* store the handle */
770 s_hDBMonMutex
= hDBMonMutex
;
775 volatile PCHAR a_cBuffer
= NULL
;
777 /* opening the mutex failed */
778 if(hDBMonMutex
== NULL
)
780 /* remember next time */
781 s_bDBMonMutexTriedOpen
= TRUE
;
783 /* opening the mutex succeeded */
788 /* synchronize with other invocations of OutputDebugString */
789 WaitForSingleObject(hDBMonMutex
, INFINITE
);
791 /* buffer of the system-wide debug message monitor */
792 hDBMonBuffer
= OpenFileMappingW(SECTION_MAP_WRITE
, FALSE
, L
"DBWIN_BUFFER");
794 /* couldn't open the buffer: send the string to the kernel debugger */
795 if(hDBMonBuffer
== NULL
) break;
798 pDBMonBuffer
= MapViewOfFile(hDBMonBuffer
,
799 SECTION_MAP_READ
| SECTION_MAP_WRITE
,
804 /* couldn't map the buffer: send the string to the kernel debugger */
805 if(pDBMonBuffer
== NULL
) break;
807 /* open the event signaling that the buffer can be accessed */
808 hDBMonBufferReady
= OpenEventW(SYNCHRONIZE
, FALSE
, L
"DBWIN_BUFFER_READY");
810 /* couldn't open the event: send the string to the kernel debugger */
811 if(hDBMonBufferReady
== NULL
) break;
813 /* open the event to be signaled when the buffer has been filled */
814 hDBMonDataReady
= OpenEventW(EVENT_MODIFY_STATE
, FALSE
, L
"DBWIN_DATA_READY");
818 /* we couldn't connect to the system-wide debug message monitor: send the
819 string to the kernel debugger */
820 if(hDBMonDataReady
== NULL
) ReleaseMutex(hDBMonMutex
);
825 /* size of the current output block */
826 volatile SIZE_T nRoundLen
;
828 /* size of the remainder of the string */
829 volatile SIZE_T nOutputStringLen
;
831 /* output the whole string */
832 nOutputStringLen
= strlen(_OutputString
);
836 /* we're connected to the debug monitor:
837 write the current block to the shared buffer */
840 /* wait a maximum of 10 seconds for the debug monitor
841 to finish processing the shared buffer */
842 if(WaitForSingleObject(hDBMonBufferReady
, 10000) != WAIT_OBJECT_0
)
844 /* timeout or failure: give up */
848 /* write the process id into the buffer */
849 pDBMonBuffer
->ProcessId
= GetCurrentProcessId();
851 /* write only as many bytes as they fit in the buffer */
852 if(nOutputStringLen
> (PAGE_SIZE
- sizeof(DWORD
) - 1))
853 nRoundLen
= PAGE_SIZE
- sizeof(DWORD
) - 1;
855 nRoundLen
= nOutputStringLen
;
857 /* copy the current block into the buffer */
858 memcpy(pDBMonBuffer
->Buffer
, _OutputString
, nRoundLen
);
860 /* null-terminate the current block */
861 pDBMonBuffer
->Buffer
[nRoundLen
] = 0;
863 /* signal that the data contains meaningful data and can be read */
864 SetEvent(hDBMonDataReady
);
866 /* else, send the current block to the kernel debugger */
869 /* output in blocks of 512 characters */
870 a_cBuffer
= (CHAR
*)HeapAlloc(GetProcessHeap(), 0, 512);
874 DbgPrint("OutputDebugStringA: Failed\n");
878 /* write a maximum of 511 bytes */
879 if(nOutputStringLen
> 510)
882 nRoundLen
= nOutputStringLen
;
884 /* copy the current block */
885 memcpy(a_cBuffer
, _OutputString
, nRoundLen
);
887 /* null-terminate the current block */
888 a_cBuffer
[nRoundLen
] = 0;
890 /* send the current block to the kernel debugger */
891 DbgPrint("%s", a_cBuffer
);
895 HeapFree(GetProcessHeap(), 0, a_cBuffer
);
900 /* move to the next block */
901 _OutputString
+= nRoundLen
;
902 nOutputStringLen
-= nRoundLen
;
904 /* repeat until the string has been fully output */
905 while (nOutputStringLen
> 0);
907 /* ignore access violations and let other exceptions fall through */
908 _SEH2_EXCEPT((_SEH2_GetExceptionCode() == STATUS_ACCESS_VIOLATION
) ? EXCEPTION_EXECUTE_HANDLER
: EXCEPTION_CONTINUE_SEARCH
)
911 HeapFree(GetProcessHeap(), 0, a_cBuffer
);
913 /* string copied verbatim from Microsoft's kernel32.dll */
914 DbgPrint("\nOutputDebugString faulted during output\n");
920 /* close all the still open resources */
921 if(hDBMonBufferReady
) CloseHandle(hDBMonBufferReady
);
922 if(pDBMonBuffer
) UnmapViewOfFile(pDBMonBuffer
);
923 if(hDBMonBuffer
) CloseHandle(hDBMonBuffer
);
924 if(hDBMonDataReady
) CloseHandle(hDBMonDataReady
);
926 /* leave the critical section */
927 if(hDBMonDataReady
!= NULL
)
928 ReleaseMutex(hDBMonMutex
);
940 OutputDebugStringW(IN LPCWSTR OutputString
)
942 UNICODE_STRING UnicodeString
;
943 ANSI_STRING AnsiString
;
946 /* convert the string in ANSI */
947 RtlInitUnicodeString(&UnicodeString
, OutputString
);
948 Status
= RtlUnicodeStringToAnsiString(&AnsiString
, &UnicodeString
, TRUE
);
950 /* OutputDebugStringW always prints something, even if conversion fails */
951 if (!NT_SUCCESS(Status
)) AnsiString
.Buffer
= "";
953 /* Output the converted string */
954 OutputDebugStringA(AnsiString
.Buffer
);
956 /* free the converted string */
957 if (NT_SUCCESS(Status
)) RtlFreeAnsiString(&AnsiString
);