//---------------------------------------------------------------------------- // // Example of how to connect to a debugger server and execute // a command when the server is broken in. // // Copyright (C) Microsoft Corporation, 2002. // //---------------------------------------------------------------------------- #include #include #include #include #include BOOL g_ForceBreak; PSTR g_Connect; PSTR g_Command = ".echo Broken in"; IDebugClient* g_Client; IDebugClient* g_ExitDispatchClient; IDebugControl* g_Control; ULONG g_ExecStatus; //---------------------------------------------------------------------------- // // Event callbacks. // //---------------------------------------------------------------------------- class EventCallbacks : public DebugBaseEventCallbacks { public: // IUnknown. STDMETHOD_(ULONG, AddRef)( THIS ); STDMETHOD_(ULONG, Release)( THIS ); // IDebugEventCallbacks. STDMETHOD(GetInterestMask)( THIS_ OUT PULONG Mask ); STDMETHOD(ChangeEngineState)( THIS_ IN ULONG Flags, IN ULONG64 Argument ); }; STDMETHODIMP_(ULONG) EventCallbacks::AddRef( THIS ) { // This class is designed to be static so // there's no true refcount. return 1; } STDMETHODIMP_(ULONG) EventCallbacks::Release( THIS ) { // This class is designed to be static so // there's no true refcount. return 0; } STDMETHODIMP EventCallbacks::GetInterestMask( THIS_ OUT PULONG Mask ) { *Mask = DEBUG_EVENT_CHANGE_ENGINE_STATE; return S_OK; } STDMETHODIMP EventCallbacks::ChangeEngineState( THIS_ IN ULONG Flags, IN ULONG64 Argument ) { if (Flags & DEBUG_CES_EXECUTION_STATUS) { g_ExecStatus = (ULONG)Argument; // If this notification came from a wait completing // we want to wake up the input thread so that new // commands can be processed. If it came from inside // a wait we don't want to ask for input as the engine // may go back to running at any time. if (!(Argument & DEBUG_STATUS_INSIDE_WAIT)) { // Wake up the wait loop. g_ExitDispatchClient->ExitDispatch(g_Client); } } return S_OK; } EventCallbacks g_EventCb; //---------------------------------------------------------------------------- // // Functions. // //---------------------------------------------------------------------------- void Exit(int Code, PCSTR Format, ...) { // Clean up any resources. if (g_Control != NULL) { g_Control->Release(); } if (g_ExitDispatchClient != NULL) { g_ExitDispatchClient->Release(); } if (g_Client != NULL) { g_Client->EndSession(DEBUG_END_DISCONNECT); g_Client->Release(); } // Output an error message if given. if (Format != NULL) { va_list Args; va_start(Args, Format); vfprintf(stderr, Format, Args); va_end(Args); } exit(Code); } void CreateInterfaces(void) { HRESULT Status; // Start things off by getting an initial interface from // the engine. This can be any engine interface but is // generally IDebugClient as the client interface is // where sessions are started. if ((Status = DebugConnect(g_Connect, __uuidof(IDebugClient), (void**)&g_Client)) != S_OK) { Exit(1, "DebugConnect(%s) failed, 0x%X\n", g_Connect, Status); } // Query for some other interfaces that we'll need. if ((Status = g_Client->QueryInterface(__uuidof(IDebugControl), (void**)&g_Control)) != S_OK) { Exit(1, "QueryInterface failed, 0x%X\n", Status); } if ((Status = g_Client->SetEventCallbacks(&g_EventCb)) != S_OK) { Exit(1, "SetEventCallbacks failed, 0x%X\n", Status); } // // This app may wait inside of a DispatchCallbacks // while it's waiting for the server to break in. // We'll need to be able to exit that dispatch so // we need another connection to the server to // send the exit request. // // This could all be avoided by simply doing a polling // loop when waiting, but the intent of this sample // is to show some of the more advanced callback-driven // techniques. // if ((Status = DebugConnect(g_Connect, __uuidof(IDebugClient), (void**)&g_ExitDispatchClient)) != S_OK) { Exit(1, "DebugConnect(%s) failed, 0x%X\n", g_Connect, Status); } } void ParseCommandLine(int Argc, char** Argv) { while (--Argc > 0) { Argv++; if (!strcmp(*Argv, "-b")) { g_ForceBreak = TRUE; } else if (!strcmp(*Argv, "-cmd")) { if (Argc < 2) { Exit(1, "-cmd missing argument\n"); } Argv++; Argc--; g_Command = *Argv; } else if (!strcmp(*Argv, "-remote")) { if (Argc < 2) { Exit(1, "-remote missing argument\n"); } Argv++; Argc--; g_Connect = *Argv; } else { Exit(1, "Unknown command line argument '%s'\n", *Argv); } } if (!g_Connect) { Exit(1, "No connection string specified, use -remote \n"); } } void WaitForBreakIn(void) { HRESULT Status; // If we're forcing a break in request one. if (g_ForceBreak) { if ((Status = g_Control->SetInterrupt(DEBUG_INTERRUPT_ACTIVE)) != S_OK) { Exit(1, "SetInterrupt failed, 0x%X\n", Status); } } // Check on the current state as the server may be broken in. if ((Status = g_Control->GetExecutionStatus(&g_ExecStatus)) != S_OK) { Exit(1, "GetExecutionStatus failed, 0x%X\n", Status); } printf("Waiting for break-in...\n"); while (g_ExecStatus != DEBUG_STATUS_BREAK) { // Wait for the server to enter the break-in state. // When this happens our event callbacks will get called // to update g_ExecStatus and wake up this wait. if ((Status = g_Client->DispatchCallbacks(INFINITE)) != S_OK) { Exit(1, "DispatchCallbacks failed, 0x%X\n", Status); } } // The server is broken in. Another user can immediately resume // it but we'll assume that we're not competing with // other server users. } void __cdecl main(int Argc, char** Argv) { ParseCommandLine(Argc, Argv); CreateInterfaces(); WaitForBreakIn(); printf("Executing '%s' on server\n", g_Command); g_Control->Execute(DEBUG_OUTCTL_ALL_CLIENTS, g_Command, DEBUG_EXECUTE_DEFAULT); Exit(0, NULL); }