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 for (ThisData
= *ThreadData
; ThisData
; ThisData
= ThisData
->Next
)
310 /* Check if this one matches */
311 if ((ThisData
->HandleMarked
) &&
312 ((ThisData
->ProcessId
== dwProcessId
) || (ThisData
->ThreadId
== dwThreadId
)))
314 /* Close open handles */
315 if (ThisData
->ThreadHandle
) CloseHandle(ThisData
->ThreadHandle
);
316 if (ThisData
->ProcessHandle
) CloseHandle(ThisData
->ProcessHandle
);
318 /* Unlink the thread data */
319 *ThreadData
= ThisData
->Next
;
322 RtlFreeHeap(RtlGetProcessHeap(), 0, ThisData
);
326 /* Move to the next one */
327 ThreadData
= &ThisData
->Next
;
334 CloseAllProcessHandles(IN DWORD dwProcessId
)
336 PDBGSS_THREAD_DATA
*ThreadData
;
337 PDBGSS_THREAD_DATA ThisData
;
339 /* Loop all thread data events */
340 ThreadData
= (PDBGSS_THREAD_DATA
*)NtCurrentTeb()->DbgSsReserved
;
341 for (ThisData
= *ThreadData
; ThisData
; ThisData
= ThisData
->Next
)
343 /* Check if this one matches */
344 if (ThisData
->ProcessId
== dwProcessId
)
346 /* Close open handles */
347 if (ThisData
->ThreadHandle
) CloseHandle(ThisData
->ThreadHandle
);
348 if (ThisData
->ProcessHandle
) CloseHandle(ThisData
->ProcessHandle
);
350 /* Unlink the thread data */
351 *ThreadData
= ThisData
->Next
;
354 RtlFreeHeap(RtlGetProcessHeap(), 0, ThisData
);
358 /* Move to the next one */
359 ThreadData
= &ThisData
->Next
;
366 ProcessIdToHandle(IN DWORD dwProcessId
)
369 OBJECT_ATTRIBUTES ObjectAttributes
;
373 /* If we don't have a PID, look it up */
374 if (dwProcessId
== MAXDWORD
) dwProcessId
= (DWORD_PTR
)CsrGetProcessId();
376 /* Open a handle to the process */
377 ClientId
.UniqueThread
= NULL
;
378 ClientId
.UniqueProcess
= UlongToHandle(dwProcessId
);
379 InitializeObjectAttributes(&ObjectAttributes
, NULL
, 0, NULL
, NULL
);
380 Status
= NtOpenProcess(&Handle
,
381 PROCESS_CREATE_THREAD
| PROCESS_VM_OPERATION
|
382 PROCESS_VM_WRITE
| PROCESS_VM_READ
|
383 PROCESS_SUSPEND_RESUME
| PROCESS_QUERY_INFORMATION
,
386 if (!NT_SUCCESS(Status
))
389 BaseSetLastNTError(Status
);
393 /* Return the handle */
397 /* PUBLIC FUNCTIONS **********************************************************/
404 CheckRemoteDebuggerPresent(IN HANDLE hProcess
,
405 OUT PBOOL pbDebuggerPresent
)
410 /* Make sure we have an output and process*/
411 if (!(pbDebuggerPresent
) || !(hProcess
))
414 SetLastError(ERROR_INVALID_PARAMETER
);
418 /* Check if the process has a debug object/port */
419 Status
= NtQueryInformationProcess(hProcess
,
424 if (NT_SUCCESS(Status
))
426 /* Return the current state */
427 *pbDebuggerPresent
= DebugPort
!= NULL
;
431 /* Otherwise, fail */
432 BaseSetLastNTError(Status
);
441 ContinueDebugEvent(IN DWORD dwProcessId
,
443 IN DWORD dwContinueStatus
)
448 /* Set the Client ID */
449 ClientId
.UniqueProcess
= (HANDLE
)dwProcessId
;
450 ClientId
.UniqueThread
= (HANDLE
)dwThreadId
;
452 /* Continue debugging */
453 Status
= DbgUiContinue(&ClientId
, dwContinueStatus
);
454 if (!NT_SUCCESS(Status
))
457 BaseSetLastNTError(Status
);
461 /* Remove the process/thread handles */
462 RemoveHandles(dwProcessId
, dwThreadId
);
473 DebugActiveProcess(IN DWORD dwProcessId
)
475 NTSTATUS Status
, Status1
;
478 /* Connect to the debugger */
479 Status
= DbgUiConnectToDbg();
480 if (!NT_SUCCESS(Status
))
482 BaseSetLastNTError(Status
);
486 /* Get the process handle */
487 Handle
= ProcessIdToHandle(dwProcessId
);
488 if (!Handle
) return FALSE
;
490 /* Now debug the process */
491 Status
= DbgUiDebugActiveProcess(Handle
);
493 /* Close the handle since we're done */
494 Status1
= NtClose(Handle
);
495 ASSERT(NT_SUCCESS(Status1
));
497 /* Check if debugging worked */
498 if (!NT_SUCCESS(Status
))
501 BaseSetLastNTError(Status
);
514 DebugActiveProcessStop(IN DWORD dwProcessId
)
516 NTSTATUS Status
, Status1
;
519 /* Get the process handle */
520 Handle
= ProcessIdToHandle(dwProcessId
);
521 if (!Handle
) return FALSE
;
523 /* Close all the process handles */
524 CloseAllProcessHandles(dwProcessId
);
526 /* Now stop debgging the process */
527 Status
= DbgUiStopDebugging(Handle
);
528 Status1
= NtClose(Handle
);
529 ASSERT(NT_SUCCESS(Status1
));
531 /* Check for failure */
532 if (!NT_SUCCESS(Status
))
535 SetLastError(ERROR_ACCESS_DENIED
);
548 DebugBreakProcess(IN HANDLE Process
)
552 /* Send the breakin request */
553 Status
= DbgUiIssueRemoteBreakin(Process
);
554 if(!NT_SUCCESS(Status
))
557 BaseSetLastNTError(Status
);
570 DebugSetProcessKillOnExit(IN BOOL KillOnExit
)
576 /* Get the debug object */
577 Handle
= DbgUiGetThreadDebugObject();
581 BaseSetLastNTError(STATUS_INVALID_HANDLE
);
585 /* Now set the kill-on-exit state */
586 State
= KillOnExit
!= 0;
587 Status
= NtSetInformationDebugObject(Handle
,
588 DebugObjectKillProcessOnExitInformation
,
592 if (!NT_SUCCESS(Status
))
595 BaseSetLastNTError(Status
);
608 IsDebuggerPresent(VOID
)
610 return (BOOL
)NtCurrentPeb()->BeingDebugged
;
618 WaitForDebugEvent(IN LPDEBUG_EVENT lpDebugEvent
,
619 IN DWORD dwMilliseconds
)
621 LARGE_INTEGER WaitTime
;
622 PLARGE_INTEGER Timeout
;
623 DBGUI_WAIT_STATE_CHANGE WaitStateChange
;
626 /* Convert to NT Timeout */
627 Timeout
= BaseFormatTimeOut(&WaitTime
, dwMilliseconds
);
629 /* Loop while we keep getting interrupted */
632 /* Call the native API */
633 Status
= DbgUiWaitStateChange(&WaitStateChange
, Timeout
);
634 } while ((Status
== STATUS_ALERTED
) || (Status
== STATUS_USER_APC
));
636 /* Check if the wait failed */
637 if (!(NT_SUCCESS(Status
)) || (Status
== DBG_UNABLE_TO_PROVIDE_HANDLE
))
639 /* Set the error code and quit */
640 BaseSetLastNTError(Status
);
644 /* Check if we timed out */
645 if (Status
== STATUS_TIMEOUT
)
647 /* Fail with a timeout error */
648 SetLastError(ERROR_SEM_TIMEOUT
);
652 /* Convert the structure */
653 Status
= DbgUiConvertStateChangeStructure(&WaitStateChange
, lpDebugEvent
);
654 if (!NT_SUCCESS(Status
))
656 /* Set the error code and quit */
657 BaseSetLastNTError(Status
);
661 /* Check what kind of event this was */
662 switch (lpDebugEvent
->dwDebugEventCode
)
664 /* New thread was created */
665 case CREATE_THREAD_DEBUG_EVENT
:
667 /* Setup the thread data */
668 SaveThreadHandle(lpDebugEvent
->dwProcessId
,
669 lpDebugEvent
->dwThreadId
,
670 lpDebugEvent
->u
.CreateThread
.hThread
);
673 /* New process was created */
674 case CREATE_PROCESS_DEBUG_EVENT
:
676 /* Setup the process data */
677 SaveProcessHandle(lpDebugEvent
->dwProcessId
,
678 lpDebugEvent
->u
.CreateProcessInfo
.hProcess
);
680 /* Setup the thread data */
681 SaveThreadHandle(lpDebugEvent
->dwProcessId
,
682 lpDebugEvent
->dwThreadId
,
683 lpDebugEvent
->u
.CreateProcessInfo
.hThread
);
686 /* Process was exited */
687 case EXIT_PROCESS_DEBUG_EVENT
:
689 /* Mark the thread data as such and fall through */
690 MarkProcessHandle(lpDebugEvent
->dwProcessId
);
692 /* Thread was exited */
693 case EXIT_THREAD_DEBUG_EVENT
:
695 /* Mark the thread data */
696 MarkThreadHandle(lpDebugEvent
->dwThreadId
);
700 case EXCEPTION_DEBUG_EVENT
:
701 case LOAD_DLL_DEBUG_EVENT
:
702 case UNLOAD_DLL_DEBUG_EVENT
:
703 case OUTPUT_DEBUG_STRING_EVENT
:
707 /* Fail anything else */
721 OutputDebugStringA(IN LPCSTR _OutputString
)
725 ULONG_PTR a_nArgs
[2];
727 a_nArgs
[0] = (ULONG_PTR
)(strlen(_OutputString
) + 1);
728 a_nArgs
[1] = (ULONG_PTR
)_OutputString
;
730 /* send the string to the user-mode debugger */
731 RaiseException(DBG_PRINTEXCEPTION_C
, 0, 2, a_nArgs
);
733 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
735 /* no user-mode debugger: try the systemwide debug message monitor, or the
736 kernel debugger as a last resort */
738 /* mutex used to synchronize invocations of OutputDebugString */
739 static HANDLE s_hDBMonMutex
= NULL
;
740 /* true if we already attempted to open/create the mutex */
741 static BOOL s_bDBMonMutexTriedOpen
= FALSE
;
743 /* local copy of the mutex handle */
744 volatile HANDLE hDBMonMutex
= s_hDBMonMutex
;
745 /* handle to the Section of the shared buffer */
746 volatile HANDLE hDBMonBuffer
= NULL
;
748 /* pointer to the mapped view of the shared buffer. It consist of the current
749 process id followed by the message string */
750 struct { DWORD ProcessId
; CHAR Buffer
[1]; } * pDBMonBuffer
= NULL
;
752 /* event: signaled by the debug message monitor when OutputDebugString can write
753 to the shared buffer */
754 volatile HANDLE hDBMonBufferReady
= NULL
;
756 /* event: to be signaled by OutputDebugString when it's done writing to the
758 volatile HANDLE hDBMonDataReady
= NULL
;
760 /* mutex not opened, and no previous attempts to open/create it */
761 if(hDBMonMutex
== NULL
&& !s_bDBMonMutexTriedOpen
)
763 /* open/create the mutex */
764 hDBMonMutex
= K32CreateDBMonMutex();
765 /* store the handle */
766 s_hDBMonMutex
= hDBMonMutex
;
771 volatile PCHAR a_cBuffer
= NULL
;
773 /* opening the mutex failed */
774 if(hDBMonMutex
== NULL
)
776 /* remember next time */
777 s_bDBMonMutexTriedOpen
= TRUE
;
779 /* opening the mutex succeeded */
784 /* synchronize with other invocations of OutputDebugString */
785 WaitForSingleObject(hDBMonMutex
, INFINITE
);
787 /* buffer of the system-wide debug message monitor */
788 hDBMonBuffer
= OpenFileMappingW(SECTION_MAP_WRITE
, FALSE
, L
"DBWIN_BUFFER");
790 /* couldn't open the buffer: send the string to the kernel debugger */
791 if(hDBMonBuffer
== NULL
) break;
794 pDBMonBuffer
= MapViewOfFile(hDBMonBuffer
,
795 SECTION_MAP_READ
| SECTION_MAP_WRITE
,
800 /* couldn't map the buffer: send the string to the kernel debugger */
801 if(pDBMonBuffer
== NULL
) break;
803 /* open the event signaling that the buffer can be accessed */
804 hDBMonBufferReady
= OpenEventW(SYNCHRONIZE
, FALSE
, L
"DBWIN_BUFFER_READY");
806 /* couldn't open the event: send the string to the kernel debugger */
807 if(hDBMonBufferReady
== NULL
) break;
809 /* open the event to be signaled when the buffer has been filled */
810 hDBMonDataReady
= OpenEventW(EVENT_MODIFY_STATE
, FALSE
, L
"DBWIN_DATA_READY");
814 /* we couldn't connect to the system-wide debug message monitor: send the
815 string to the kernel debugger */
816 if(hDBMonDataReady
== NULL
) ReleaseMutex(hDBMonMutex
);
821 /* size of the current output block */
822 volatile SIZE_T nRoundLen
;
824 /* size of the remainder of the string */
825 volatile SIZE_T nOutputStringLen
;
827 /* output the whole string */
828 nOutputStringLen
= strlen(_OutputString
);
832 /* we're connected to the debug monitor:
833 write the current block to the shared buffer */
836 /* wait a maximum of 10 seconds for the debug monitor
837 to finish processing the shared buffer */
838 if(WaitForSingleObject(hDBMonBufferReady
, 10000) != WAIT_OBJECT_0
)
840 /* timeout or failure: give up */
844 /* write the process id into the buffer */
845 pDBMonBuffer
->ProcessId
= GetCurrentProcessId();
847 /* write only as many bytes as they fit in the buffer */
848 if(nOutputStringLen
> (PAGE_SIZE
- sizeof(DWORD
) - 1))
849 nRoundLen
= PAGE_SIZE
- sizeof(DWORD
) - 1;
851 nRoundLen
= nOutputStringLen
;
853 /* copy the current block into the buffer */
854 memcpy(pDBMonBuffer
->Buffer
, _OutputString
, nRoundLen
);
856 /* null-terminate the current block */
857 pDBMonBuffer
->Buffer
[nRoundLen
] = 0;
859 /* signal that the data contains meaningful data and can be read */
860 SetEvent(hDBMonDataReady
);
862 /* else, send the current block to the kernel debugger */
865 /* output in blocks of 512 characters */
866 a_cBuffer
= (CHAR
*)HeapAlloc(GetProcessHeap(), 0, 512);
870 DbgPrint("OutputDebugStringA: Failed\n");
874 /* write a maximum of 511 bytes */
875 if(nOutputStringLen
> 510)
878 nRoundLen
= nOutputStringLen
;
880 /* copy the current block */
881 memcpy(a_cBuffer
, _OutputString
, nRoundLen
);
883 /* null-terminate the current block */
884 a_cBuffer
[nRoundLen
] = 0;
886 /* send the current block to the kernel debugger */
887 DbgPrint("%s", a_cBuffer
);
891 HeapFree(GetProcessHeap(), 0, a_cBuffer
);
896 /* move to the next block */
897 _OutputString
+= nRoundLen
;
898 nOutputStringLen
-= nRoundLen
;
900 /* repeat until the string has been fully output */
901 while (nOutputStringLen
> 0);
903 /* ignore access violations and let other exceptions fall through */
904 _SEH2_EXCEPT((_SEH2_GetExceptionCode() == STATUS_ACCESS_VIOLATION
) ? EXCEPTION_EXECUTE_HANDLER
: EXCEPTION_CONTINUE_SEARCH
)
907 HeapFree(GetProcessHeap(), 0, a_cBuffer
);
909 /* string copied verbatim from Microsoft's kernel32.dll */
910 DbgPrint("\nOutputDebugString faulted during output\n");
916 /* close all the still open resources */
917 if(hDBMonBufferReady
) CloseHandle(hDBMonBufferReady
);
918 if(pDBMonBuffer
) UnmapViewOfFile(pDBMonBuffer
);
919 if(hDBMonBuffer
) CloseHandle(hDBMonBuffer
);
920 if(hDBMonDataReady
) CloseHandle(hDBMonDataReady
);
922 /* leave the critical section */
923 if(hDBMonDataReady
!= NULL
)
924 ReleaseMutex(hDBMonMutex
);
936 OutputDebugStringW(IN LPCWSTR OutputString
)
938 UNICODE_STRING UnicodeString
;
939 ANSI_STRING AnsiString
;
942 /* convert the string in ANSI */
943 RtlInitUnicodeString(&UnicodeString
, OutputString
);
944 Status
= RtlUnicodeStringToAnsiString(&AnsiString
, &UnicodeString
, TRUE
);
946 /* OutputDebugStringW always prints something, even if conversion fails */
947 if (!NT_SUCCESS(Status
)) AnsiString
.Buffer
= "";
949 /* Output the converted string */
950 OutputDebugStringA(AnsiString
.Buffer
);
952 /* free the converted string */
953 if (NT_SUCCESS(Status
)) RtlFreeAnsiString(&AnsiString
);