/*++
Copyright (c) 1989 - 1993 Microsoft Corporation
Module Name:
dbguiapi.c
Abstract:
This module implements the DbgUi APIs
Author:
Mark Lucovsky (markl) 23-Jan-1990
Revision History:
--*/
#include "smsrvp.h"
//
// Forward declarations.
//
NTSTATUS
DbgpCreateProcess(
IN PDBGP_USER_INTERFACE UserInterface,
IN OUT PDBGP_APP_THREAD AppThread,
OUT PDBGUI_CREATE_PROCESS CreateProcessInfo
);
NTSTATUS
DbgpCreateThread(
IN PDBGP_USER_INTERFACE UserInterface,
IN OUT PDBGP_APP_THREAD AppThread,
OUT PDBGUI_CREATE_THREAD CreateThread
);
NTSTATUS
DbgpLoadDll(
IN PDBGP_USER_INTERFACE UserInterface,
IN OUT PDBGP_APP_THREAD AppThread,
OUT PDBGKM_LOAD_DLL LoadDll
);
NTSTATUS
DbgpUiWaitStateChange (
IN PDBGP_USER_INTERFACE UserInterface,
IN OUT PDBGUI_APIMSG ApiMsg
)
/*++
Routine Description:
This function is called when a user interface has waited
on its state change notification semaphore, and wants to
pickup a state change message.
Arguments:
UserInterface - Supplies the address of the user interface making the call
ApiMsg - Supplies the DbgUi API message that contains the information
needed to complete this call.
Return Value:
STATUS_SUCCESS - A state change occured and is available in the
ApiMsg.
DBG_NO_STATE_CHANGE - No state change was found for the calling
user interface.
--*/
{
NTSTATUS st;
PDBGP_APP_THREAD AppThread;
DBG_STATE PreviousState;
st = STATUS_SUCCESS;
//
// Scan the user interface's app list looking for an
// app whose State is not DbgIdle or DbgReplyPending
//
AppThread = DbgpLocateStateChangeApp(UserInterface,&PreviousState);
if ( !AppThread ) {
return DBG_NO_STATE_CHANGE;
}
ApiMsg->u.WaitStateChange.NewState = PreviousState;
ApiMsg->u.WaitStateChange.AppClientId = AppThread->AppClientId;
switch ( PreviousState ) {
case DbgCreateThreadStateChange :
//
// Open the thread and Dup a handle over to the user
// interface.
//
st = DbgpCreateThread(
UserInterface,
AppThread,
&ApiMsg->u.WaitStateChange.StateInfo.CreateThread
);
break;
case DbgCreateProcessStateChange :
//
// Open the process, thread, and section, and Dup a handle
// over to the user interface.
//
st = DbgpCreateProcess(
UserInterface,
AppThread,
&ApiMsg->u.WaitStateChange.StateInfo.CreateProcessInfo
);
break;
case DbgExitThreadStateChange :
ApiMsg->u.WaitStateChange.StateInfo.ExitThread =
AppThread->LastSsApiMsg.u.ExitThread;
break;
case DbgExitProcessStateChange :
ApiMsg->u.WaitStateChange.StateInfo.ExitProcess =
AppThread->LastSsApiMsg.u.ExitProcess;
break;
case DbgLoadDllStateChange :
//
// Dup File handle to the user interface
//
st = DbgpLoadDll(
UserInterface,
AppThread,
&ApiMsg->u.WaitStateChange.StateInfo.LoadDll
);
break;
case DbgUnloadDllStateChange :
//
// Dup section handle to the user interface
//
ApiMsg->u.WaitStateChange.StateInfo.UnloadDll =
AppThread->LastSsApiMsg.u.UnloadDll;
break;
case DbgExceptionStateChange :
case DbgBreakpointStateChange :
case DbgSingleStepStateChange :
ApiMsg->u.WaitStateChange.StateInfo.Exception =
AppThread->LastSsApiMsg.u.Exception;
break;
default :
st = STATUS_NOT_IMPLEMENTED;
break;
}
return st;
}
NTSTATUS
DbgpCreateThread(
IN PDBGP_USER_INTERFACE UserInterface,
IN OUT PDBGP_APP_THREAD AppThread,
OUT PDBGUI_CREATE_THREAD CreateThread
)
/*++
Routine Description:
This function is called during the processing of a WaitStateChange
DbgUi API when the the state change for the App was
DbgCreateThreadStateChange.
This functions main purpose is to dup a handle to the thread into
the thread's controlling user interface.
Arguments:
UserInterface - Supplies the address of the thread's user interface.
AppThread - Supplies the address of the application thread.
CreateThread - Supplies the address of the create thread message that
is being returned to the user interface.
Return Value:
TBD
--*/
{
NTSTATUS st;
CreateThread->HandleToThread = NULL;
CreateThread->NewThread = AppThread->LastSsApiMsg.u.CreateThread;
//
// If handle to thread was successfully opened,
// then attempt to duplicate it into the user interface
//
if ( AppThread->HandleToThread ) {
try {
st = NtDuplicateObject(
NtCurrentProcess(),
AppThread->HandleToThread,
UserInterface->DebugUiProcess,
&CreateThread->HandleToThread,
DBGP_DUP_APP_THREAD_ACCESS,
0L,
DUPLICATE_CLOSE_SOURCE
);
}
except ( GetExceptionCode() == STATUS_INVALID_HANDLE ? EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH ) {
st = STATUS_INVALID_HANDLE;
}
if (!NT_SUCCESS(st)){
st = DBG_UNABLE_TO_PROVIDE_HANDLE;
CreateThread->HandleToThread = NULL;
AppThread->HandleToThread = NULL;
} else {
AppThread->HandleToThread = (HANDLE)((ULONG)CreateThread->HandleToThread | 1);
}
} else {
st = DBG_UNABLE_TO_PROVIDE_HANDLE;
}
return st;
}
NTSTATUS
DbgpCreateProcess(
IN PDBGP_USER_INTERFACE UserInterface,
IN OUT PDBGP_APP_THREAD AppThread,
OUT PDBGUI_CREATE_PROCESS CreateProcessInfo
)
/*++
Routine Description:
This function is called during the processing of a WaitStateChange
DbgUi API when the the state change for the App was
DbgCreateProcessStateChange.
This functions main purpose is to dup a handle to the thread, its
process, and the process's section into the thread's controlling
user interface.
Arguments:
UserInterface - Supplies the address of the thread's user interface.
AppThread - Supplies the address of the application thread.
CreateProcessInfo - Supplies the address of the create process message that
is being returned to the user interface.
Return Value:
TBD
--*/
{
NTSTATUS st;
NTSTATUS ReturnStatus;
ReturnStatus = STATUS_SUCCESS;
CreateProcessInfo->HandleToThread = NULL;
CreateProcessInfo->HandleToProcess = NULL;
CreateProcessInfo->NewProcess = AppThread->LastSsApiMsg.u.CreateProcessInfo.NewProcess;
CreateProcessInfo->NewProcess.FileHandle = NULL;
//
// If handle to thread was successfully opened,
// then attempt to duplicate it into the user interface
//
if ( AppThread->HandleToThread ) {
try {
st = NtDuplicateObject(
NtCurrentProcess(),
AppThread->HandleToThread,
UserInterface->DebugUiProcess,
&CreateProcessInfo->HandleToThread,
DBGP_DUP_APP_THREAD_ACCESS,
0L,
DUPLICATE_CLOSE_SOURCE
);
}
except ( GetExceptionCode() == STATUS_INVALID_HANDLE ? EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH ) {
st = STATUS_INVALID_HANDLE;
}
if (!NT_SUCCESS(st)){
ReturnStatus = DBG_UNABLE_TO_PROVIDE_HANDLE;
CreateProcessInfo->HandleToThread = NULL;
AppThread->HandleToThread = NULL;
} else {
AppThread->HandleToThread = (HANDLE)((ULONG)CreateProcessInfo->HandleToThread | 1);
}
} else {
ReturnStatus = DBG_UNABLE_TO_PROVIDE_HANDLE;
}
//
// If handle to process was successfully opened,
// then attempt to duplicate it into the user interface
//
if ( AppThread->AppProcess->DbgSrvHandleToProcess ) {
st = NtDuplicateObject(
NtCurrentProcess(),
AppThread->AppProcess->DbgSrvHandleToProcess,
UserInterface->DebugUiProcess,
&CreateProcessInfo->HandleToProcess,
DBGP_DUP_APP_PROCESS_ACCESS,
0L,
0L
);
if (!NT_SUCCESS(st)){
ReturnStatus = DBG_UNABLE_TO_PROVIDE_HANDLE;
CreateProcessInfo->HandleToProcess = NULL;
AppThread->AppProcess->HandleToProcess = NULL;
} else {
AppThread->AppProcess->HandleToProcess =
CreateProcessInfo->HandleToProcess;
if ( AppThread->LastSsApiMsg.u.CreateProcessInfo.NewProcess.FileHandle ) {
st = NtDuplicateObject(
AppThread->AppProcess->DbgSrvHandleToProcess,
AppThread->LastSsApiMsg.u.CreateProcessInfo.NewProcess.FileHandle,
UserInterface->DebugUiProcess,
&CreateProcessInfo->NewProcess.FileHandle,
DBGP_DUP_APP_FILE_ACCESS,
0L,
0L
);
if (!NT_SUCCESS(st)){
ReturnStatus = DBG_UNABLE_TO_PROVIDE_HANDLE;
CreateProcessInfo->NewProcess.FileHandle = NULL;
}
}
}
} else {
ReturnStatus = DBG_UNABLE_TO_PROVIDE_HANDLE;
}
return ReturnStatus;
}
NTSTATUS
DbgpLoadDll(
IN PDBGP_USER_INTERFACE UserInterface,
IN OUT PDBGP_APP_THREAD AppThread,
OUT PDBGKM_LOAD_DLL LoadDll
)
/*++
Routine Description:
This function is called during the processing of a WaitStateChange
DbgUi API when the the state change for the App was
DbgLoadDllStateChange.
This functions main purpose is to dup a handle to file into the
thread's controlling user interface.
Arguments:
UserInterface - Supplies the address of the thread's user interface.
AppThread - Supplies the address of the application thread.
LoadDll - Supplies the address of the load dll message that
is being returned to the user interface.
Return Value:
TBD
--*/
{
NTSTATUS st;
NTSTATUS ReturnStatus;
ReturnStatus = STATUS_SUCCESS;
*LoadDll = AppThread->LastSsApiMsg.u.LoadDll;
st = NtDuplicateObject(
AppThread->AppProcess->DbgSrvHandleToProcess,
AppThread->LastSsApiMsg.u.LoadDll.FileHandle,
UserInterface->DebugUiProcess,
&LoadDll->FileHandle,
DBGP_DUP_APP_FILE_ACCESS,
0L,
0L
);
if (!NT_SUCCESS(st)){
ReturnStatus = DBG_UNABLE_TO_PROVIDE_HANDLE;
LoadDll->FileHandle = NULL;
}
return ReturnStatus;
}
NTSTATUS
DbgpUiContinue (
IN PDBGP_USER_INTERFACE UserInterface,
IN OUT PDBGUI_APIMSG ApiMsg
)
/*++
Routine Description:
This function is called when a user interface has received
a state change message, and wants to continue one of its
application threads. Continuing translates into a reply
to an outstanding DbgSs API.
Arguments:
UserInterface - Supplies the address of the user interface making the call
ApiMsg - Supplies the DbgUi API message that contains the information
needed to complete this call.
Return Value:
STATUS_SUCCESS - Successful call to DbgUiContinue
STATUS_INVALID_CID - An invalid ClientId was specified for the
AppClientId, or the specified Application was not waiting
for a continue.
STATUS_INVALID_PARAMETER - An invalid continue status was specified.
--*/
{
NTSTATUS st;
PDBGP_APP_THREAD AppThread;
PDBGUI_CONTINUE args;
DBGSRV_APIMSG ContinueMsg;
PDBGP_SUBSYSTEM Subsystem;
args = &ApiMsg->u.Continue;
//
// Make sure that Continue status is valid
//
switch (args->ContinueStatus) {
case DBG_EXCEPTION_HANDLED :
case DBG_EXCEPTION_NOT_HANDLED :
case DBG_TERMINATE_THREAD :
case DBG_TERMINATE_PROCESS :
case DBG_CONTINUE :
break;
default:
return STATUS_INVALID_PARAMETER;
}
RtlEnterCriticalSection(&DbgpHashTableLock);
AppThread = DbgpIsAppInHashTable(&args->AppClientId);
if ( !AppThread ) {
RtlLeaveCriticalSection(&DbgpHashTableLock);
return STATUS_INVALID_CID;
}
Subsystem = AppThread->Subsystem;
//
// Now determine what type of continue this is. Depending on
// the threads continue state certain things need to happen.
// If we are continuing an exit thread or exit process, data
// structures need to be torn down, and handles in the user
// interface need to be closed.
//
RtlEnterCriticalSection(&UserInterface->UserInterfaceLock);
//
// Disallow continues on Apps that have not been picked up
// yet.
//
if ( AppThread->CurrentState != DbgReplyPending ) {
RtlLeaveCriticalSection(&UserInterface->UserInterfaceLock);
RtlLeaveCriticalSection(&DbgpHashTableLock);
return STATUS_INVALID_CID;
}
AppThread->CurrentState = DbgIdle;
DBGSRV_FORMAT_API_MSG(ContinueMsg,DbgSrvContinueApi,0L,AppThread->LastSsApiMsg.ContinueKey);
ContinueMsg.ReturnedStatus = args->ContinueStatus;
switch (AppThread->ContinueState) {
//
// These involve data structure tear down
//
case DbgExitThreadStateChange :
//
// Try to close the handle to the thread that
// the user interface has.
//
if ( AppThread->HandleToThread ) {
try {
NtDuplicateObject(
AppThread->UserInterface->DebugUiProcess,
AppThread->HandleToThread,
NULL,
NULL,
0L,
0L,
DUPLICATE_CLOSE_SOURCE
);
}
except ( GetExceptionCode() == STATUS_INVALID_HANDLE ? EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH ) {
;
}
}
AppThread->HandleToThread = NULL;
//
// delink the thread from its process, and deallocate it.
//
RemoveEntryList(&AppThread->AppLinks);
//
// Remove the thread from the thread hash table, and
// deallocate the thread
//
RemoveEntryList(&AppThread->HashTableLinks);
RtlFreeHeap(RtlProcessHeap(), 0,AppThread);
break;
case DbgExitProcessStateChange :
//
// Try to close the handle to the thread that
// the user interface has.
//
if ( AppThread->HandleToThread ) {
try {
st = NtDuplicateObject(
AppThread->UserInterface->DebugUiProcess,
AppThread->HandleToThread,
NULL,
NULL,
0L,
0L,
DUPLICATE_CLOSE_SOURCE
);
}
except ( GetExceptionCode() == STATUS_INVALID_HANDLE ? EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH ) {
;
}
}
AppThread->HandleToThread = NULL;
//
// Try to close the handle to the process
// that we gave to the user interface
//
if ( AppThread->AppProcess->DbgSrvHandleToProcess ) {
try {
st = NtDuplicateObject(
AppThread->UserInterface->DebugUiProcess,
AppThread->AppProcess->HandleToProcess,
NULL,
NULL,
0L,
0L,
DUPLICATE_CLOSE_SOURCE
);
}
except ( GetExceptionCode() == STATUS_INVALID_HANDLE ? EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH ) {
;
}
NtClose(AppThread->AppProcess->DbgSrvHandleToProcess);
}
//
// Remove the thread from the thread hash table,
// the process from the process hash table, and
// the process from its user interface.
//
RemoveEntryList(&AppThread->HashTableLinks);
RemoveEntryList(&AppThread->AppProcess->HashTableLinks);
RemoveEntryList(&AppThread->AppProcess->AppLinks);
RtlFreeHeap(RtlProcessHeap(), 0,AppThread->AppProcess);
RtlFreeHeap(RtlProcessHeap(), 0,AppThread);
break;
//
// No work needed
//
case DbgCreateThreadStateChange :
case DbgCreateProcessStateChange :
case DbgExceptionStateChange :
case DbgBreakpointStateChange :
case DbgSingleStepStateChange :
case DbgLoadDllStateChange :
case DbgUnloadDllStateChange :
break;
default:
ASSERT(FALSE);
}
RtlLeaveCriticalSection(&UserInterface->UserInterfaceLock);
RtlLeaveCriticalSection(&DbgpHashTableLock);
st = NtRequestPort(Subsystem->CommunicationPort, (PPORT_MESSAGE)&ContinueMsg);
ASSERT(NT_SUCCESS(st));
return st;
}