Figure 5   Remote Debugger Files

File
Copy From Location
MSVCMON.EXE
VS\MSDEV98\BIN
TLNOT.DLL
VS\MSDEV98\BIN
DM.DLL
VS\MSDEV98\BIN
MSDIS110.DLL
VS\MSDEV98\BIN
MSVCRT.DLL
%SYSTEMROOT%\System(32)
MSVCP60.DLL
%SYSTEMROOT%\System(32)
PSAPI.DLL*
%SYSTEMROOT%\System32
*Only if the remote machine is using Windows NT


Figure 6   Commands.cpp


 /*----------------------------------------------------------------------
 John Robbins
 Microsoft Systems Journal, August 1999 - Bugslayer Column!
 ----------------------------------------------------------------------*/
 #include "stdafx.h"
 #include "AdvancedBreakpoints.h"
 #include "CmdBPDlg.h"
 #include "Commands.h"
 
 #pragma warning ( disable : 4310 )
 
 #ifdef _DEBUG
 #define new DEBUG_NEW
 #undef THIS_FILE
 static char THIS_FILE[] = __FILE__;
 #endif
 
 /*//////////////////////////////////////////////////////////////////////
                            File Scope Typdefs
 //////////////////////////////////////////////////////////////////////*/
 // A simple structure to make setting the WM_KEYDOWN LPARAM values easier.
 typedef struct tag_KEYDATA
 {
     WORD    cRepeat       ;
     BYTE    ScanCode      ;
     BYTE    fExtended : 1 ;
     BYTE    Reserved  : 3 ;
     BYTE    fAltDown  : 1 ;
     BYTE    fRepeat   : 1 ;
     BYTE    fUp       : 1 ;
 } KEYDATA ;
 
 /*//////////////////////////////////////////////////////////////////////
                     File Scope Constants and Defines
 //////////////////////////////////////////////////////////////////////*/
 const LPCTSTR k_MSDEVTITLE  = _T ( "Microsoft Visual C++" ) ;
 const LPCTSTR k_ADDRESSTEXT = _T ( "&Address:" ) ;
 const LPCTSTR k_MEMORY      = _T ( "Memory" ) ;
 
 /*//////////////////////////////////////////////////////////////////////
                            File Scope Typdefs
 //////////////////////////////////////////////////////////////////////*/
 // Enumerator function for top level windows.
 static BOOL CALLBACK EnumWins ( HWND hWnd , LPARAM lParam ) ;
 // Enumerator function for child windows.
 static BOOL CALLBACK EnumChildWins ( HWND hWnd , LPARAM lParam ) ;
 // Returns TRUE if the hWnd has a sibling window with the title "&Address:".
 static BOOL CheckForAddressSibling ( HWND hWnd ) ;
 // Looks for the case where the memory window is undocked and a top-level window.
 static HWND LookForUndockedMemWindow ( void ) ;
 // Forces text into the memory window edit control.
 static void ForceText ( HWND hWnd , LPCTSTR szText ) ;
 // The hack-o-rama edit control subclass function.
 static LRESULT CALLBACK EditSubClass ( HWND   hWnd   ,
                                        UINT   uiMsg  ,
                                        WPARAM wParam ,
                                        LPARAM lParam  ) ;
 
 /*//////////////////////////////////////////////////////////////////////
                            File Scope Globals
 //////////////////////////////////////////////////////////////////////*/
 // The real memory window edit control WNDPROC.  This is so my subclass
 // can unhook itself.
 static WNDPROC g_pOrigProc = NULL ;
 // The buffer that holds the text my subclass is supposed to force into
 // the memory edit control when I have the control subclassed.
 static TCHAR g_szText[ MAX_PATH ] ;
 
 
 /*//////////////////////////////////////////////////////////////////////
                            Code Starts Here!
 //////////////////////////////////////////////////////////////////////*/
 CCommands::CCommands()
 {
     m_pApplication = NULL;
     m_pDebugger = NULL;
     m_pApplicationEventsObj = NULL;
     m_pDebuggerEventsObj = NULL;
 }
 
 CCommands::~CCommands()
 {
     ASSERT (m_pApplication != NULL);
     m_pApplication->Release();
     if (m_pDebugger != NULL)
     {
         m_pDebugger->Release();
         m_pDebugger = NULL;
     }
 }
 
 void CCommands::SetApplicationObject(IApplication* pApplication)
 {
     // This function assumes pApplication has already been AddRef'd
     //  for us, which CDSAddIn did in its QueryInterface call
     //  just before it called us.
     m_pApplication = pApplication;
 
     // Create Application event handlers
     XApplicationEventsObj::CreateInstance(&m_pApplicationEventsObj);
     m_pApplicationEventsObj->AddRef();
     m_pApplicationEventsObj->Connect(m_pApplication);
     m_pApplicationEventsObj->m_pCommands = this;
 
     // Create Debugger event handler
     CComPtr<IDispatch> pDebugger;
     if (SUCCEEDED(m_pApplication->get_Debugger(&pDebugger))
         && pDebugger != NULL)
     {
         XDebuggerEventsObj::CreateInstance(&m_pDebuggerEventsObj);
         m_pDebuggerEventsObj->AddRef();
         m_pDebuggerEventsObj->Connect(pDebugger);
         m_pDebuggerEventsObj->m_pCommands = this;
         // I need to keep a debugger object around or the event will
         // never get triggered.
         VERIFY(SUCCEEDED(pDebugger->QueryInterface(IID_IDebugger,
                                               (LPVOID*) &m_pDebugger)));
     }
 }
 
 void CCommands::UnadviseFromEvents()
 {
     ASSERT (m_pApplicationEventsObj != NULL);
     m_pApplicationEventsObj->Disconnect(m_pApplication);
     m_pApplicationEventsObj->Release();
     m_pApplicationEventsObj = NULL;
 
     if (m_pDebuggerEventsObj != NULL)
     {
         // Since we were able to connect to the Debugger events, we
         //  should be able to access the Debugger object again to
         //  unadvise from its events (thus the VERIFY_OK below--see stdafx.h).
         CComPtr<IDispatch> pDebugger;
         VERIFY_OK(m_pApplication->get_Debugger(&pDebugger));
         ASSERT (pDebugger != NULL);
         m_pDebuggerEventsObj->Disconnect(pDebugger);
         m_pDebuggerEventsObj->Release();
         m_pDebuggerEventsObj = NULL;
     }
     if ( NULL != m_pDebugger )
     {
         m_pDebugger->Release ( ) ;
         m_pDebugger = NULL ;
     }
 }
 
 
 ////////////////////////////////////////////////////////////////////////
 // Event handlers
 
 // TODO: Fill out the implementation for those events you wish to handle
 //  Use m_pCommands->GetApplicationObject() to access the Developer
 //  Studio Application object
 
 // Application events
 
 HRESULT CCommands::XApplicationEvents::BeforeBuildStart()
 {
     AFX_MANAGE_STATE(AfxGetStaticModuleState());
     return S_OK;
 }
 
 HRESULT CCommands::XApplicationEvents::
                  BuildFinish(long /*nNumErrors*/, long /*nNumWarnings*/)
 {
     AFX_MANAGE_STATE(AfxGetStaticModuleState());
     return S_OK;
 }
 
 HRESULT CCommands::XApplicationEvents::BeforeApplicationShutDown()
 {
     AFX_MANAGE_STATE(AfxGetStaticModuleState());
     return S_OK;
 }
 
 HRESULT CCommands::XApplicationEvents::DocumentOpen(IDispatch* /*theDocument*/)
 {
     AFX_MANAGE_STATE(AfxGetStaticModuleState());
     return S_OK;
 }
 
 HRESULT CCommands::XApplicationEvents::
                          BeforeDocumentClose(IDispatch* /*theDocument*/)
 {
     AFX_MANAGE_STATE(AfxGetStaticModuleState());
     return S_OK;
 }
 
 HRESULT CCommands::XApplicationEvents::DocumentSave(IDispatch* /*theDocument*/)
 {
     AFX_MANAGE_STATE(AfxGetStaticModuleState());
     return S_OK;
 }
 
 HRESULT CCommands::XApplicationEvents::NewDocument(IDispatch* /*theDocument*/)
 {
     AFX_MANAGE_STATE(AfxGetStaticModuleState());
     return S_OK;
 }
 
 HRESULT CCommands::XApplicationEvents::WindowActivate(IDispatch* /*theWindow*/)
 {
     AFX_MANAGE_STATE(AfxGetStaticModuleState());
     return S_OK;
 }
 
 HRESULT CCommands::XApplicationEvents::WindowDeactivate(IDispatch* /*theWindow*/)
 {
     AFX_MANAGE_STATE(AfxGetStaticModuleState());
     return S_OK;
 }
 
 HRESULT CCommands::XApplicationEvents::WorkspaceOpen()
 {
     AFX_MANAGE_STATE(AfxGetStaticModuleState());
     return S_OK;
 }
 
 HRESULT CCommands::XApplicationEvents::WorkspaceClose()
 {
     AFX_MANAGE_STATE(AfxGetStaticModuleState());
     return S_OK;
 }
 
 HRESULT CCommands::XApplicationEvents::NewWorkspace()
 {
     AFX_MANAGE_STATE(AfxGetStaticModuleState());
     return S_OK;
 }
 
 // Debugger event
 
 HRESULT CCommands::XDebuggerEvents::BreakpointHit(IDispatch* pBreakpoint)
 {
     AFX_MANAGE_STATE(AfxGetStaticModuleState());
 
     CComQIPtr<IBreakpoint, &IID_IBreakpoint> pBP = pBreakpoint ;
 
     // Convert this breakpoint into a string.
     CString sBPStr ;
     VERIFY ( FormatBreakpoint ( pBP , sBPStr ) ) ;
 
     // Look to see if this breakpoint is one that I am supposed to be
     // doing commands for.
     BPCMD cBpCmd ;
     BOOL bDoCommands = FALSE ;
     int iSize = m_pCommands->m_aBpCmd.GetSize ( ) ;
     for ( int i = 0 ; i < iSize ; i++ )
     {
         cBpCmd = m_pCommands->m_aBpCmd.GetAt ( i ) ;
         if ( cBpCmd.sBP == sBPStr )
         {
             bDoCommands = TRUE ;
             break ;
         }
     }
 
     if ( TRUE == bDoCommands )
     {
         int iCount = cBpCmd.aCmds.GetSize ( ) ;
         ASSERT ( iCount != 0 ) ;
         for ( int j = 0 ; j < iCount ; j++ )
         {
             COMMANDSTRUCT cCmd = cBpCmd.aCmds.GetAt ( j ) ;
 
             switch ( cCmd.eCmd )
             {
                 case eMemDump :
                     // BUG BUG BUG BUG BUG BUG BUG BUG BUG BUG BUG BUG
                     // Right now memory breakpoints only work with
                     // location breakpoints. Read the column for
                     // why this is and what you can do to work around it.
                     if ( dsLocation == cBpCmd.eType )
                     {
                         m_pCommands->
                          AdvancedBPCmd_memdump(cCmd.stStrParams.sParam);
                     }
                     break ;
                 case eTrace   :
                     m_pCommands->
                         AdvancedBPCmd_trace(cCmd.stTraceParams.sFmt  ,
                                             cCmd.stTraceParams.aParams);
                     break ;
                 case eMacro   :
                     m_pCommands->
                         AdvancedBPCmd_macro ( cCmd.stStrParams.sParam );
                     break ;
                 default :
                     ASSERT ( FALSE ) ;
                     break ;
             }
         }
     }
 
     return ( S_OK ) ;
 }
 
 
 ////////////////////////////////////////////////////////////////////////
 // CCommands methods
 
 STDMETHODIMP CCommands::AdvancedBreakpointsDialog()
 {
     AFX_MANAGE_STATE(AfxGetStaticModuleState())
 
     VERIFY_OK(m_pApplication->EnableModeless(VARIANT_FALSE));
 
     // Check that there is an active project.  No project == no advanced
     // breakpoint dialog.
     CComPtr<IDispatch> pProject ;
     HRESULT hRes = m_pApplication->get_ActiveProject ( &pProject ) ;
 
     if ( ! SUCCEEDED ( hRes ) )
     {
         AfxMessageBox ( IDS_NOPROJECTOPEN ) ;
         VERIFY_OK(m_pApplication->EnableModeless(VARIANT_TRUE));
         return ( S_OK ) ;
     }
 
     CCmdBPDlg dlg ;
 
     if ( FALSE == dlg.SetApplicationObject ( m_pApplication ) )
     {
         VERIFY_OK(m_pApplication->EnableModeless(VARIANT_TRUE));
         return ( S_OK ) ;
     }
 
     // Get the real data arrays into the dialog.
     dlg.SetArrays ( &m_aBpCmd , &m_aUnassociatedCmds ) ;
 
     if ( IDOK == dlg.DoModal ( ) )
     {
 #ifdef _DEBUG
         // Dump out the results.
         int iSize = m_aBpCmd.GetSize ( ) ;
         TRACE ( "m_aBpCmd size - %d\n" , iSize ) ;
         for ( int i = 0 ; i < iSize ; i++ )
         {
             BPCMD bpCmd = m_aBpCmd.GetAt ( i ) ;
             TRACE ( "Associated BP - %d\n" , i ) ;
             CString strCmds ;
             FormatAllCommands ( bpCmd , strCmds ) ;
             TRACE ( "    BP   : %s\n" , bpCmd.sBP ) ;
             TRACE ( "    CMDS : %s\n" , strCmds ) ;
         }
 
         iSize = m_aUnassociatedCmds.GetSize ( ) ;
         TRACE ( "m_aUnassociatedCmds size - %d\n" , iSize ) ;
         for ( i = 0 ; i < iSize ; i++ )
         {
             BPCMD bpCmd = m_aUnassociatedCmds.GetAt ( i ) ;
             TRACE ( "Associated BP - %d\n" , i ) ;
             CString strCmds ;
             FormatAllCommands ( bpCmd , strCmds ) ;
             TRACE ( "    BP   : %s\n" , bpCmd.sBP ) ;
             TRACE ( "    CMDS : %s\n" , strCmds ) ;
         }
 
 #endif  // _DEBUG
     }
 
     VERIFY_OK(m_pApplication->EnableModeless(VARIANT_TRUE));
 
     return S_OK;
 }
 
 void CCommands::AdvancedBPCmd_macro ( CString & sMacro )
 {
     ASSERT ( NULL != m_pApplication ) ;
     CComBSTR bstrTemp ;
     bstrTemp = (LPCTSTR)sMacro ;
     m_pApplication->ExecuteCommand ( bstrTemp ) ;
 }
 
 void CCommands::AdvancedBPCmd_trace ( CString      & sFmt ,
                                       CStringArray & aVars )
 {
     ASSERT ( NULL != m_pDebugger ) ;
     ASSERT ( NULL != m_pApplication ) ;
 
     CString sFull ;
     CComBSTR bstrTemp ;
     int iCurrVar = 0 ;
     int iLen = sFmt.GetLength ( ) ;
 
     for ( int i = 0 ; i < iLen ; i++ )
     {
         if ( ( _T ( '%' ) == sFmt[ i ]   ) &&
              ( _T ( 's' ) == sFmt[ i+1 ] )    )
         {
             // Move past the '%'.  The i++ in the for statement will
             // move past the 's'.
             i++ ;
 
             bstrTemp = aVars[ iCurrVar ] ;
             iCurrVar++ ;
 
             CComBSTR bstrRet ;
             VERIFY_OK ( m_pDebugger->Evaluate ( bstrTemp , &bstrRet ) );
 
             sFull += (BSTR)bstrRet ;
         }
         else
         {
             sFull += sFmt[ i ] ;
         }
     }
 
     bstrTemp = sFull ;
 
     VERIFY_OK ( m_pApplication->PrintToOutputWindow ( bstrTemp ) ) ;
 
 }
 
 void CCommands::AdvancedBPCmd_memdump ( CString & sMem )
 {
     ASSERT ( NULL != m_pApplication ) ;
 
     // Activate the memory window.
     CComBSTR bstrTemp = _T ( "ActivateMemoryWindow" ) ;
     VERIFY_OK ( m_pApplication->ExecuteCommand ( bstrTemp ) ) ;
 
     // Find the IDE main window.
     HWND hWnd = NULL ;
     EnumWindows ( EnumWins , (LPARAM)&hWnd ) ;
 
     ASSERT ( NULL != hWnd ) ;
     if ( NULL != hWnd )
     {
         // Set the focus to the IDE window.  I don't know if I really
         // need to do this, but it cannot hurt.
         SetFocus ( hWnd ) ;
 
         // First, try and find the memory edit control if it is inside
         // the IDE environment.  (This is either a docked window or
         // a regular MDI child window.)
         HWND hEdit = NULL ;
         EnumChildWindows ( hWnd , EnumChildWins , (LPARAM)&hEdit ) ;
 
         // If I did not find it, then it is a floating top-level window.
         if ( NULL == hEdit )
         {
             hEdit = LookForUndockedMemWindow ( ) ;
         }
 
         // This is pretty serious!
         ASSERT ( NULL != hEdit ) ;
         if ( NULL != hEdit )
         {
 
             // Force the focus to the edit control, and fake the mouse click
             SetFocus ( hEdit ) ;
             PostMessage ( hEdit , WM_LBUTTONDOWN , MK_LBUTTON , 0 ) ;
             PostMessage ( hEdit , WM_LBUTTONUP , MK_LBUTTON , 0 ) ;
 
 
             // Force my text In and subclass the edit control so that I can catch 
             // the WM_SETTEXT message to ensure the text Is properly set.
 
             // Force the user's dump value in no matter what.
             ForceText ( hEdit , sMem ) ;
             // Stick the dump value in global memory so the subclass
             // proc can put it in the edit control if it needs to.
             _tcscpy ( g_szText , sMem ) ;
             // Subclass the control to ensure that my value "sticks."
             g_pOrigProc =(WNDPROC)SetWindowLong(hEdit             ,
                                                 GWL_WNDPROC       ,
                                                (LONG)EditSubClass );
         }
     }
 }
 
 static BOOL CALLBACK EnumWins ( HWND hWnd , LPARAM lParam )
 {
     HWND * phWnd = (HWND*)lParam ;
 
     TCHAR szTitle[ MAX_PATH ] ;
 
     if ( 0 != GetWindowText ( hWnd , szTitle , MAX_PATH ) )
     {
         // Does the title have "Microsoft Visual C++" anywhere in it?
         if ( 0 != _tcsstr ( szTitle , k_MSDEVTITLE ) )
         {
             // Sure does, the enumeration is done.
             *phWnd = hWnd ;
             return ( FALSE ) ;
         }
     }
     return ( TRUE ) ;
 }
 
 static BOOL CALLBACK EnumChildWins ( HWND hWnd , LPARAM lParam )
 {
     HWND * phWnd = (HWND*)lParam ;
 
     TCHAR szTemp[ MAX_PATH ] ;
 
     // Get the class name of this child window.
     if ( 0 != GetClassName ( hWnd , szTemp , MAX_PATH ) )
     {
         // Is it an edit control?
         if ( 0 == _tcscmp ( szTemp , _T ( "Edit" ) ) )
         {
             if ( TRUE == CheckForAddressSibling ( hWnd ) )
             {
                 *phWnd = hWnd ;
                 return ( FALSE ) ;
             }
         }
     }
     return ( TRUE ) ;
 }
 
 static BOOL CheckForAddressSibling ( HWND hWnd )
 {
     // Check this window to see if its nearest sibling is "&Address:"
     // so I know that it is the memory edit control.  It should
     // always be the previous sibling, but I don't take any chances.
     TCHAR szTemp[ MAX_PATH ] ;
     HWND hSib = ::GetWindow ( hWnd , GW_HWNDPREV ) ;
     if ( 0 != GetWindowText ( hSib , szTemp , MAX_PATH ) )
     {
         if ( 0 == _tcscmp ( szTemp , k_ADDRESSTEXT ) )
         {
             return ( TRUE ) ;
         }
     }
     hSib = ::GetWindow ( hWnd , GW_HWNDNEXT ) ;
     if ( 0 != GetWindowText ( hSib , szTemp , MAX_PATH ) )
     {
         if ( 0 == _tcscmp ( szTemp , k_ADDRESSTEXT ) )
         {
             return ( TRUE ) ;
         }
     }
     return ( FALSE ) ;
 }
 
 static HWND LookForUndockedMemWindow ( void )
 {
     // The undocked Memory window hierarchy looks like the following:
     // + "Memory" Window
     //   +---Frame window
     //      +---Main window
     //          +---Dialog window
     //          |   +---"Address" label
     //          |   +---Edit control        <-- What I need to find.
     //          +---Display window (all values)
 
     HWND hRet = NULL ;
     // The first step is to find a top-level window with the title
     // "Memory".
     HWND hTop = FindWindow ( NULL , k_MEMORY ) ;
     if ( NULL != hTop )
     {
         // Get the first child window.
         HWND hChild = GetWindow ( hTop , GW_CHILD ) ;
         ASSERT ( NULL != hChild ) ;
         if ( NULL != hChild )
         {
             // Get that child's child.
             hChild = GetWindow ( hChild , GW_CHILD ) ;
             ASSERT ( NULL != hChild ) ;
             if ( NULL != hChild )
             {
                 // Get the dialog window.
                 HWND hDlg = GetWindow ( hChild , GW_CHILD ) ;
                 ASSERT ( NULL != hDlg ) ;
                 if ( NULL != hDlg )
                 {
                     // Get the static "Address" label.
                     hChild = GetWindow ( hDlg , GW_CHILD ) ;
                     ASSERT ( NULL != hChild ) ;
                     if ( NULL != hChild )
                     {
                         // Get the edit control.
                         hRet = GetWindow ( hChild , GW_HWNDNEXT ) ;
                         ASSERT ( NULL != hRet ) ;
                     }
                 }
             }
         }
     }
     return ( hRet ) ;
 }
 
 static void ForceText ( HWND hWnd , LPCTSTR szText )
 {
     // Set the text into the memory window edit control.
     SetWindowText ( hWnd , szText ) ;
 
     // Now I need to fake that the user pressed the ENTER key.  To
     // figure out what I needed to do, I watched all the messages to
     // the edit control with Spy++.
     KEYDATA kd ;
 
     memset ( &kd , 0 , sizeof ( KEYDATA ) ) ;
     kd.cRepeat = 0 ;
     kd.ScanCode = (BYTE)MapVirtualKey ( VK_RETURN , 0 ) ;
     kd.fExtended = 0 ;
     kd.fAltDown = 1 ;
     kd.fRepeat = 0 ;
     kd.fUp = 0 ;
 
     DWORD dwVal ;
     memcpy ( &dwVal , &kd , sizeof ( DWORD ) ) ;
     PostMessage ( hWnd , WM_KEYDOWN , VK_RETURN , dwVal ) ;
 }
 
 static LRESULT CALLBACK EditSubClass ( HWND hWnd , UINT uiMsg , WPARAM wParam ,
                                        LPARAM lParam )
 {
     // Always let the real window have it first.
     LRESULT lRet = CallWindowProc ( g_pOrigProc , hWnd , uiMsg ,
                                     wParam , lParam ) ;
     if ( WM_SETTEXT == uiMsg )
     {
         // OK, the IDE has set the edit control to the previous value.
         // Undo the subclass and then poink my value in.
         SetWindowLong ( hWnd , GWL_WNDPROC , (LONG)g_pOrigProc ) ;
 
         ForceText ( hWnd , g_szText ) ;
     }
     else if ( EM_SETSEL == uiMsg )
     {
         // The IDE did not need to set the text and has moved on to
         // processing it.  Therefore, I just undo the subclass.
         SetWindowLong ( hW  nd , GWL_WNDPROC , (LONG)g_pOrigProc ) ;
     }
     return ( lRet ) ;
 }