/*++ 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; }