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 ) ;
}