home *** CD-ROM | disk | FTP | other *** search
/ io Programmo 40 / IOPROG_40.ISO / SOFT / NETFrameworkSDK.exe / comsdk.cab / samples1.exe / Debugger / commands.cpp < prev    next >
Encoding:
C/C++ Source or Header  |  2000-06-23  |  211.8 KB  |  7,192 lines

  1. /*------------------------------------------------------------------------- *
  2.  * commands.cpp: com debugger shell functions
  3.  * ------------------------------------------------------------------------- */
  4.  
  5. #include "stdafx.h"
  6.  
  7. #include "cordbpriv.h"
  8. #include "corsvc.h"
  9.  
  10. #ifdef _INTERNAL_DEBUG_SUPPORT_
  11. #include "InternalOnly.h"
  12. #endif
  13.  
  14.  
  15. /* ------------------------------------------------------------------------- *
  16.  * RunDebuggerCommand is used to create and run a new NGWS process.
  17.  * ------------------------------------------------------------------------- */
  18.  
  19. class RunDebuggerCommand : public DebuggerCommand
  20. {
  21. private:
  22.     WCHAR *m_lastRunArgs;
  23.  
  24. public:
  25.     RunDebuggerCommand(const WCHAR *name, int minMatchLength = 0)
  26.         : DebuggerCommand(name, minMatchLength), m_IFEOData(NULL)
  27.     {
  28.         if (g_pShell)
  29.             m_lastRunArgs = g_pShell->m_lastRunArgs;
  30.         else
  31.             m_lastRunArgs = NULL;
  32.     }
  33.  
  34.     virtual ~RunDebuggerCommand()
  35.     {
  36.     }
  37.  
  38.     char *m_IFEOData;
  39.     HKEY  m_IFEOKey;
  40.     DWORD m_IFEOKeyType;
  41.     DWORD m_IFEOKeyLen;
  42.     
  43.     //
  44.     // Yanking the Debugger value out of the registry will prevent
  45.     // infinite launch recusion when we're not the win32 debugger of
  46.     // the process.
  47.     //
  48.     void TurnOffIFEO(WCHAR *args)
  49.     {
  50.         // Extract the .exe name from the command.
  51.         WCHAR *endOfExe = wcschr(args, L' ');
  52.  
  53.         if (endOfExe)
  54.             *endOfExe = L'\0';
  55.  
  56.         WCHAR *exeNameStart = wcsrchr(args, L'\\');
  57.  
  58.         if (exeNameStart == NULL)
  59.             exeNameStart = args;
  60.         else
  61.             exeNameStart++;
  62.  
  63.         MAKE_ANSIPTR_FROMWIDE(exeNameA, exeNameStart);
  64.         
  65.         // Is there an entry in the registry for this exe?
  66.         char buffer[1024];
  67.  
  68.         sprintf(buffer, "Software\\Microsoft\\Windows NT\\CurrentVersion\\Image File Execution Options\\%s", exeNameA);
  69.  
  70.         if (!strchr(buffer, '.'))
  71.             strcat(buffer, ".exe");
  72.         
  73.         if (RegOpenKeyExA(HKEY_LOCAL_MACHINE, buffer, 0, KEY_ALL_ACCESS,
  74.                           &m_IFEOKey) == ERROR_SUCCESS)
  75.         {
  76.             // Get the length of the key data.
  77.             if (RegQueryValueExA(m_IFEOKey, "Debugger", NULL,
  78.                                  &m_IFEOKeyType, NULL,
  79.                                  &m_IFEOKeyLen) == ERROR_SUCCESS)
  80.             {
  81.                 // Make some room...
  82.                 m_IFEOData = new char[m_IFEOKeyLen + 1];
  83.  
  84.                 if (m_IFEOData)
  85.                 {
  86.                     // Grab the data....
  87.                     if (RegQueryValueExA(m_IFEOKey, "Debugger", NULL,
  88.                                          &m_IFEOKeyType,
  89.                                          (BYTE*) m_IFEOData,
  90.                                          &m_IFEOKeyLen) == ERROR_SUCCESS)
  91.                     {
  92.                         // We've got a copy of the value, so nuke it.
  93.                         RegDeleteValueA(m_IFEOKey, "Debugger");
  94.                     }
  95.                 }
  96.             }
  97.  
  98.             // Leave m_IFEOKey open. TurnOnIFEO will close it.
  99.         }
  100.  
  101.         // Put the args back.
  102.         if (endOfExe)
  103.             *endOfExe = L' ';
  104.     }
  105.  
  106.     void TurnOnIFEO(void)
  107.     {
  108.         if (m_IFEOData != NULL)
  109.         {
  110.             // Put back the IFEO key now that the process is
  111.             // launched. Note: we don't care if this part fails...
  112.             RegSetValueExA(m_IFEOKey, "Debugger", NULL, m_IFEOKeyType,
  113.                            (const BYTE*) m_IFEOData, m_IFEOKeyLen);
  114.  
  115.             delete [] m_IFEOData;
  116.  
  117.             RegCloseKey(m_IFEOKey);
  118.         }
  119.     }
  120.     
  121.     void Do(DebuggerShell *shell, ICorDebug *cor, const WCHAR *args)
  122.     {
  123.         // If no arguments provided, use the previously provided arguments
  124.         if ((*args == L'\0') && (m_lastRunArgs != NULL))
  125.             args = m_lastRunArgs;
  126.  
  127.         // If there were no arguments and no previously existing arguments
  128.         if (args == NULL || *args == L'\0')
  129.         {
  130.             shell->Error(L"Program name expected.\n");
  131.             return;
  132.         }
  133.  
  134.         // If the arguments are different than the last, save them as the last
  135.         if (    m_lastRunArgs == NULL 
  136.              || ( args != NULL
  137.              && 0 != wcscmp(args, m_lastRunArgs) ) )
  138.         {
  139.             delete [] shell->m_lastRunArgs;
  140.             m_lastRunArgs = NULL;
  141.  
  142.             shell->m_lastRunArgs = new WCHAR [wcslen(args) + 1];
  143.  
  144.             if (shell->m_lastRunArgs == NULL)
  145.             {
  146.                 shell->ReportError(E_OUTOFMEMORY);
  147.                 return;
  148.             }
  149.  
  150.             wcscpy (shell->m_lastRunArgs, args);
  151.  
  152.             m_lastRunArgs = shell->m_lastRunArgs;
  153.         }
  154.  
  155.         // Kill the currently running process, if it exists
  156.         shell->Kill();
  157.  
  158.         // Create and fill in the structure for creating a new NGWS process
  159.         STARTUPINFOW startupInfo = {0};
  160.         startupInfo.cb = sizeof (STARTUPINFOW);
  161.         PROCESS_INFORMATION processInfo = {0};
  162.  
  163.         // Get current directory parameter.
  164.         LPWSTR szCurrentDir;
  165.         szCurrentDir = wcschr(m_lastRunArgs, L';');
  166.         if (szCurrentDir) *szCurrentDir++ = 0;
  167.  
  168.         // Createprocess needs to modify the arguments, so make temp copy
  169.         WCHAR *argsCopy = (WCHAR *) _alloca(wcslen(args) * sizeof (WCHAR));
  170.         wcscpy(argsCopy, args);
  171.  
  172.         CorDebugCreateProcessFlags cddf = DEBUG_NO_SPECIAL_OPTIONS;
  173.         
  174.         if (shell->m_rgfActiveModes & DSM_ENABLE_EDIT_AND_CONTINUE)
  175.             cddf = DEBUG_ENABLE_EDIT_AND_CONTINUE;
  176.  
  177.         // Create the new NGWS process
  178.         ICorDebugProcess *proc;
  179.         DWORD createFlags = 0;
  180.  
  181.         if (shell->m_rgfActiveModes & DSM_SEPARATE_CONSOLE)
  182.             createFlags |= CREATE_NEW_CONSOLE;
  183.  
  184.         if (shell->m_rgfActiveModes & DSM_WIN32_DEBUGGER)
  185.             createFlags |= DEBUG_ONLY_THIS_PROCESS;
  186.  
  187.         // Turn off any Image File Execution Option settings in the
  188.         // registry for this app.
  189.         TurnOffIFEO(argsCopy);
  190.         
  191.         HRESULT hr = cor->CreateProcess(NULL, argsCopy,
  192.                                         NULL, NULL, TRUE, 
  193.                                         createFlags,
  194.                                         NULL, szCurrentDir, 
  195.                                         &startupInfo, &processInfo,
  196.                                         cddf, &proc);
  197.         
  198.         // Turn any Image File Execution Option settings in the
  199.         // registry for this app back on.
  200.         TurnOnIFEO();
  201.         
  202.         // Succeeded, so close process handle since the callback will
  203.         // provide it
  204.         if (SUCCEEDED(hr))
  205.         {
  206.             BOOL succ = CloseHandle(processInfo.hProcess);
  207.  
  208.             // Some sort of error has occured
  209.             if (!succ)
  210.             {
  211.                 WCHAR *p = wcschr(argsCopy, L' ');
  212.  
  213.                 if (p != NULL)
  214.                     *p = '\0';
  215.                 
  216.                 shell->Write(L"'%s'", argsCopy);
  217.                 shell->ReportError(HRESULT_FROM_WIN32(GetLastError()));
  218.                 return;
  219.             }
  220.  
  221.             // We need to remember our target process now so we can
  222.             // make use of it even before the managed CreateProcess
  223.             // event arrives. This is mostly needed for Win32
  224.             // debugging support.
  225.             g_pShell->SetTargetProcess(proc);
  226.             
  227.             // We don't care to keep this reference to the new process.
  228.             proc->Release();
  229.             
  230.             // Run the newly-created process
  231.             shell->Run(true); // No continue for CreateProcess.
  232.         }
  233.  
  234.         // Otherwise report the error
  235.         else
  236.         {
  237.             WCHAR *p = wcschr(argsCopy, L' ');
  238.  
  239.             if (p != NULL)
  240.                 *p = '\0';
  241.             
  242.             shell->ReportError(hr);
  243.         }
  244.     }
  245.  
  246.     // Provide help specific to this command
  247.     void Help(Shell *shell)
  248.     {
  249.         ShellCommand::Help(shell);
  250.         shell->Write(L" [<executable> [<args>]]");
  251.         shell->Write(L"\nStops the current program and runs a new one, using the");
  252.         shell->Write(L"\ngiven command line. If no args are used, uses the");
  253.         shell->Write(L"\nsame command line as last time.");
  254.         shell->Write(L"\n");
  255.     }
  256.  
  257.     const WCHAR *ShortHelp(Shell *shell)
  258.     {
  259.         return L"Run a new program";
  260.     }
  261. };
  262.  
  263. /* ------------------------------------------------------------------------- *
  264.  * AttachDebuggerCommand is used to attach to an already-existing NGWS
  265.  * process.
  266.  * ------------------------------------------------------------------------- */
  267.  
  268. class AttachDebuggerCommand : public DebuggerCommand
  269. {
  270. public:
  271.     AttachDebuggerCommand(const WCHAR *name, int minMatchLength = 0)
  272.         : DebuggerCommand(name, minMatchLength)
  273.         {
  274.         }
  275.  
  276.     void Do(DebuggerShell *shell, ICorDebug *cor, const WCHAR *args)
  277.         {
  278.             int pid;
  279.  
  280.             if (shell->GetIntArg(args, pid))
  281.             {
  282.                 // Kill the currently running process
  283.                 shell->Kill();
  284.  
  285.                 BOOL win32Attach = FALSE;
  286.                 
  287.                 if (shell->m_rgfActiveModes & DSM_WIN32_DEBUGGER)
  288.                     win32Attach = TRUE;
  289.                 
  290.                 // Attempt to attach to the provided process ID
  291.                 ICorDebugProcess *proc;
  292.  
  293.                 HRESULT hr = cor->DebugActiveProcess(pid, win32Attach, &proc);
  294.  
  295.                 if (SUCCEEDED(hr))
  296.                 {
  297.                     // We don't care to keep this reference to the process.
  298.                     g_pShell->SetTargetProcess(proc);
  299.                     proc->Release();
  300.  
  301.                     if (!(g_pShell->m_rgfActiveModes & 
  302.                           DSM_SHOW_SELECTIVE_APPDOMAIN))
  303.                     {
  304.                         shell->Run(true); // No initial Continue!
  305.                     }
  306.                     else
  307.                     {
  308.                         WaitForSingleObject (shell->m_hProcessCreated, INFINITE);
  309.                         g_pShell->Write (L"Attach::Please select the app domain to attach to (use command 'app 0').\n");
  310.                     }
  311.                 }
  312.                 else
  313.                     shell->ReportError(hr);
  314.             }
  315.             else
  316.                 Help(shell);
  317.         }
  318.  
  319.     // Provide help specific to this command
  320.     void Help(Shell *shell)
  321.     {
  322.         ShellCommand::Help(shell);
  323.         shell->Write(L" [<process id>]");
  324.         shell->Write(L"\nStops the current program and attaches to a new process.");
  325.         shell->Write(L"\n");
  326.     }
  327.  
  328.     const WCHAR *ShortHelp(Shell *shell)
  329.     {
  330.         return L"Attach to a running process";
  331.     }
  332. };
  333.  
  334.  
  335. /* ------------------------------------------------------------------------- *
  336.  * This is an implementation of the ICORSvcDbgNotify class to be used by
  337.  * the AttachDebuggerAtRTStartupCommand.
  338.  * ------------------------------------------------------------------------- */
  339. class CINotifyImpl : public ICORSvcDbgNotify
  340. {
  341. private:
  342.     LONG m_cRef;
  343.  
  344. public:
  345.     // ------------------------------------------------------------------------
  346.     // Other
  347.     CINotifyImpl() : m_cRef(1)
  348.     {
  349.     }
  350.  
  351.     // ------------------------------------------------------------------------
  352.     // IUnknown
  353.  
  354.     STDMETHODIMP    QueryInterface (REFIID iid, void **ppv)
  355.     {
  356.         if (ppv == NULL)
  357.             return E_INVALIDARG;
  358.  
  359.         if (iid == IID_IUnknown)
  360.         {
  361.             *ppv = (IUnknown *) this;
  362.             AddRef();
  363.             return S_OK;
  364.         }
  365.  
  366.         if (iid == IID_ICORSvcDbgNotify)
  367.         {
  368.             *ppv = (ICORSvcDbgNotify *) this;
  369.             AddRef();
  370.             return S_OK;
  371.         }
  372.  
  373.         *ppv = NULL;
  374.         return E_NOINTERFACE;
  375.     }
  376.  
  377.     STDMETHODIMP_(ULONG) AddRef(void)
  378.     {
  379.         return InterlockedIncrement(&m_cRef);
  380.     }
  381.  
  382.     STDMETHODIMP_(ULONG) Release(void)
  383.     {
  384.         if (InterlockedDecrement(&m_cRef) == 0)
  385.         {
  386.             //delete this;
  387.             return 0;
  388.         }
  389.  
  390.         return 1;
  391.     }
  392.  
  393.     // ------------------------------------------------------------------------
  394.     // ICORSvcDbgNotify
  395.  
  396.     /*
  397.      * NotifyRuntimeStartup will be called on the interface provided by a
  398.      * call to RequestRuntimeStartupNotification.  The runtime will not
  399.      * continue until the call to NotifyRuntimeStartup returns.
  400.      */
  401.     STDMETHODIMP NotifyRuntimeStartup(
  402.         UINT_PTR procId)
  403.     {
  404.         return (E_NOTIMPL);
  405.     }
  406.  
  407.     /*
  408.      * NotifyServiceStopped lets those who have requested events know that the
  409.      * service is being stopped, so they will not get their requested
  410.      * notifications.  Calls on this method should not take long - if any great
  411.      * amount of work must be done, spin up a new thread to do it and let this
  412.      * one return.
  413.      */
  414.     STDMETHODIMP NotifyServiceStopped()
  415.     {
  416.         return (E_NOTIMPL);
  417.     }
  418. };
  419.  
  420.  
  421. /* ------------------------------------------------------------------------- *
  422.  * SyncAttachDebuggerAtRTStartupCommand will attach the debugger when the
  423.  * runtime starts up within a specified process.  The process must already
  424.  * exist, and must not have started the NGWS runtime.
  425.  * ------------------------------------------------------------------------- */
  426.  
  427. class SyncAttachDebuggerAtRTStartupCommand :
  428.     public DebuggerCommand, public CINotifyImpl
  429. {
  430. private:
  431.     DebuggerShell  *m_pShell;
  432.     HANDLE          m_hContinue;
  433.     ICorDebug      *m_pCor;
  434.  
  435. public:
  436.     SyncAttachDebuggerAtRTStartupCommand(const WCHAR *name, int minMatchLength = 0)
  437.         : DebuggerCommand(name, minMatchLength), CINotifyImpl(), m_pShell(NULL),
  438.           m_hContinue(NULL)
  439.         {
  440.         }
  441.  
  442.     ~SyncAttachDebuggerAtRTStartupCommand()
  443.     {
  444.         if (m_hContinue != NULL)
  445.             CloseHandle(m_hContinue);
  446.     }
  447.  
  448.     void Do(DebuggerShell *shell, ICorDebug *cor, const WCHAR *args)
  449.     {
  450.         int pid;
  451.         
  452.         if (shell->GetIntArg(args, pid))
  453.         {
  454.             m_pShell = shell;
  455.             m_pCor = cor;
  456.  
  457.             // Kill the currently running process
  458.             shell->Kill();
  459.  
  460.             // Get a reference to the debugger info interface for the NGWS service
  461.             HRESULT hr;
  462.             MULTI_QI    mq;
  463.  
  464.             mq.pIID = &IID_ICORSvcDbgInfo;
  465.             mq.pItf = NULL;
  466.             mq.hr = S_OK;
  467.             hr = CoCreateInstanceEx(CLSID_CORSvc, NULL, CLSCTX_LOCAL_SERVER, NULL, 1, &mq);
  468.  
  469.             if (SUCCEEDED(hr))
  470.             {
  471.                 // Now we have an info interface
  472.                 ICORSvcDbgInfo *psvc = (ICORSvcDbgInfo *) mq.pItf;
  473.                 _ASSERTE(psvc);
  474.  
  475.                 // Ask for notification when the runtime starts up
  476.                 hr = psvc->RequestRuntimeStartupNotification((UINT_PTR) pid, ((ICORSvcDbgNotify *) this));
  477.  
  478.                 // Run will return when the event queue has been drained
  479.                 shell->Run(true);
  480.  
  481.                 // let go of the object
  482.                 if (psvc)
  483.                     psvc->Release();
  484.             }
  485.         }
  486.         else
  487.             Help(shell);
  488.     }
  489.  
  490.     /*
  491.      * NotifyRuntimeStartup will be called on the interface provided by a
  492.      * call to RequestRuntimeStartupNotification.  The runtime will not
  493.      * continue until the call to NotifyRuntimeStartup returns.
  494.      */
  495.     STDMETHODIMP NotifyRuntimeStartup(
  496.         UINT_PTR procId)
  497.     {
  498.         // Invoke the logic to debug an active process
  499.         ICorDebugProcess *proc;
  500.         HRESULT hr = m_pCor->DebugActiveProcess(procId, FALSE, &proc);
  501.  
  502.         // Upon success, we return from the DCOM call right away, since
  503.         // the main runtime thread must be allowed to continue for the
  504.         // attach to complete and the call to Run from Do above to return
  505.         if (SUCCEEDED(hr))
  506.         {
  507.             // We don't care to keep this reference to the process.
  508.             proc->Release();
  509.         }
  510.         else
  511.             m_pShell->ReportError(hr);
  512.  
  513.         // Returning here indicates to the service that the runtime can now continue
  514.         return (S_OK);
  515.     }
  516.  
  517.     // Provide help specific to this command
  518.     void Help(Shell *shell)
  519.     {
  520.         ShellCommand::Help(shell);
  521.         shell->Write(L" [<process id>]");
  522.         shell->Write(L"\nStops the current program and waits for the process");
  523.         shell->Write(L"\nidentified by <process id> to start up the NGWS runtime,");
  524.         shell->Write(L"\nat which point it attaches the debugger and the shell continues.");
  525.         shell->Write(L"\nNOTE: the target process can not have already started up the");
  526.         shell->Write(L"\n      NGWS runtime - it must not have been loaded.");
  527.         shell->Write(L"\n");
  528.     }
  529.  
  530.     const WCHAR *ShortHelp(Shell *shell)
  531.     {
  532.         return L"Attach to a running process when the NGWS Runtime is loaded";
  533.     }
  534. };
  535.  
  536. /* ------------------------------------------------------------------------- *
  537.  * KillDebuggerCommand is used to terminate the current debugee.
  538.  * ------------------------------------------------------------------------- */
  539.  
  540. class KillDebuggerCommand : public DebuggerCommand
  541. {
  542. public:
  543.     KillDebuggerCommand(const WCHAR *name, int minMatchLength = 0)
  544.         : DebuggerCommand(name, minMatchLength)
  545.     {
  546.     }
  547.  
  548.     void Do(DebuggerShell *shell, ICorDebug *cor, const WCHAR *args)
  549.     {
  550.         // Kill the current debugee
  551.         shell->Kill();
  552.     }
  553.  
  554.     // Provide help specific to this command
  555.     void Help(Shell *shell)
  556.     {
  557.         ShellCommand::Help(shell);
  558.         shell->Write(L"\nStops the current program.");
  559.     }
  560.  
  561.     const WCHAR *ShortHelp(Shell *shell)
  562.     {
  563.         return L"Stop the current program";
  564.     }
  565. };
  566.  
  567. /* ------------------------------------------------------------------------- *
  568.  * QuitDebuggerCommand is used to quit the shell debugger
  569.  * ------------------------------------------------------------------------- */
  570.  
  571. class QuitDebuggerCommand : public DebuggerCommand
  572. {
  573. public:
  574.     QuitDebuggerCommand(const WCHAR *name, int minMatchLength = 0)
  575.         : DebuggerCommand(name, minMatchLength)
  576.     {
  577.     }
  578.  
  579.     void Do(DebuggerShell *shell, ICorDebug *cor, const WCHAR *args)
  580.     {
  581.         // Tell the shell that we are ready to quit
  582.         shell->m_quit = true;
  583.  
  584.         // Terminate the current debugee
  585.         shell->Kill();
  586.     }
  587.  
  588.     // Provide help specific to this command
  589.     void Help(Shell *shell)
  590.     {
  591.         ShellCommand::Help(shell);
  592.         shell->Write(L"\nStops the current program quits the debugger.");
  593.         shell->Write(L"\n");
  594.     }
  595.  
  596.     const WCHAR *ShortHelp(Shell *shell)
  597.     {
  598.         return L"Stop the current program and exit the debugger";
  599.     }
  600. };
  601.  
  602. /* ------------------------------------------------------------------------- *
  603.  * GoDebuggerCommand runs the debugee (it does not disable callbacks)
  604.  * ------------------------------------------------------------------------- */
  605.  
  606. class GoDebuggerCommand : public DebuggerCommand
  607. {
  608. public:
  609.     GoDebuggerCommand(const WCHAR *name, int minMatchLength = 0)
  610.         : DebuggerCommand(name, minMatchLength)
  611.     {
  612.         
  613.     }
  614.  
  615.     void Do(DebuggerShell *shell, ICorDebug *cor, const WCHAR *args)
  616.     {
  617.         // A counter to indicate how many times the command is executed
  618.         int count;
  619.  
  620.         // If no count is provided, assume a value of 1
  621.         if (!shell->GetIntArg(args, count))
  622.             count = 1;
  623.  
  624.         // Perform the command count times
  625.         while (count-- > 0)
  626.         {
  627.             // If a debugee does not exist, quit command
  628.             if (shell->m_currentProcess == NULL)
  629.             {
  630.                 shell->Error(L"Process not running.\n");
  631.                 break;
  632.             }
  633.             
  634.             // Otherwise, run the current debugee
  635.             else
  636.                 shell->Run();
  637.         }
  638.     }
  639.  
  640.     // Provide help specific to this command
  641.     void Help(Shell *shell)
  642.     {
  643.         ShellCommand::Help(shell);
  644.         shell->Write(L" [<count>]");
  645.         shell->Write(L"\nContinues the current program.  If an argument is ");
  646.         shell->Write(L"\nspecified, the command is performed multiple times.");
  647.         shell->Write(L"\n");
  648.     }
  649.  
  650.     const WCHAR *ShortHelp(Shell *shell)
  651.     {
  652.         return L"Continue the current program";
  653.     }
  654. };
  655.  
  656. /* ------------------------------------------------------------------------- *
  657.  * SetIpDebuggerCommand is used to change the current IP
  658.  * ------------------------------------------------------------------------- */
  659.  
  660. class SetIpDebuggerCommand : public DebuggerCommand
  661. {
  662. public:
  663.     SetIpDebuggerCommand(const WCHAR *name, int minMatchLength = 0)
  664.         : DebuggerCommand(name, minMatchLength)
  665.         {
  666.         }
  667.  
  668.     void Do(DebuggerShell *shell, ICorDebug *cor, const WCHAR *args)
  669.         {
  670.             int lineNumber;
  671.             long ILIP;
  672.             HRESULT hr;
  673.  
  674.             // If no current process, terminate
  675.             if (shell->m_currentProcess == NULL)
  676.             {
  677.                 shell->Error(L"No active process.\n");
  678.                 return;
  679.             }
  680.  
  681.             if (!shell->GetIntArg(args, lineNumber))
  682.             {
  683.                 shell->Write( L"Need the offset argument\n");
  684.                 return;
  685.             }
  686.  
  687.             ILIP = ValidateLineNumber( shell, lineNumber );
  688.  
  689.             if( ILIP == -1 )
  690.             {
  691.                 shell->Write( L"Invalid line number\n");
  692.                 return;
  693.             }
  694.             
  695.             SIZE_T offset = (SIZE_T)ILIP;
  696.                 
  697.             hr = shell->m_currentFrame->CanSetIP( offset);
  698.             if (hr != S_OK )
  699.                 hr = ConfirmSetIP(shell, hr);
  700.  
  701.             if (FAILED( hr ) )
  702.             {
  703.                 return;
  704.             }
  705.  
  706.             hr = shell->m_currentFrame->SetIP( offset);
  707.             
  708.             switch( hr )
  709.             {
  710.                 case S_OK:
  711.                     shell->Write( L"IP set successfully!\n");
  712.                     shell->SetDefaultFrame();
  713.                     break;
  714.  
  715.                 case CORDBG_S_BAD_START_SEQUENCE_POINT:
  716.                     shell->Write( L"Need to jump from a sequence point!\n");
  717.                     break;
  718.                     
  719.                 case CORDBG_S_BAD_END_SEQUENCE_POINT:
  720.                     shell->Write( L"Need to jump to another sequence point!\n");
  721.                     break;
  722.  
  723.                 case CORDBG_E_CANT_SET_IP_INTO_FINALLY:
  724.                     shell->Write( L"Not allowed to jump into a finally!\n");
  725.                     break;
  726.                     
  727.                 case CORDBG_E_CANT_SET_IP_INTO_CATCH:
  728.                     shell->Write( L"Not allowed to jump into a catch!\n");
  729.                     break;
  730.                     
  731.                 case E_FAIL:
  732.                     shell->Write( L"It's simply impossible to SetIP as requested!\n");
  733.                     break;
  734.                     
  735.                 default:
  736.                     if (FAILED( hr ) )
  737.                     {
  738.                         shell->Write( L"It failed, reason unknown!\n");
  739.                     }
  740.                     break;
  741.             }
  742.     }
  743.     
  744.     long GetILIPFromSourceLine( DebuggerShell *shell, 
  745.                                 DebuggerSourceFile *file,
  746.                                 long lineNumber,
  747.                                 DebuggerFunction *fnx)
  748.     {
  749.         DebuggerModule *m = file->m_module;
  750.  
  751.         if (m->GetSymbolReader() == NULL)
  752.             return -1;
  753.         
  754.         // GetMethodFromDocumentPosition to get an ISymUnmanagedMethod
  755.         // from this doc.
  756.         ISymUnmanagedMethod *pSymMethod;
  757.  
  758.         HRESULT hr = m->GetSymbolReader()->GetMethodFromDocumentPosition(
  759.                                                         file->GetDocument(),
  760.                                                         lineNumber,
  761.                                                         0,
  762.                                                         &pSymMethod);
  763.  
  764.         if (FAILED(hr))
  765.         {
  766.             g_pShell->ReportError(hr);
  767.             return -1;
  768.         }
  769.         
  770.         ULONG32 lineRangeCount = 0;
  771.  
  772.         // How many ranges?
  773.         hr = pSymMethod->GetRanges(file->GetDocument(),
  774.                                    lineNumber, 0,
  775.                                    0, &lineRangeCount,
  776.                                    NULL);
  777.  
  778.         if (FAILED(hr))
  779.         {
  780.             g_pShell->ReportError(hr);
  781.             return -1;
  782.         }
  783.  
  784.         long res = -1;
  785.         
  786.         // Make room for the ranges
  787.         if (lineRangeCount > 0)
  788.         {
  789.             ULONG32 *rangeArray;
  790.             
  791.             rangeArray = (ULONG32*)_alloca(sizeof(ULONG32) * lineRangeCount);
  792.             _ASSERTE(rangeArray != NULL);
  793.             
  794.             hr = pSymMethod->GetRanges(file->GetDocument(),
  795.                                        lineNumber, 0,
  796.                                        lineRangeCount,
  797.                                        &lineRangeCount,
  798.                                        rangeArray);
  799.  
  800.             if (FAILED(hr))
  801.             {
  802.                 g_pShell->ReportError(hr);
  803.                 return -1;
  804.             }
  805.         
  806.         
  807.             DebuggerFunction *f = m->ResolveFunction(pSymMethod, NULL);
  808.             if (fnx != f)
  809.                 return -1;
  810.  
  811.             res = rangeArray[0];
  812.         }
  813.  
  814.         return res; //failure
  815.     }
  816.  
  817.     
  818.     long ValidateLineNumber( DebuggerShell *shell, long lineNumber )
  819.     {
  820.         HRESULT hr;
  821.     
  822.         //
  823.         // First we jump through hoops (luckily, all cut-n-pasted) to
  824.         // get a DebuggerModule...
  825.         //
  826.         
  827.         // Get an ICorDebugCode pointer from the current frame
  828.         ICorDebugCode *icode;
  829.         hr = shell->m_currentFrame->GetCode(&icode);
  830.  
  831.         // Error check
  832.         if (FAILED(hr))
  833.         {
  834.             shell->ReportError(hr);
  835.             return -1;
  836.         }
  837.  
  838.         // Get an ICorDebugFunction pointer from the code pointer
  839.         ICorDebugFunction *ifunction;
  840.         icode->GetFunction(&ifunction);
  841.  
  842.         // Error check
  843.         if (FAILED(hr))
  844.         {
  845.             RELEASE(icode);
  846.             shell->ReportError(hr);
  847.             return -1;
  848.         }
  849.  
  850.         // Resolve the ICorDebugFunction pointer to a DebuggerFunction ptr
  851.         DebuggerFunction *function = DebuggerFunction::FromCorDebug(ifunction);   
  852.         
  853.         // Get the DebuggerSourceFile
  854.         unsigned currentLineNumber;
  855.         DebuggerSourceFile *sf;
  856.         hr = function->FindLineFromIP(0, &sf, ¤tLineNumber);
  857.         if (FAILED(hr))
  858.         {
  859.             g_pShell->ReportError(hr);
  860.             return -1;
  861.         }
  862.  
  863.         if (sf->FindClosestLine(lineNumber, false) == lineNumber)
  864.         {
  865.             return GetILIPFromSourceLine( shell, sf, lineNumber, function);
  866.         }
  867.  
  868.         return -1;
  869.      }       
  870.  
  871.     HRESULT ConfirmSetIP( DebuggerShell *shell, HRESULT hr )
  872.     {
  873.         
  874.     
  875.         switch( hr )
  876.         {
  877.             case CORDBG_E_CODE_NOT_AVAILABLE:
  878.                 shell->Write( L"Can't set ip because the code isn't available\n");
  879.                 hr = E_FAIL;
  880.                 break;
  881.                 
  882.             case CORDBG_E_CANT_SET_IP_INTO_FINALLY:
  883.                 shell->Write( L"Can't set ip because set into a finally not allowed\n");
  884.                 hr = E_FAIL;
  885.                 break;
  886.                 
  887.             case CORDBG_E_CANT_SET_IP_INTO_CATCH:
  888.                 shell->Write( L"Can't set ip because set into a catch not allowed\n");
  889.                 hr = E_FAIL;
  890.                 break;
  891.  
  892.             case CORDBG_S_BAD_START_SEQUENCE_POINT:
  893.                 shell->Write( L"SetIP can work, but is bad b/c you're not starting from a source line\n");
  894.                 hr = S_OK;
  895.                 break;
  896.             
  897.             case CORDBG_S_BAD_END_SEQUENCE_POINT:
  898.                 shell->Write( L"SetIP can work, but is bad b/c you're going to a nonsource line\n");
  899.                 hr = S_OK;
  900.                 break;
  901.  
  902.             case CORDBG_E_INSUFFICIENT_INFO_FOR_SET_IP:
  903.                 shell->Write( L"SetIP can work, but is bad b/c we don't have enough info to properly fix up the variables,etc\n");
  904.                 hr = E_FAIL;
  905.                 break;
  906.  
  907.             case E_FAIL:
  908.                 shell->Write( L"SetIP said: E_FAIL (miscellaneous, fatal, error)\n");
  909.                 hr = E_FAIL;
  910.                 break;
  911.  
  912.             case CORDBG_E_CANT_SET_IP_OUT_OF_FINALLY:
  913.                 shell->Write( L"Can't set ip to outside of a finally while unwinding\n");
  914.                 hr = E_FAIL;
  915.                 break;
  916.  
  917.             case CORDBG_E_SET_IP_NOT_ALLOWED_ON_NONLEAF_FRAME:
  918.                 shell->Write( L"Can't setip on a nonleaf frame!\n");
  919.                 hr = E_FAIL;
  920.                 break;
  921.  
  922.             case CORDBG_E_SET_IP_IMPOSSIBLE:
  923.                 shell->Write( L"SetIP said: I refuse: this is just plain impossible\n");
  924.                 hr = E_FAIL;
  925.                 break;
  926.  
  927.             default:
  928.                 shell->Write( L"SetIP returned 0x%x\n", hr);
  929.                 hr = E_FAIL;
  930.                 break;
  931.         }
  932.         
  933.         if (FAILED( hr ) )
  934.             return hr;
  935.  
  936.         shell->Write( L"Do you want to SetIp despite the risks inherent in this action (Y/N)?\n");
  937.         WCHAR sz[20];
  938.         shell->ReadLine( sz, 10);
  939.         if( _wcsicmp( sz, L"n")==0 )
  940.         {
  941.             return E_FAIL;
  942.         }
  943.         
  944.         return S_OK;
  945.     }
  946.  
  947.     // Provide help specific to this command
  948.     void Help(Shell *shell)
  949.     {
  950.         ShellCommand::Help(shell);
  951.         shell->Write(L" linenumber");
  952.         shell->Write(L"\nSet the next statement to be executed to linenumber\n");
  953.     }
  954.  
  955.     const WCHAR *ShortHelp(Shell *shell)
  956.     {
  957.         return L"Set the IP to a new line";
  958.     }
  959. };
  960.  
  961.  
  962.  
  963. /* ------------------------------------------------------------------------- *
  964.  * StepDebuggerCommand steps into a function call
  965.  * ------------------------------------------------------------------------- */
  966.  
  967. class StepDebuggerCommand : public DebuggerCommand
  968. {
  969. private:
  970.     bool m_in;
  971.  
  972. public:
  973.     StepDebuggerCommand(const WCHAR *name, bool in, int minMatchLength = 0)
  974.         : DebuggerCommand(name, minMatchLength), m_in(in)
  975.     {
  976.     }
  977.  
  978.  
  979.     // @mfunc void | StepDebuggerCommand | Do | There are three options
  980.     // for stepping: either we have no current frame (create a stepper off
  981.     // of the thread, call StepRanges with ranges==NULL), there is a current
  982.     // frame (create a stepper off the frame, call StepRanges w/ appropriate
  983.     // ranges), or there is a current frame,but it's inside a {prolog,epilog,
  984.     // etc} & we don't want to be - create a stepper
  985.     void Do(DebuggerShell *shell, ICorDebug *cor, const WCHAR *args)
  986.     {
  987.         HRESULT hr = S_OK;
  988.         ICorDebugStepper *pStepper;
  989.         bool fSkipStepRanges; //in case we're stepping over the prolog
  990.  
  991.         COR_DEBUG_STEP_RANGE *ranges = NULL;
  992.         SIZE_T rangeCount = 0;
  993.         
  994.         // A counter to indicate how many times the command is executed
  995.         int count;
  996.  
  997.         // If no count is provided, assume a value of 1
  998.         if (!shell->GetIntArg(args, count))
  999.             count = 1;
  1000.  
  1001.             // Perform the command count times
  1002.         while (count-- > 0)
  1003.         {
  1004.             fSkipStepRanges = false; 
  1005.             
  1006.             shell->m_showSource = true;
  1007.         
  1008.             // If no current process, terminate
  1009.             if (shell->m_currentProcess == NULL)
  1010.             {
  1011.                 shell->Error(L"Process not running.\n");
  1012.                 return;
  1013.             }
  1014.             
  1015.             // If no current thread, terminate
  1016.             if (shell->m_currentThread == NULL)
  1017.             {
  1018.                 shell->Error(L"Thread no longer exists.\n");
  1019.                 return;
  1020.             }
  1021.  
  1022.             if (shell->m_currentFrame != NULL)
  1023.             {
  1024.                 // Create a stepper based on the current frame
  1025.                 HRESULT hr=shell->m_currentFrame->CreateStepper(&pStepper);
  1026.                 if (FAILED(hr))
  1027.                 {
  1028.                     shell->ReportError(hr);
  1029.                     return;
  1030.                 }
  1031.  
  1032.                 ULONG32 ip;
  1033.                 CorDebugMappingResult mappingResult;
  1034.                 hr = shell->m_currentFrame->GetIP(&ip, &mappingResult);
  1035.  
  1036.                 // If we're in a prolog but don't want to be, step us to
  1037.                 // the next (non-PROLOG) line of IL.
  1038.                 // If we're in the prolog but want to be, then we should
  1039.                 // single-step through the prolog.  Note that ComputeStopMask
  1040.                 // will ensure that the (don't)skip flag is passed to the RC
  1041.                 if (mappingResult & ~(MAPPING_EXACT|MAPPING_APPROXIMATE) )
  1042.                 {
  1043.                     fSkipStepRanges = true;
  1044.                 }
  1045.                 else
  1046.                 {
  1047.                     // Error check
  1048.                     if (FAILED(hr))
  1049.                     {
  1050.                         shell->ReportError(hr);
  1051.                         return;
  1052.                     }
  1053.  
  1054.                     // Get an ICorDebugCode pointer from the current frame
  1055.                     ICorDebugCode *icode;
  1056.                     hr = shell->m_currentFrame->GetCode(&icode);
  1057.  
  1058.                     // Error check
  1059.                     if (FAILED(hr))
  1060.                     {
  1061.                         shell->ReportError(hr);
  1062.                         return;
  1063.                     }
  1064.  
  1065.                     // Get an ICorDebugFunction pointer from the code pointer
  1066.                     ICorDebugFunction *ifunction;
  1067.                     icode->GetFunction(&ifunction);
  1068.                 
  1069.                     // Error check
  1070.                     if (FAILED(hr))
  1071.                     {
  1072.                         RELEASE(icode);
  1073.                         shell->ReportError(hr);
  1074.                         return;
  1075.                     }
  1076.  
  1077.                     // Resolve the ICorDebugFunction pointer to a DebuggerFunction ptr
  1078.                     DebuggerFunction *function = 
  1079.                         DebuggerFunction::FromCorDebug(ifunction);
  1080.  
  1081.                     // Release iface pointers
  1082.                     RELEASE(icode);
  1083.                     RELEASE(ifunction);
  1084.  
  1085.                     // Get the ranges for the current IP
  1086.                     function->GetStepRangesFromIP(ip, &ranges, &rangeCount);
  1087.                                                     
  1088.                     if (rangeCount == 0)
  1089.                         shell->m_showSource = false;
  1090.                     else if (g_pShell->m_rgfActiveModes & DSM_ENHANCED_DIAGNOSTICS)
  1091.                     {
  1092.                         for (int i=0; i < rangeCount;i++)
  1093.                         {
  1094.                             shell->Write(L"Step range (IL): 0x%x to 0x%x\n", 
  1095.                                 ranges[i].startOffset,
  1096.                                 ranges[i].endOffset);
  1097.                         }
  1098.                     }
  1099.  
  1100.                 }
  1101.             }
  1102.  
  1103.             // Create a stepper based on the current thread
  1104.             else
  1105.             {
  1106.                 //note that this will fall into the step ranges case
  1107.                 HRESULT hr = shell->m_currentThread->CreateStepper(&pStepper);
  1108.                 if (FAILED(hr))
  1109.                 {
  1110.                     shell->ReportError( hr );
  1111.                     return;
  1112.                 }                
  1113.  
  1114.                 fSkipStepRanges = true;
  1115.             }
  1116.             
  1117.                         
  1118.             hr = pStepper->SetUnmappedStopMask( shell->
  1119.                                                 ComputeStopMask() );
  1120.             if (FAILED(hr))
  1121.             {
  1122.                 shell->ReportError( hr );
  1123.                 return;
  1124.             }
  1125.  
  1126.             hr = pStepper->SetInterceptMask( shell->
  1127.                                              ComputeInterceptMask());
  1128.             if (FAILED(hr))
  1129.             {
  1130.                 shell->ReportError( hr );
  1131.                 return;
  1132.             }
  1133.                     
  1134.             // Tell the shell about the new stepper
  1135.             shell->StepStart(shell->m_currentThread, pStepper);
  1136.  
  1137.             if (fSkipStepRanges)
  1138.             {
  1139.                 hr = pStepper->Step( m_in );
  1140.                 if (FAILED(hr))
  1141.                 {
  1142.                     shell->ReportError( hr );
  1143.                     return;
  1144.                 }
  1145.             }
  1146.             else
  1147.             {
  1148.                 // Tell the stepper to step on the provided ranges
  1149.                 HRESULT hr = pStepper->StepRange(m_in, ranges, rangeCount);
  1150.             
  1151.                 // Error check
  1152.                 if (FAILED(hr))
  1153.                 {
  1154.                     shell->ReportError(hr);
  1155.                     return;
  1156.                 }
  1157.  
  1158.                 // Clean up
  1159.                 delete [] ranges;
  1160.             }
  1161.             // Continue the process
  1162.             shell->Run();
  1163.         }
  1164.     }
  1165.  
  1166.     // Provide help specific to this command
  1167.     void Help(Shell *shell)
  1168.     {
  1169.         ShellCommand::Help(shell);
  1170.         shell->Write(L" [<count>]");
  1171.         shell->Write(L"\nSteps the current program to the next line, stepping");
  1172.  
  1173.         if (m_in)
  1174.             shell->Write(L"\ninto");
  1175.         else
  1176.             shell->Write(L"\nover");
  1177.  
  1178.         shell->Write(L" function calls. If an argument is specified,");
  1179.         shell->Write(L"\nmultiple steps are performed.");
  1180.         shell->Write(L"\n");
  1181.     }
  1182.  
  1183.     const WCHAR *ShortHelp(Shell *shell)
  1184.     {
  1185.         if (m_in)
  1186.             return L"Step into the next source line";
  1187.         else
  1188.             return L"Step over the next source line";
  1189.     }
  1190. };
  1191.  
  1192.  
  1193. class StepOutDebuggerCommand : public DebuggerCommand
  1194. {
  1195. public:
  1196.     StepOutDebuggerCommand(const WCHAR *name, int minMatchLength = 0)
  1197.         : DebuggerCommand(name, minMatchLength)
  1198.     {
  1199.     }
  1200.  
  1201.     void Do(DebuggerShell *shell, ICorDebug *cor, const WCHAR *args)
  1202.     {
  1203.         HRESULT hr;
  1204.  
  1205.         // A counter to indicate how many times the command is executed
  1206.         int count;
  1207.  
  1208.         // If no count is provided, assume a value of 1
  1209.         if (!shell->GetIntArg(args, count))
  1210.             count = 1;
  1211.  
  1212.         // Perform the command count times
  1213.         while (count-- > 0)
  1214.         {
  1215.             shell->m_showSource = true;
  1216.  
  1217.             // Error if no currently running process
  1218.             if (shell->m_currentProcess == NULL)
  1219.             {
  1220.                 shell->Error(L"Process not running.\n");
  1221.                 return;
  1222.             }
  1223.             
  1224.             // Error if no currently running thread
  1225.             if (shell->m_currentThread == NULL)
  1226.             {
  1227.                 shell->Error(L"Thread no longer exists.\n");
  1228.                 return;
  1229.             }
  1230.  
  1231.             ICorDebugStepper *pStepper;
  1232.  
  1233.             // Create a stepper based on the current frame
  1234.             if (shell->m_currentFrame != NULL)
  1235.                 hr = shell->m_currentFrame->CreateStepper(&pStepper);
  1236.  
  1237.             // Create a stepper based on the current thread
  1238.             else
  1239.                 hr = shell->m_currentThread->CreateStepper(&pStepper);
  1240.  
  1241.             // Error check
  1242.             if (FAILED(hr))
  1243.             {
  1244.                 shell->ReportError(hr);
  1245.                 return;
  1246.             }
  1247.             
  1248.             hr = pStepper->SetUnmappedStopMask( shell->ComputeStopMask() );
  1249.             if (FAILED(hr))
  1250.             {
  1251.                 shell->Write( L"Unable to set unmapped stop mask");
  1252.                 return;
  1253.             }                
  1254.  
  1255.             hr = pStepper->SetInterceptMask( shell->ComputeInterceptMask() );
  1256.             if (FAILED(hr))
  1257.             {
  1258.                 shell->ReportError( hr );
  1259.                 return;
  1260.             }
  1261.                 
  1262.             
  1263.             // Tell the stepper to step out
  1264.             hr = pStepper->StepOut();
  1265.  
  1266.             if (FAILED(hr))
  1267.             {
  1268.                 g_pShell->ReportError(hr);
  1269.                 return;
  1270.             }
  1271.  
  1272.             // Indicate the current stepper to the shell
  1273.             shell->StepStart(shell->m_currentThread, pStepper);
  1274.  
  1275.             // Continue the process
  1276.             shell->Run();
  1277.         }
  1278.     }
  1279.  
  1280.     // Provide help specific to this command
  1281.     void Help(Shell *shell)
  1282.     {
  1283.         ShellCommand::Help(shell);
  1284.         shell->Write(L" [<count>]");
  1285.         shell->Write(L"\nSteps the current program out of the current function.");
  1286.         shell->Write(L"\nIf an argument is specified, multiple steps are performed.");
  1287.         shell->Write(L"\n");
  1288.     }
  1289.  
  1290.     const WCHAR *ShortHelp(Shell *shell)
  1291.     {
  1292.         return L"Step out of the current function";
  1293.     }
  1294. };
  1295.  
  1296. class StepSingleDebuggerCommand : public DebuggerCommand
  1297. {
  1298. private:
  1299.     bool m_in;
  1300.  
  1301. public:
  1302.     StepSingleDebuggerCommand(const WCHAR *name, bool in, int minMatchLength = 0)
  1303.         : DebuggerCommand(name, minMatchLength), m_in(in)
  1304.     {
  1305.     }
  1306.  
  1307.     void Do(DebuggerShell *shell, ICorDebug *cor, const WCHAR *args)
  1308.     {
  1309.         HRESULT hr;
  1310.  
  1311.         // A counter to indicate how many times the command is executed
  1312.         int count;
  1313.  
  1314.         shell->m_showSource = false;
  1315.  
  1316.         // If no count is provided, assume a value of 1
  1317.         if (!shell->GetIntArg(args, count))
  1318.             count = 1;
  1319.  
  1320.         // Perform the command count times
  1321.         while (count-- > 0)
  1322.         {
  1323.             // Error if no currently running process
  1324.             if (shell->m_currentProcess == NULL)
  1325.             {
  1326.                 shell->Error(L"Process not running.\n");
  1327.                 return;
  1328.             }
  1329.             
  1330.             // Error if no currently running thread
  1331.             if (shell->m_currentThread == NULL && shell->m_currentUnmanagedThread == NULL)
  1332.             {
  1333.                 shell->Error(L"Thread no longer exists.\n");
  1334.                 return;
  1335.             }
  1336.  
  1337.             ICorDebugChain *ichain = NULL;
  1338.             BOOL managed = FALSE;
  1339.  
  1340.             if (shell->m_currentThread != NULL)
  1341.             {
  1342.                 HRESULT hr = shell->m_currentThread->GetActiveChain(&ichain);
  1343.                 if (FAILED(hr))
  1344.                 {
  1345.                     shell->ReportError(hr);
  1346.                     return;
  1347.                 }
  1348.  
  1349.                 hr = ichain->IsManaged(&managed);
  1350.                 if (FAILED(hr))
  1351.                 {
  1352.                     RELEASE(ichain);
  1353.                     shell->ReportError(hr);
  1354.                     return;
  1355.                 }
  1356.             }
  1357.  
  1358.             if (managed || shell->m_currentUnmanagedThread == NULL)
  1359.             {
  1360.                 if (ichain)
  1361.                     RELEASE(ichain);
  1362.                 
  1363.                 ICorDebugStepper *pStepper;
  1364.  
  1365.                 // Create a stepper based on the current frame
  1366.                 if (shell->m_currentFrame != NULL)
  1367.                     hr = shell->m_currentFrame->CreateStepper(&pStepper);
  1368.                 else
  1369.                     hr = shell->m_currentThread->CreateStepper(&pStepper);
  1370.                                 
  1371.                 // Error check
  1372.                 if (FAILED(hr))
  1373.                 {
  1374.                     shell->ReportError(hr);
  1375.                     return;
  1376.                 }
  1377.  
  1378.                 hr = pStepper->SetUnmappedStopMask( shell->ComputeStopMask() );
  1379.                 if (FAILED(hr))
  1380.                 {
  1381.                     shell->Write( L"Unable to set unmapped stop mask");
  1382.                     return;
  1383.                 }                
  1384.                 hr = pStepper->SetInterceptMask(shell->ComputeInterceptMask());
  1385.                 if (FAILED(hr))
  1386.                 {
  1387.                     shell->ReportError( hr );
  1388.                     return;
  1389.                 }
  1390.                 
  1391.                 
  1392.                 // Tell the stepper what to do
  1393.                 hr = pStepper->Step(m_in);
  1394.             
  1395.                 // Error check
  1396.                 if (FAILED(hr))
  1397.                 {
  1398.                     shell->ReportError(hr);
  1399.                     return;
  1400.                 }
  1401.  
  1402.                 // Indicate the current stepper to the shell
  1403.                 shell->StepStart(shell->m_currentThread, pStepper);
  1404.  
  1405.                 // Continue the process
  1406.                 shell->Run();
  1407.             }
  1408.             else
  1409.             {
  1410.                 if (ichain == NULL)
  1411.                     shell->m_currentUnmanagedThread->m_unmanagedStackEnd = 0;
  1412.                 else
  1413.                 {
  1414.                     CORDB_ADDRESS start, end;
  1415.                     hr = ichain->GetStackRange(&start, &end);
  1416.  
  1417.                     RELEASE(ichain);
  1418.                     
  1419.                     if (FAILED(hr))
  1420.                     {
  1421.                         shell->ReportError(hr);
  1422.                         return;
  1423.                     }
  1424.  
  1425.                     shell->m_currentUnmanagedThread->m_unmanagedStackEnd = end;
  1426.                 }
  1427.  
  1428.                 ICorDebugRegisterSet *regSet = NULL;
  1429.                 if (shell->m_currentThread != NULL)
  1430.                 {
  1431.                     hr = shell->m_currentThread->GetRegisterSet(®Set);
  1432.                     if (FAILED(hr))
  1433.                     {
  1434.                         shell->ReportError(hr);
  1435.                         return;
  1436.                     }
  1437.                 }
  1438.  
  1439.                 CONTEXT context;
  1440.                 context.ContextFlags = CONTEXT_FULL;
  1441.                 if (regSet != NULL)
  1442.                     hr = regSet->GetThreadContext(sizeof(context), (BYTE*)&context);
  1443.                 else
  1444.                     hr = shell->m_currentProcess->GetThreadContext(
  1445.                                                    shell->m_currentUnmanagedThread->GetId(),
  1446.                                                    sizeof(context), (BYTE*)&context);
  1447.                 if (FAILED(hr))
  1448.                 {
  1449.                     shell->ReportError(hr);
  1450.                     return;
  1451.                 }
  1452.  
  1453. #ifdef _X86_
  1454.                 context.EFlags |= 0x100;
  1455. #else // !_X86_
  1456.                 _ASSERTE(!"@TODO Alpha - StepSingleDebuggerCommand::Do (Commands.cpp)");
  1457. #endif // _X86_
  1458.  
  1459.                 if (regSet != NULL)
  1460.                 {
  1461.                     hr = regSet->SetThreadContext(sizeof(context), (BYTE*)&context);
  1462.                     RELEASE(regSet);
  1463.                 }
  1464.                 else
  1465.                     hr = shell->m_currentProcess->SetThreadContext(
  1466.                                                    shell->m_currentUnmanagedThread->GetId(),
  1467.                                                    sizeof(context), (BYTE*)&context);
  1468.  
  1469.                 if (FAILED(hr))
  1470.                 {
  1471.                     shell->ReportError(hr);
  1472.                     return;
  1473.                 }
  1474.  
  1475.                 shell->m_currentUnmanagedThread->m_stepping = TRUE;
  1476.  
  1477.                 // Continue the process
  1478.                 shell->Run();
  1479.             }
  1480.         }
  1481.     }
  1482.  
  1483.     // Provide help specific to this command
  1484.     void Help(Shell *shell)
  1485.     {
  1486.         ShellCommand::Help(shell);
  1487.         shell->Write(L" [<count>]");
  1488.         shell->Write(L"\nSteps the current program a single instruction, stepping");
  1489.         if (m_in)
  1490.             shell->Write(L"\ninto");
  1491.         else
  1492.             shell->Write(L"\nover");
  1493.  
  1494.         shell->Write(L" function calls.");
  1495.  
  1496.         shell->Write(L"\nIf an argument is specified, multiple steps are performed.");
  1497.         shell->Write(L"\n");
  1498.     }
  1499.  
  1500.     const WCHAR *ShortHelp(Shell *shell)
  1501.     {
  1502.         if (m_in)
  1503.             return L"Step into the next native instruction";
  1504.         else
  1505.             return L"Step over the next native instruction";
  1506.     }
  1507. };
  1508.  
  1509. class BreakpointDebuggerCommand : public DebuggerCommand
  1510. {
  1511. public:
  1512.     BreakpointDebuggerCommand(const WCHAR *name, int minMatchLength = 0)
  1513.         : DebuggerCommand(name, minMatchLength)
  1514.     {
  1515.         
  1516.     }
  1517.  
  1518.  
  1519.     // Name is class::method
  1520.     BOOL    BindClassFunc ( WCHAR *name, 
  1521.                             const WCHAR *end, 
  1522.                             SIZE_T index, 
  1523.                             DWORD thread, 
  1524.                             DebuggerBreakpoint *breakpoint)
  1525.     {
  1526.         BOOL bAtleastOne = false;
  1527.         BOOL bFound = false;
  1528.         bool bUnused = false;
  1529.         HASHFIND find;
  1530.  
  1531.         // check if the user has specified a module name
  1532.         WCHAR *szModuleEnd = wcschr(name, L'!');
  1533.         WCHAR szModName [MAX_PATH] = L"";
  1534.         bool bModNameSpecified = false;
  1535.         char rcFile1[MAX_PATH];
  1536.  
  1537.         if (szModuleEnd != NULL)
  1538.         {
  1539.             if (szModuleEnd > name)
  1540.             {
  1541.                 int iCount = szModuleEnd - name;
  1542.  
  1543.                 wcsncpy (szModName, name, iCount);
  1544.                 szModName [iCount] = L'\0';
  1545.  
  1546.                 bModNameSpecified = true;
  1547.  
  1548.                 // separate out the module file name 
  1549.                 MAKE_ANSIPTR_FROMWIDE(name1A, szModName);
  1550.                 _splitpath(name1A, NULL, NULL, rcFile1, NULL);
  1551.                 char *pTemp = rcFile1;
  1552.                 while (*pTemp != '\0')
  1553.                 {    
  1554.                     *pTemp = tolower (*pTemp);
  1555.                     pTemp++;
  1556.                 }
  1557.             }
  1558.  
  1559.             name = szModuleEnd+1;
  1560.         }
  1561.  
  1562.         // For each module, check if that class::method exists
  1563.         // and if it does, set a breakpoint on it
  1564.         for (DebuggerModule *m = (DebuggerModule *) 
  1565.              g_pShell->m_modules.FindFirst(&find);
  1566.             m != NULL;
  1567.             m = (DebuggerModule *) g_pShell->m_modules.FindNext(&find))
  1568.         {
  1569.             if (bModNameSpecified)
  1570.             {
  1571.                 // the user has specified the module name.
  1572.  
  1573.                 WCHAR *pszModName = m->GetName();
  1574.                 if (pszModName == NULL)
  1575.                     pszModName = L"<UnknownName>";
  1576.  
  1577.                 char        rcFile[MAX_PATH];
  1578.  
  1579.                 MAKE_ANSIPTR_FROMWIDE(nameA, pszModName);
  1580.                 _splitpath(nameA, NULL, NULL, rcFile, NULL);
  1581.                 // convert the name to lowercase
  1582.                 char *pTemp = rcFile;
  1583.                 while (*pTemp != '\0')
  1584.                 {    
  1585.                     *pTemp = tolower (*pTemp);
  1586.                     pTemp++;
  1587.                 }
  1588.                 
  1589.                 if (strcmp (rcFile, rcFile1))
  1590.                     continue;
  1591.             }
  1592.  
  1593.             // Create a new breakpoint based on the provided information
  1594.             if (bFound)
  1595.             {
  1596.                 breakpoint = new DebuggerBreakpoint(name,
  1597.                                         end - name, 
  1598.                                         index, thread);
  1599.                 bUnused = true;
  1600.             }
  1601.  
  1602.             if (breakpoint != NULL)
  1603.             {
  1604.                 if ((bFound = breakpoint->Bind(m, NULL))
  1605.                         == true)
  1606.                 {
  1607.                     // Indicate that atleast one breakpoint was set
  1608.                     bAtleastOne = true;
  1609.                     bUnused = false;
  1610.  
  1611.                     g_pShell->OnBindBreakpoint(breakpoint, m);
  1612.                     breakpoint->Activate();
  1613.                     g_pShell->PrintBreakpoint(breakpoint);
  1614.                 }
  1615.             }
  1616.             else
  1617.             {
  1618.                 // out of memory
  1619.                 g_pShell->ReportError(E_OUTOFMEMORY);
  1620.                 break;
  1621.             }
  1622.         }
  1623.  
  1624.         if (bUnused == true)
  1625.             delete  breakpoint;
  1626.  
  1627.         return bAtleastOne;
  1628.     }
  1629.  
  1630.  
  1631.     // Name is filename:lineNumber
  1632.     BOOL    BindFilename (  WCHAR *name, 
  1633.                             const WCHAR *end, 
  1634.                             SIZE_T index, 
  1635.                             DWORD thread, 
  1636.                             DebuggerBreakpoint *breakpoint)
  1637.     {
  1638.         BOOL bAtleastOne = false;
  1639.         BOOL bFound = false;
  1640.         bool bUnused = false;
  1641.         HASHFIND find;
  1642.         HRESULT    hr;
  1643.  
  1644.         // Convert the filename to lowercase letters
  1645.         WCHAR *pstrName = breakpoint->GetName();
  1646.         WCHAR *pstrTemp = pstrName;
  1647.  
  1648.         while (*pstrTemp)
  1649.         {
  1650.             *pstrTemp = towlower (*pstrTemp);
  1651.             pstrTemp++;
  1652.         }
  1653.  
  1654.         // First, try to match the string name as it is
  1655.         for (DebuggerModule *m = (DebuggerModule *) 
  1656.              g_pShell->m_modules.FindFirst(&find);
  1657.             m != NULL;
  1658.             m = (DebuggerModule *) g_pShell->m_modules.FindNext(&find))
  1659.         {
  1660.             ISymUnmanagedDocument *doc = NULL;
  1661.  
  1662.             // Create a new breakpoint based 
  1663.             // on the provided information
  1664.             if (bFound)
  1665.             {
  1666.                 breakpoint = new DebuggerBreakpoint(name,
  1667.                                                     end - name, 
  1668.                                                     index, thread);
  1669.                 bUnused = true;
  1670.             }
  1671.  
  1672.             if (breakpoint != NULL)
  1673.             {
  1674.                 hr = m->MatchFullFileNameInModule (pstrName, &doc);
  1675.  
  1676.                 bFound = false;
  1677.  
  1678.                 if (SUCCEEDED (hr))
  1679.                 {
  1680.                     if (doc != NULL)
  1681.                     {
  1682.                         // this means we found a source file 
  1683.                         // name in this module which exactly
  1684.                         // matches the user specified filename. 
  1685.                         // So set a breakpoint on this.
  1686.                         if (breakpoint->Bind(m, doc) == true)
  1687.                         {
  1688.                             // Indicate that atleast one breakpoint was set
  1689.                             bAtleastOne = true;
  1690.                             bFound = true;
  1691.                             bUnused = false;
  1692.  
  1693.                             g_pShell->OnBindBreakpoint(breakpoint, m);
  1694.                             breakpoint->Activate();
  1695.                             g_pShell->PrintBreakpoint(breakpoint);
  1696.                         }
  1697.                     }
  1698.                     else
  1699.                         continue;                
  1700.                 }
  1701.             }
  1702.         }
  1703.  
  1704.         if (bAtleastOne == false)
  1705.         {
  1706.             // no file matching the user specified file was found.
  1707.             // Perform another search, this time using only the
  1708.             // stripped file name (minus the path) and see if that
  1709.             // has a match in some module
  1710.  
  1711.             // The way we'll proceed is:
  1712.             // 1. Find all matches for all modules.
  1713.             // 2. If there is only one match, set a breakpoint on it.
  1714.             // 3. Else more than one match found
  1715.             //    Ask the user to resolve the between the matched filenames and then 
  1716.             //    set breakpoints on the one he wants to.
  1717.  
  1718.             WCHAR    *rgpstrFileName [MAX_MODULES][MAX_FILE_MATCHES_PER_MODULE];
  1719.             ISymUnmanagedDocument *rgpDocs [MAX_MODULES][MAX_FILE_MATCHES_PER_MODULE];
  1720.             DebuggerModule *rgpDebugModule [MAX_MODULES];
  1721.             int iCount [MAX_MODULES]; // keeps track of number of filesnames in the module 
  1722.                                       // which matched the stripped filenanme
  1723.             int iCumulCount = 0;
  1724.             int iModIndex = 0;
  1725.  
  1726.             for (DebuggerModule *m = (DebuggerModule *) 
  1727.                  g_pShell->m_modules.FindFirst(&find);
  1728.                 m != NULL;
  1729.                 m = (DebuggerModule *) g_pShell->m_modules.FindNext(&find))
  1730.             {
  1731.                 rgpDebugModule [iModIndex] = NULL;
  1732.  
  1733.                 hr = m->MatchStrippedFNameInModule (pstrName,
  1734.                                                 rgpstrFileName [iModIndex],
  1735.                                                 rgpDocs [iModIndex],
  1736.                                                 &iCount [iModIndex]
  1737.                                                 );
  1738.  
  1739.                 if (SUCCEEDED (hr) && iCount [iModIndex])
  1740.                 {
  1741.                     iCumulCount += iCount [iModIndex];
  1742.                     rgpDebugModule [iModIndex] = m;
  1743.                 }
  1744.  
  1745.                 ++iModIndex;
  1746.                 _ASSERTE (iModIndex < MAX_MODULES);
  1747.             }
  1748.  
  1749.             // Was a match found?
  1750.             if (iCumulCount)
  1751.             {
  1752.                 int iInd;
  1753.  
  1754.                 // if more than one match was found, then first filter 
  1755.                 // out the duplicates. Duplicates may be present due to
  1756.                 // multiple appdomains - if the same module is loaded in 
  1757.                 // "n" appdomains, then there will be "n" modules as far
  1758.                 // as cordbg is concerned.
  1759.                 if (iCumulCount > 1)
  1760.                 {
  1761.                     WCHAR **rgFName = new WCHAR *[iCumulCount];
  1762.                     int iTempNameIndex = 0;
  1763.  
  1764.                     if (rgFName != NULL)
  1765.                     {
  1766.                         for (iInd = 0; iInd < iModIndex; iInd++)
  1767.                         {
  1768.                             if (rgpDebugModule [iInd] != NULL)
  1769.                             {
  1770.                                 int iTempCount = 0;
  1771.                                 while (iTempCount < iCount [iInd])
  1772.                                 {
  1773.                                     int j=0;
  1774.                                     boolean fMatchFound = false;
  1775.                                     while (j<iTempNameIndex)
  1776.                                     {
  1777.                                         if (!wcscmp(rgFName[j], 
  1778.                                                     rgpstrFileName [iInd][iTempCount]))
  1779.                                         {
  1780.                                             // this is a duplicate, so need to 
  1781.                                             // remove it from the list...
  1782.                                             for (int i=iTempCount; 
  1783.                                                 i < (iCount [iInd]-1); 
  1784.                                                 i++)
  1785.                                             {
  1786.                                                 rgpstrFileName [iInd][i] = 
  1787.                                                         rgpstrFileName [iInd][i+1];
  1788.                 
  1789.                                             }
  1790.                                             rgpstrFileName [iInd][i] = NULL;
  1791.                                             iCount [iInd]--;
  1792.                                             
  1793.                                             fMatchFound = true;
  1794.  
  1795.                                             break;
  1796.  
  1797.                                         }
  1798.                                         j++;
  1799.                                     }
  1800.                                     // if no match was found, then add this filename
  1801.                                     // to the list of unique filenames
  1802.                                     if (!fMatchFound)
  1803.                                     {    
  1804.                                         rgFName [iTempNameIndex++] =
  1805.                                                 rgpstrFileName [iInd][iTempCount]; 
  1806.                                     }
  1807.  
  1808.                                     iTempCount++;
  1809.                                 }
  1810.                             }
  1811.                         }
  1812.  
  1813.                         delete [] rgFName;
  1814.                         iCumulCount = iTempNameIndex;
  1815.                     }
  1816.                 }
  1817.  
  1818.                 // if there was only one match found,
  1819.                 // then set a breakpoint on it
  1820.                 if (iCumulCount == 1)
  1821.                 {
  1822.                     for (iInd = 0; iInd<iModIndex; iInd++)
  1823.                         if (rgpDebugModule [iInd] != NULL)
  1824.                             break;
  1825.  
  1826.                     _ASSERT (iInd < iModIndex);
  1827.                     
  1828.                     if (breakpoint->Bind (rgpDebugModule [iInd],
  1829.                                     rgpDocs [iInd][0])    == true)
  1830.                     {
  1831.                         // Indicate that atleast one breakpoint
  1832.                         // was set
  1833.                         bAtleastOne = true;
  1834.                         bUnused = false;
  1835.  
  1836.                         // also update the breakpoint name from the 
  1837.                         // one that the user input to the one which 
  1838.                         // is stored in the module's meta data
  1839.                         breakpoint->UpdateName (rgpstrFileName [iInd][0]);
  1840.  
  1841.                         g_pShell->OnBindBreakpoint(breakpoint, m);
  1842.                         breakpoint->Activate();
  1843.                         g_pShell->PrintBreakpoint(breakpoint);
  1844.  
  1845.                     }
  1846.                 }
  1847.                 else
  1848.                 {
  1849.                     // there were multiple matches. So get the user input
  1850.                     // on which ones he wants to set. 
  1851.                     // NOTE: User selection is 1-based, i.e., user enters "1"
  1852.                     // if he wants a breakpoint to be put on the first option shown
  1853.                     // to him.
  1854.                     int iUserSel = g_pShell->GetUserSelection (
  1855.                                                 rgpDebugModule,
  1856.                                                 rgpstrFileName,
  1857.                                                 iCount,
  1858.                                                 iModIndex,
  1859.                                                 iCumulCount
  1860.                                                 );
  1861.  
  1862.                     if (iUserSel == (iCumulCount+1))
  1863.                     {
  1864.                         // this means that the user wants a 
  1865.                         // breakpoint on all matched locations
  1866.                         for (iInd = 0; iInd < iModIndex; iInd++)
  1867.                         {
  1868.                             if (rgpDebugModule [iInd] != NULL)
  1869.                             {
  1870.                                 for (int iTempCount = 0;
  1871.                                     iTempCount < iCount [iInd];
  1872.                                     iTempCount++)
  1873.                                 {
  1874.                                     // Create a new breakpoint
  1875.                                     // based on the provided 
  1876.                                     // information
  1877.                                     if (bFound)
  1878.                                     {
  1879.                                         breakpoint = new DebuggerBreakpoint (
  1880.                                                             name, end - name, 
  1881.                                                                 index, thread);
  1882.  
  1883.                                         bUnused = true;
  1884.                                     }
  1885.  
  1886.                                     if (breakpoint != NULL)
  1887.                                     {
  1888.  
  1889.                                         if ((bFound = breakpoint->Bind(
  1890.                                                 rgpDebugModule [iInd],
  1891.                                                 rgpDocs[iInd][iTempCount])
  1892.                                                 ) == true)
  1893.                                         {
  1894.                                             // Indicate that atleast one
  1895.                                             // breakpoint was set
  1896.                                             bAtleastOne = true;
  1897.                                             if (bUnused == true)
  1898.                                                 bUnused = false;
  1899.                                             breakpoint->UpdateName (
  1900.                                                     rgpstrFileName [iInd][iTempCount]);
  1901.  
  1902.                                             g_pShell->OnBindBreakpoint(breakpoint, m);
  1903.                                             breakpoint->Activate();
  1904.                                             g_pShell->PrintBreakpoint(breakpoint);
  1905.  
  1906.                                         }
  1907.                                     }
  1908.                                 }
  1909.                             }
  1910.                         }
  1911.  
  1912.                     }
  1913.                     else
  1914.                     {
  1915.                         int iTempCumulCount = 0;
  1916.  
  1917.                         // locate the module which contains 
  1918.                         // the user specified breakpoint option
  1919.                         for (iInd = 0; iInd < iModIndex; iInd++)
  1920.                         {
  1921.                             if (rgpDebugModule [iInd] != NULL)
  1922.                             {
  1923.                                 if ((iTempCumulCount + iCount [iInd])
  1924.                                         >= iUserSel)
  1925.                                 {
  1926.                                     // found the module. Now 
  1927.                                     // calculate the file index
  1928.                                     // within this module.
  1929.                                     // Reuse iTempCumulCount
  1930.                                     iTempCumulCount = 
  1931.                                         iUserSel - iTempCumulCount - 1; // "-1" since it is 1 based
  1932.  
  1933.                                     if (breakpoint->Bind(
  1934.                                             rgpDebugModule [iInd],
  1935.                                             rgpDocs [iInd][iTempCumulCount]
  1936.                                             )
  1937.                                             == true)
  1938.                                     {
  1939.                                         // Indicate that atleast
  1940.                                         // one breakpoint was set
  1941.                                         bAtleastOne = true;
  1942.                                         if (bUnused == true)
  1943.                                             bUnused = false;
  1944.  
  1945.                                         breakpoint->UpdateName (
  1946.                                                 rgpstrFileName [iInd][iTempCumulCount]);
  1947.  
  1948.                                         g_pShell->OnBindBreakpoint(breakpoint, m);
  1949.                                         breakpoint->Activate();
  1950.                                         g_pShell->PrintBreakpoint(breakpoint);
  1951.                                     }
  1952.  
  1953.                                     break;
  1954.                                 }
  1955.  
  1956.                                 iTempCumulCount += iCount [iInd];
  1957.                             }
  1958.  
  1959.                         }
  1960.  
  1961.                         _ASSERT (iInd < iModIndex);
  1962.                     }
  1963.  
  1964.                 }
  1965.             }
  1966.                 
  1967.         }
  1968.  
  1969.         if (bUnused)
  1970.             delete breakpoint;
  1971.  
  1972.         return bAtleastOne;
  1973.     }
  1974.  
  1975.  
  1976.  
  1977.  
  1978.     // Helper function to parse the arguments, the format being
  1979.     // [[<file>:]<line no>] [[<class>::]<function>[:offset]]
  1980.     //      [if <expression>] [thread <id>]
  1981.     // and the modifiers are 'if' and 'thread'
  1982.     bool GetModifiers(DebuggerShell *shell, 
  1983.                       const WCHAR *&args, DWORD &thread, WCHAR *&expression)
  1984.     {
  1985.         thread = NULL_THREAD_ID;
  1986.         expression = NULL;
  1987.  
  1988.         const WCHAR *word;
  1989.  
  1990.         while (shell->GetStringArg(args, word) == 0)
  1991.         {
  1992.             if (wcsncmp(word, L"if", 1) == 0)
  1993.             {
  1994.                 if (!shell->GetStringArg(args, expression))
  1995.                     return (false);
  1996.             }
  1997.             else if (wcsncmp(word, L"thread", 6) == 0)
  1998.             {
  1999.                 int ithread;
  2000.                 if (!shell->GetIntArg(args, ithread))
  2001.                     return (false);
  2002.                 thread = ithread;
  2003.             }
  2004.             else
  2005.                 break;
  2006.         }
  2007.         return (true);
  2008.     }
  2009.  
  2010.  
  2011.     void Do(DebuggerShell *shell, ICorDebug *cor, const WCHAR *args)
  2012.     {
  2013.         DWORD thread;
  2014.         WCHAR *expression;
  2015.         DebuggerBreakpoint *breakpoint = NULL;
  2016.         BOOL bAtleastOne = false;
  2017.  
  2018.         // Display all current breakpoints
  2019.         if (*args == 0)
  2020.         {
  2021.             // Iterate through all used IDs, and print out the info
  2022.             // for each one that maps to an breakpoint.
  2023.             for (DWORD i = 0; i <= shell->m_lastBreakpointID; i++)
  2024.             {
  2025.                 breakpoint = shell->FindBreakpoint(i);
  2026.                 if (breakpoint != NULL)
  2027.                     shell->PrintBreakpoint(breakpoint);
  2028.             }
  2029.  
  2030.             return;
  2031.         }
  2032.  
  2033.         // If a number is provided, break at that line number in the current
  2034.         // source file
  2035.         else if (iswdigit(*args))
  2036.         {
  2037.             // The line to create the breakpoint for.
  2038.             int lineNumber;
  2039.  
  2040.             // Check that there is an active frame
  2041.             if (shell->m_currentFrame == NULL)
  2042.             {
  2043.                 shell->Error(L"No current source file to set breakpoint in.\n");
  2044.                 return;
  2045.             }
  2046.             
  2047.             // Get the line number and any modifiers
  2048.             if (shell->GetIntArg(args, lineNumber)
  2049.                 && GetModifiers(shell, args, thread, expression))
  2050.             {
  2051.                 // Lookup the current source file. Assumes that if command is
  2052.                 // just given a line number the current source file is implied.
  2053.  
  2054.                 HRESULT hr;
  2055.                 ICorDebugCode *icode;
  2056.                 ICorDebugFunction *ifunction;
  2057.                 ULONG32 ip;
  2058.  
  2059.                 // Get the code from the current frame, and then get the function
  2060.                 hr = shell->m_currentFrame->GetCode(&icode);
  2061.                 
  2062.                 // Error check
  2063.                 if (FAILED(hr))
  2064.                 {
  2065.                     shell->ReportError(hr);
  2066.                     return;
  2067.                 }
  2068.                 
  2069.                 hr = icode->GetFunction(&ifunction);
  2070.                 
  2071.                 // Error check
  2072.                 if (FAILED(hr))
  2073.                 {
  2074.                     RELEASE(icode);
  2075.                     shell->ReportError(hr);
  2076.                     return;
  2077.                 }
  2078.  
  2079.                 DebuggerFunction *function 
  2080.                     = DebuggerFunction::FromCorDebug(ifunction);
  2081.                 _ASSERTE(function);
  2082.  
  2083.                 // Release the interfaces
  2084.                 RELEASE(icode);
  2085.                 RELEASE(ifunction);
  2086.  
  2087.                 // Get the IP of the current frame
  2088.                 CorDebugMappingResult mappingResult;
  2089.                 shell->m_currentFrame->GetIP(&ip, &mappingResult);
  2090.  
  2091.                 // Now get the source file and current line number
  2092.                 DebuggerSourceFile *sf;
  2093.                 unsigned int currentLineNumber;
  2094.  
  2095.                 // Find the line number corresponding to the IP
  2096.                 hr = function->FindLineFromIP(ip, &sf, ¤tLineNumber);
  2097.  
  2098.                 if (FAILED(hr))
  2099.                 {
  2100.                     g_pShell->ReportError(hr);
  2101.                     return;
  2102.                 }
  2103.  
  2104.                 // If there is no associated source file, or we explicitly don't
  2105.                 // want to display source
  2106.                 if (sf == NULL || !shell->m_showSource)
  2107.                 {
  2108.                     _ASSERTE(function->m_name != NULL);
  2109.  
  2110.                     // Make sure the line provided is valid
  2111.                     if (function->ValidateInstruction(function->m_nativeCode != NULL, 
  2112.                                                       lineNumber))
  2113.                     {
  2114.                         breakpoint = new DebuggerBreakpoint(function,
  2115.                             lineNumber, thread);
  2116.  
  2117.                         //Out of memory
  2118.                         if (breakpoint == NULL)
  2119.                         {
  2120.                             shell->ReportError(E_OUTOFMEMORY);
  2121.                             return;
  2122.                         }
  2123.                     }
  2124.                     else
  2125.                         shell->Error(L"%d is not a valid instruction"
  2126.                                      L" offset in %s\n", 
  2127.                                      lineNumber, function->m_name);
  2128.                 }
  2129.  
  2130.                 // Set the breakpoint by line number within the current
  2131.                 // functions source file.
  2132.                 else
  2133.                 {
  2134.                     // Find the closest valid source line number
  2135.                     unsigned int newLineNumber = 
  2136.                         sf->FindClosestLine(lineNumber, true);
  2137.  
  2138.                     _ASSERTE(newLineNumber != 0);
  2139.  
  2140.                     // If the line number was invalid, print out the new line
  2141.                     if (newLineNumber != lineNumber)
  2142.                     {
  2143.                         shell->Error(L"No code at line %d, setting "
  2144.                                      L" breakpoint at line %d.\n", 
  2145.                                      lineNumber, newLineNumber);
  2146.                     }
  2147.  
  2148.                     // Create a breakpoint
  2149.                     breakpoint = new DebuggerBreakpoint(sf, newLineNumber, 
  2150.                                                         thread);
  2151.  
  2152.                     if (breakpoint == NULL)
  2153.                     {
  2154.                         shell->ReportError(E_OUTOFMEMORY);
  2155.                         return;
  2156.                     }
  2157.                 }
  2158.             }
  2159.         }
  2160.  
  2161.         // A fully-described breakpoint is provided, by file:linenumber or 
  2162.         // classname::function:offset
  2163.         else
  2164.         {
  2165.             WCHAR *name;
  2166.  
  2167.             // Get either the file name or the class/function name
  2168.             if (shell->GetStringArg(args, name)
  2169.                 && GetModifiers(shell, args, thread, expression))
  2170.             {
  2171.                 int index = 0;
  2172.                 const WCHAR *end = args;
  2173.  
  2174.                 for (WCHAR *p = name; p < end-1; p++)
  2175.                 {
  2176.                     if (p[0] == L':' && iswdigit(p[1]))
  2177.                     {
  2178.                         end = p;
  2179.                         p++;
  2180.                         shell->GetIntArg(p, index);
  2181.                         break;
  2182.                     }
  2183.                 }
  2184.  
  2185.                 // Create a new breakpoint based on the provided information
  2186.                 if ((breakpoint = new DebuggerBreakpoint(name, end - name, 
  2187.                                 index, thread)) != NULL)
  2188.                 {
  2189.  
  2190.                 // Determine if it's a class::method or filename:linenumber
  2191.  
  2192.                     WCHAR *classEnd = wcschr(breakpoint->GetName(), L':');
  2193.                     if (classEnd != NULL && classEnd[1] == L':')
  2194.                     {
  2195.                         bAtleastOne = BindClassFunc (name, end, index, thread, breakpoint);
  2196.                     }
  2197.                     else
  2198.                     {
  2199.                         bAtleastOne = BindFilename (name, end, index, thread, breakpoint);
  2200.                     }
  2201.  
  2202.                     if (!bAtleastOne)
  2203.                     {
  2204.                         // this means that the user specified string didn't match any Class::method
  2205.                         // or any filename in any of the loaded modules. So do the following:
  2206.                         if (breakpoint->BindUnmanaged(g_pShell->m_currentProcess))
  2207.                             g_pShell->OnBindBreakpoint(breakpoint, NULL);
  2208.                     }
  2209.                 }
  2210.                 else
  2211.                 {
  2212.                     // out of memory!!
  2213.                     g_pShell->ReportError(E_OUTOFMEMORY);
  2214.                 }
  2215.             }
  2216.  
  2217.         }
  2218.  
  2219.         if (breakpoint == NULL)
  2220.             Help(shell);
  2221.         else
  2222.         {
  2223.             if (!bAtleastOne)
  2224.             {
  2225.                 breakpoint->Activate();
  2226.                 shell->PrintBreakpoint(breakpoint);
  2227.             }
  2228.         }
  2229.     }
  2230.  
  2231.  
  2232.     // Provide help specific to this command
  2233.     void Help(Shell *shell)
  2234.     {
  2235.         ShellCommand::Help(shell);
  2236.         shell->Write(L" [[<file>:]<line no>] [[[<class>]::]<function>[:offset]] ");
  2237.         shell->Write(L"\nSets a breakpoint at the given location. With ");
  2238.         shell->Write(L"\nno arguments, prints the current breakpoints.");
  2239.         shell->Write(L"\n");
  2240.     }
  2241.  
  2242.     const WCHAR *ShortHelp(Shell *shell)
  2243.     {
  2244.         return L"Set a breakpoint, or show all breakpoints";
  2245.     }
  2246. };
  2247.  
  2248. class RemoveBreakpointDebuggerCommand : public DebuggerCommand
  2249. {
  2250. public:
  2251.     RemoveBreakpointDebuggerCommand(const WCHAR *name, int minMatchLength = 0)
  2252.         : DebuggerCommand(name, minMatchLength)
  2253.     {
  2254.     }
  2255.  
  2256.     void Do(DebuggerShell *shell, ICorDebug *cor, const WCHAR *args)
  2257.     {
  2258.         // If no argument, remove all breakpoints
  2259.         if (*args == NULL)
  2260.         {
  2261.             shell->Write(L"Removing all breakpoints.\n");
  2262.             shell->RemoveAllBreakpoints();
  2263.         }
  2264.         else
  2265.         {
  2266.             while (*args != NULL)
  2267.             {
  2268.                 int id;
  2269.  
  2270.                 // Get the breakpoint ID to remove
  2271.                 if (shell->GetIntArg(args, id))
  2272.                 {
  2273.                     // Find the breakpoint by ID
  2274.                     DebuggerBreakpoint *breakpoint = shell->FindBreakpoint(id);
  2275.  
  2276.                     // Indicate that the ID provided was invalid
  2277.                     if (breakpoint == NULL)
  2278.                         shell->Error(L"Invalid breakpoint %d.\n", id);
  2279.  
  2280.                     // Otherwise, deactivate the breakpoint and delete it
  2281.                     else
  2282.                     {
  2283.                         breakpoint->Deactivate();
  2284.                         delete breakpoint;
  2285.                     }
  2286.                 }
  2287.  
  2288.                 // If the user provided something other than a number
  2289.                 else
  2290.                 {
  2291.                     Help(shell);
  2292.                     break;
  2293.                 }
  2294.             }
  2295.         }
  2296.     }
  2297.  
  2298.     // Provide help specific to this command
  2299.     void Help(Shell *shell)
  2300.     {
  2301.         ShellCommand::Help(shell);
  2302.         shell->Write(L" [<breakpoint number> ...]");
  2303.         shell->Write(L"\nDeletes the specified breakpoint. With no arguments,");
  2304.         shell->Write(L"\ndeletes all breakpoints");
  2305.         shell->Write(L"\n");
  2306.     }
  2307.  
  2308.     const WCHAR *ShortHelp(Shell *shell)
  2309.     {
  2310.         return L"Delete one or more breakpoints";
  2311.     }
  2312. };
  2313.  
  2314. class ThreadsDebuggerCommand : public DebuggerCommand
  2315. {
  2316.  
  2317. public:
  2318.     ThreadsDebuggerCommand(const WCHAR *name, int minMatchLength = 0)
  2319.         : DebuggerCommand(name, minMatchLength)
  2320.     {
  2321.     }
  2322.  
  2323.     void Do(DebuggerShell *shell, ICorDebug *cor, const WCHAR *args)
  2324.     {
  2325.         // If there is no process, there must be no threads!
  2326.         if (shell->m_currentProcess == NULL)
  2327.         {
  2328.             shell->Write(L"No current process.\n");
  2329.             return;
  2330.         }
  2331.  
  2332.         // Display the active threads
  2333.         if (*args == 0)
  2334.         {
  2335.             HRESULT hr;
  2336.             ICorDebugThreadEnum *e;
  2337.             ICorDebugThread *ithread = NULL;
  2338.  
  2339.             // Enumerate the process' threads
  2340.             hr = shell->m_currentProcess->EnumerateThreads(&e);
  2341.  
  2342.             if (FAILED(hr))
  2343.             {
  2344.                 shell->ReportError(hr);
  2345.                 return;
  2346.             }
  2347.  
  2348.             ULONG count;  // indicates how many records were retrieved
  2349.  
  2350.         // Print out information for each thread
  2351.             for (hr = e->Next(1, &ithread, &count);
  2352.                  count == 1;
  2353.                  hr = e->Next(1, &ithread, &count))
  2354.             {
  2355.                 // If the call to Next fails...
  2356.                 if (FAILED(hr))
  2357.                 {
  2358.                     shell->ReportError(hr);
  2359.                     RELEASE(e);
  2360.                     return;
  2361.                 }
  2362.  
  2363.                 // Indicate the current thread
  2364.                 if (ithread == shell->m_currentThread)
  2365.                     shell->Write(L"*");
  2366.                 else
  2367.                     shell->Write(L" ");
  2368.  
  2369.             // Print thread info
  2370.                 shell->PrintThreadState(ithread);
  2371.  
  2372.                 // And release the iface pointer
  2373.                 RELEASE(ithread);
  2374.             }
  2375.  
  2376.             if (FAILED(hr))
  2377.             {
  2378.                 shell->ReportError(hr);
  2379.                 return;
  2380.             }
  2381.  
  2382.             // Release the enumerator
  2383.             RELEASE(e);
  2384.         }
  2385.  
  2386.         // Otherwise, switch current thread
  2387.         else
  2388.         {
  2389.             HRESULT hr;
  2390.             int tid;
  2391.  
  2392.             if (shell->GetIntArg(args, tid))
  2393.             {
  2394.                 ICorDebugThread *thread;
  2395.  
  2396.                 // Get the thread by ID
  2397.                 hr = shell->m_currentProcess->GetThread(tid, &thread);
  2398.  
  2399.                 // No such thread
  2400.                 if (FAILED(hr))
  2401.                     shell->Write(L"No such thread.\n");
  2402.  
  2403.                 // Thread found, display info
  2404.                 else
  2405.                 {
  2406.                     shell->SetCurrentThread(shell->m_currentProcess, thread);
  2407.                     shell->SetDefaultFrame();
  2408.                     shell->PrintThreadState(thread);
  2409.                     thread->Release();
  2410.                 }
  2411.             }
  2412.             else
  2413.                 shell->Write(L"Invalid thread id.\n");
  2414.         }
  2415.     }
  2416.  
  2417.     // Provide help specific to this command
  2418.     void Help(Shell *shell)
  2419.     {
  2420.         ShellCommand::Help(shell);
  2421.         shell->Write(L" [<thread id>]");
  2422.         shell->Write(L"\nSets the current thread.    If no argument, prints the");
  2423.         shell->Write(L"\nlist of threads.");
  2424.         shell->Write(L"\n");
  2425.     }
  2426.  
  2427.     const WCHAR *ShortHelp(Shell *shell)
  2428.     {
  2429.         return L"Select a thread, or show all threads";
  2430.     }
  2431. };
  2432.  
  2433. class WhereDebuggerCommand : public DebuggerCommand
  2434. {
  2435. public:
  2436.     WhereDebuggerCommand(const WCHAR *name, int minMatchLength = 0)
  2437.         : DebuggerCommand(name, minMatchLength)
  2438.     {
  2439.     }
  2440.  
  2441.     void Do(DebuggerShell *shell, ICorDebug *cor, const WCHAR *args)
  2442.     {
  2443.         HRESULT hr = S_OK;
  2444.         ULONG count;
  2445.         int iNumFramesToShow;
  2446.  
  2447.         if (!shell->GetIntArg(args, iNumFramesToShow))
  2448.             iNumFramesToShow = 1000;
  2449.         else
  2450.         {
  2451.             if (iNumFramesToShow < 0)
  2452.                 iNumFramesToShow = 1000;
  2453.         }
  2454.             // Get a pointer to the current thread
  2455.             ICorDebugThread *ithread = shell->m_currentThread;
  2456.  
  2457.             // If there is no current thread, cannot perform command
  2458.             if (ithread == NULL)
  2459.             {
  2460.                 shell->Write(L"Thread no longer exists.\n");
  2461.                 return;
  2462.             }
  2463.  
  2464.             // Get the thread ID
  2465.             DWORD id;
  2466.             hr = ithread->GetID(&id);
  2467.  
  2468.             if (FAILED(hr))
  2469.             {
  2470.                 shell->ReportError(hr);
  2471.                 return;
  2472.             }
  2473.  
  2474.             CorDebugUserState us;
  2475.             hr = ithread->GetUserState(&us);
  2476.  
  2477.             if (FAILED(hr))
  2478.             {
  2479.                 shell->ReportError(hr);
  2480.                 return;
  2481.             }
  2482.  
  2483.             // Output thread ID, state
  2484.             shell->Write(L"Thread 0x%x Current State:%s\n", id,
  2485.                 shell->UserThreadStateToString(us));
  2486.  
  2487.             int i = 0;
  2488.  
  2489.             // Enumerate the chains
  2490.             int frameIndex = 0;
  2491.     
  2492.             ICorDebugChainEnum  *ce;
  2493.             ICorDebugChain      *ichain;
  2494.             hr = ithread->EnumerateChains(&ce);
  2495.  
  2496.             if (FAILED(hr))
  2497.             {
  2498.                 shell->ReportError(hr);
  2499.                 return;
  2500.             }
  2501.  
  2502.             // Get the first chain in the enumeration
  2503.             hr = ce->Next(1, &ichain, &count);
  2504.         
  2505.             if (FAILED(hr))
  2506.             {
  2507.                 shell->ReportError(hr);
  2508.                 RELEASE(ce);
  2509.                 return;
  2510.             }
  2511.  
  2512.             while ((count == 1) && (iNumFramesToShow > 0))
  2513.             {
  2514.                 shell->PrintChain(ichain, &frameIndex, &iNumFramesToShow);
  2515.                 RELEASE(ichain);
  2516.  
  2517.                 hr = ce->Next(1, &ichain, &count);
  2518.  
  2519.                 if (FAILED(hr))
  2520.                 {
  2521.                     shell->ReportError(hr);
  2522.                     RELEASE(ce);
  2523.                     return;
  2524.                 }
  2525.             }
  2526.  
  2527.             // Done with the chain enumerator
  2528.             RELEASE(ce);
  2529.  
  2530.             shell->Write(L"\n");
  2531.     }
  2532.  
  2533.     // Provide help specific to this command
  2534.     void Help(Shell *shell)
  2535.     {
  2536.         ShellCommand::Help(shell);
  2537.         shell->Write(L"\nPrints the stack trace for the current thread.  If an");
  2538.         shell->Write(L"\nargument is given, only that many stack frames are shown ");
  2539.         shell->Write(L"\n");
  2540.     }
  2541.  
  2542.     const WCHAR *ShortHelp(Shell *shell)
  2543.     {
  2544.         return L"Show a stack trace";
  2545.     }
  2546. };
  2547.  
  2548. class ShowDebuggerCommand : public DebuggerCommand
  2549. {
  2550. private:
  2551.     // Keep track of the last argument
  2552.     int lastCount;
  2553.  
  2554. public:
  2555.     ShowDebuggerCommand(const WCHAR *name, int minMatchLength = 0)
  2556.         : DebuggerCommand(name, minMatchLength), lastCount(5)
  2557.     {
  2558.     }
  2559.  
  2560.     void Do(DebuggerShell *shell, ICorDebug *cor, const WCHAR *args)
  2561.     {
  2562.         int count;
  2563.  
  2564.         // If no argument, use last count
  2565.         if (!shell->GetIntArg(args, count))
  2566.             count = lastCount;
  2567.         else
  2568.             lastCount = count;
  2569.  
  2570.         // Print the current source line, and count line above and below
  2571.         BOOL ret = shell->PrintCurrentSourceLine(count);
  2572.  
  2573.         // Report if unsuccessful
  2574.         if (!ret)
  2575.             shell->Write(L"No source code information available.\n");
  2576.  
  2577.         shell->m_showSource = true;
  2578.     }
  2579.  
  2580.     // Provide help specific to this command
  2581.     void Help(Shell *shell)
  2582.     {
  2583.         ShellCommand::Help(shell);
  2584.         shell->Write(L" [<line count>]");
  2585.         shell->Write(L"\nPrints the source code at the current IP.");
  2586.         shell->Write(L"\nIf an argument is given,  that many extra lines will ");
  2587.         shell->Write(L"\nbe shown before and after the current line.");
  2588.         shell->Write(L"\nThe default count is 5, and the last count you used will");
  2589.         shell->Write(L"\nbecome the default for the current session.");
  2590.         shell->Write(L"\n");
  2591.     }
  2592.  
  2593.     const WCHAR *ShortHelp(Shell *shell)
  2594.     {
  2595.         return L"Show source lines";
  2596.     }
  2597. }; 
  2598.  
  2599.  
  2600. class PathDebuggerCommand : public DebuggerCommand
  2601. {
  2602. public:
  2603.     PathDebuggerCommand(const WCHAR *name, int minMatchLength = 0)
  2604.         : DebuggerCommand(name, minMatchLength)
  2605.     {
  2606.         
  2607.     }
  2608.  
  2609.     void Do(DebuggerShell *shell, ICorDebug *cor, const WCHAR *args)
  2610.     {
  2611.         WCHAR* newPath = NULL;
  2612.         HKEY key;
  2613.  
  2614.         shell->GetStringArg(args, newPath);
  2615.  
  2616.         int iLength = wcslen (newPath);
  2617.  
  2618.         if (iLength != 0)
  2619.         {
  2620.             // If there is no program executing, then set the 
  2621.             // global path in the registry
  2622.             if (shell->m_lastRunArgs == NULL)
  2623.             {
  2624.                 // Set the new path, and save it in the registry
  2625.                 if (shell->OpenDebuggerRegistry(&key))
  2626.                 {
  2627.                     if (shell->WriteSourcesPath(key, newPath))
  2628.                     {
  2629.                         // Delete the previous path
  2630.                         delete [] shell->m_currentSourcesPath;
  2631.  
  2632.                         // Attempt to read what was just written
  2633.                         if (!(shell->ReadSourcesPath(key,
  2634.                                                      &(shell->m_currentSourcesPath))))
  2635.                         {
  2636.                             shell->Error(L"Path not set!\n");
  2637.                             shell->m_currentSourcesPath = NULL;
  2638.                             return;
  2639.                         }
  2640.                     }
  2641.                     else
  2642.                         shell->Error(L"Path not set!\n");
  2643.  
  2644.                     // Close the registry key
  2645.                     shell->CloseDebuggerRegistry(key);
  2646.                 }
  2647.             }
  2648.  
  2649.             shell->UpdateCurrentPath (newPath);
  2650.         }
  2651.  
  2652.         // Display new path
  2653.         if (shell->m_currentSourcesPath)
  2654.             shell->Write(L"Path: %s\n", shell->m_currentSourcesPath);
  2655.         else
  2656.             shell->Write(L"Path: none\n");
  2657.     }
  2658.     // Provide help specific to this command
  2659.     void Help(Shell *shell)
  2660.     {
  2661.         ShellCommand::Help(shell);
  2662.         shell->Write(L" [<new path>]");
  2663.         shell->Write(L"\nPrints the current path used to search for source files.");
  2664.         shell->Write(L"\nIf an argument is given, that becomes the new path used");
  2665.         shell->Write(L"\nto search for source files.");
  2666.         shell->Write(L"\nThis path is kept between sessions in the registry.");
  2667.         shell->Write(L"\n");
  2668.     }
  2669.  
  2670.     const WCHAR *ShortHelp(Shell *shell)
  2671.     {
  2672.         return L"Set the source file search path, or show the current one";
  2673.     }
  2674. };
  2675.  
  2676. class RefreshSourceDebuggerCommand : public DebuggerCommand
  2677. {
  2678. public:
  2679.     RefreshSourceDebuggerCommand(const WCHAR *name, int minMatchLength = 0)
  2680.         : DebuggerCommand(name, minMatchLength)
  2681.     {
  2682.     }
  2683.  
  2684.     void Do(DebuggerShell *shell, ICorDebug *cor, const WCHAR *args)
  2685.     {
  2686.         // Get the file name to refresh
  2687.         WCHAR* fileName = NULL;
  2688.         shell->GetStringArg(args, fileName);
  2689.  
  2690.         // If a file name was provided
  2691.         if (wcslen(fileName) != 0)
  2692.         {
  2693.             // Look for the source file
  2694.             DebuggerSourceFile* sf = shell->LookupSourceFile(fileName);
  2695.  
  2696.             // If the source file is found, reload the text
  2697.             if (sf != NULL)
  2698.             {
  2699.                 // Reload the text and print the current source line
  2700.                 if (sf->ReloadText(shell->m_currentSourcesPath, false))
  2701.                     shell->PrintCurrentSourceLine(0);
  2702.  
  2703.                 // Else if the file no longer exists, say so
  2704.                 else
  2705.                     shell->Error(L"No source code information "
  2706.                                  L"available for file %s.\n", fileName);
  2707.             }
  2708.  
  2709.             // Indicate that the file is not found
  2710.             else
  2711.                 shell->Error(L"File %s is not currently part of this program.\n",
  2712.                              fileName);
  2713.         }
  2714.         else
  2715.             Help(shell);
  2716.     }
  2717.  
  2718.     // Provide help specific to this command
  2719.     void Help(Shell *shell)
  2720.     {
  2721.         ShellCommand::Help(shell);
  2722.         shell->Write(L" [<source file>]");
  2723.         shell->Write(L"\nRefreshes the source code for a given source file.");
  2724.         shell->Write(L"\nThe source file must be part of the currently executing");
  2725.         shell->Write(L"\nprogram to be refreshed.");
  2726.         shell->Write(L"\nUse this command after setting a source file path with");
  2727.         shell->Write(L"\nthe 'path' command to bring in missing source code.");
  2728.         shell->Write(L"\n");
  2729.     }
  2730.  
  2731.     const WCHAR *ShortHelp(Shell *shell)
  2732.     {
  2733.         return L"Reload a source file";
  2734.     }
  2735. };
  2736.  
  2737. class PrintDebuggerCommand : public DebuggerCommand
  2738. {
  2739. public:
  2740.     PrintDebuggerCommand(const WCHAR *name, int minMatchLength = 0)
  2741.         : DebuggerCommand(name, minMatchLength)
  2742.     {
  2743.         
  2744.     }
  2745.  
  2746.     
  2747.     void Do(DebuggerShell *shell, ICorDebug *cor, const WCHAR *args)
  2748.     {
  2749.         WCHAR wsz[40];
  2750.         ICorDebugThread *thread = shell->m_currentThread;
  2751.  
  2752.         ICorDebugILFrame *f = NULL;
  2753.         ICorDebugCode *icode = NULL;
  2754.         ICorDebugFunction *ifunction = NULL;
  2755.         ICorDebugValueEnum *pArgs = NULL;
  2756.         ICorDebugValueEnum *pLocals = NULL;
  2757.         
  2758.         if (thread == NULL)
  2759.         {
  2760.             shell->Write(L"Thread no longer exists.\n");
  2761.             return;
  2762.         }
  2763.  
  2764.         // Get the name of the variable to print.
  2765.         WCHAR* exp = NULL;
  2766.         shell->GetStringArg(args, exp);
  2767.  
  2768.         // If a name is provided, 
  2769.         if (args - exp > 0)
  2770.         {
  2771.             // Make a copy of the variable name to print
  2772.             WCHAR *expAlloc = (WCHAR *)_alloca((args - exp + 1) * sizeof (WCHAR));
  2773.             wcsncpy(expAlloc, exp, args - exp);
  2774.             expAlloc[args - exp] = L'\0';
  2775.  
  2776.             // Get the value for the name provided
  2777.             ICorDebugValue *ivalue;
  2778.             ivalue = shell->EvaluateExpression(expAlloc, shell->m_currentFrame, true);
  2779.  
  2780.             // If the name provided is valid, print it!
  2781.             if (ivalue != NULL)
  2782.                 shell->PrintVariable(expAlloc, ivalue, 0, TRUE);
  2783.             else
  2784.             {
  2785.                 bool fFound = shell->EvaluateAndPrintGlobals(expAlloc);
  2786.  
  2787.                 if (!fFound)
  2788.                     shell->Write( L"Variable unavailable, or not valid\n" );
  2789.             }
  2790.         }
  2791.         else
  2792.         {
  2793.             // Load up the info we need to search for locals.
  2794.  
  2795.             HRESULT hr;
  2796.  
  2797.             // Get the current frame
  2798.             f = shell->m_currentFrame;
  2799.  
  2800.             if (f == NULL)
  2801.             {
  2802.                 if (shell->m_rawCurrentFrame == NULL)
  2803.                     shell->Error(L"No current managed IL frame.\n");
  2804.                 else
  2805.                     shell->Error(L"The information needed to display "
  2806.                                  L"variables is not available.\n");
  2807.                 goto LExit;
  2808.             }
  2809.             
  2810.             // Get the code for the current frame
  2811.             hr = f->GetCode(&icode);
  2812.  
  2813.             if (FAILED(hr))
  2814.             {
  2815.                 shell->ReportError(hr);
  2816.                 goto LExit;
  2817.             }
  2818.  
  2819.             // Then get the function
  2820.             hr = icode->GetFunction(&ifunction);
  2821.  
  2822.             if (FAILED(hr))
  2823.             {
  2824.                 shell->ReportError(hr);
  2825.                 goto LExit;
  2826.             }
  2827.  
  2828.             // Now get the IP for the start of the function
  2829.             ULONG32 ip;
  2830.             CorDebugMappingResult mappingResult;
  2831.             hr = f->GetIP(&ip, &mappingResult);
  2832.  
  2833.             if (FAILED(hr))
  2834.             {
  2835.                 shell->ReportError(hr);
  2836.                 goto LExit;
  2837.             }
  2838.  
  2839.             // Get the DebuggerFunction for the function iface
  2840.             DebuggerFunction *function;
  2841.             function = DebuggerFunction::FromCorDebug(ifunction);
  2842.             _ASSERTE(function);
  2843.  
  2844.             // Clean up
  2845.             RELEASE(icode);
  2846.             icode = NULL;
  2847.             RELEASE(ifunction);
  2848.             ifunction = NULL;
  2849.  
  2850.             hr = f->EnumerateArguments( &pArgs );
  2851.             if ( !SUCCEEDED( hr ) )
  2852.             {
  2853.                 shell->Write( L"Unable to obtain method argument iterator!\n" );
  2854.                 goto LExit;
  2855.             }
  2856.             
  2857.             unsigned int i;
  2858.             ULONG argCount;
  2859.             hr = pArgs->GetCount(&argCount);
  2860.             if( !SUCCEEDED( hr ) )
  2861.             {
  2862.                 shell->Write(L"Unable to obtain a count of arguments\n");
  2863.                 goto LExit;
  2864.             }
  2865.  
  2866. #ifdef _DEBUG
  2867.             bool fVarArgs;
  2868.             PCCOR_SIGNATURE sig;
  2869.             ULONG callConv;
  2870.  
  2871.             fVarArgs = false;
  2872.             sig = function->GetSignature();
  2873.             callConv = CorSigUncompressCallingConv(sig);
  2874.  
  2875.             if ( (callConv & IMAGE_CEE_CS_CALLCONV_MASK)&
  2876.                  IMAGE_CEE_CS_CALLCONV_VARARG)
  2877.                 fVarArgs = true;
  2878. #endif //_DEBUG
  2879.  
  2880.              ULONG cTemp;
  2881.              cTemp = function->GetArgumentCount();
  2882.  
  2883.              // Var Args functions have call-site-specific numbers of
  2884.              // arguments
  2885.             _ASSERTE( argCount == cTemp || fVarArgs);
  2886.  
  2887.             // Print out each argument first
  2888.             LPWSTR nameWsz;
  2889.             for (i = 0; i < argCount; i++)
  2890.             {
  2891.                 DebuggerVarInfo* arg = function->GetArgumentAt(i);
  2892.  
  2893.                 //@TODO: Remove when DbgMeta becomes Unicode
  2894.                 if (arg != NULL)
  2895.                 {
  2896.                     MAKE_WIDEPTR_FROMUTF8(nameW, arg->name);
  2897.                     nameWsz = nameW;
  2898.                 }
  2899.                 else
  2900.                 {
  2901.                     wsprintf( wsz, L"Arg%d", i );
  2902.                     nameWsz = wsz;
  2903.                 }
  2904.  
  2905.                 // Get the field value
  2906.                 ICorDebugValue *ival;
  2907.                 ULONG celtFetched = 0;
  2908.                 hr = pArgs->Next(1, &ival,&celtFetched);
  2909.  
  2910.                 // If successful, print the variable
  2911.                 if (SUCCEEDED(hr) && celtFetched==1)
  2912.                 {
  2913.                     shell->PrintVariable(nameWsz, ival, 0, FALSE);
  2914.                 }
  2915.  
  2916.                 // Otherwise, indicate that it is unavailable
  2917.                 else
  2918.                     shell->Write(L"%s = <unavailable>", nameWsz);
  2919.  
  2920.                 shell->Write(L"\n");
  2921.             }
  2922.  
  2923.             pArgs->Release();
  2924.             pArgs = NULL;
  2925.             
  2926.             // Get the active local vars
  2927.             DebuggerVariable *localVars;
  2928.             unsigned int localVarCount;
  2929.  
  2930.             localVarCount = 0;
  2931.             localVars = NULL;
  2932.  
  2933.             if( function->GetActiveLocalVars(ip, &localVars, &localVarCount) )
  2934.             {
  2935.                 // Print all the locals in the current scope.
  2936.                 for (i = 0; i < localVarCount; i++)
  2937.                 {
  2938.                     // Get the argument info
  2939.                     DebuggerVariable *local = &(localVars[i]);
  2940.  
  2941.                     // Get the field value
  2942.                     ICorDebugValue* ival;
  2943.                     hr = f->GetLocalVariable(local->m_varNumber, &ival);
  2944.  
  2945.                     // If successful, print the variable
  2946.                     if (SUCCEEDED(hr) )
  2947.                         shell->PrintVariable(local->m_name, ival, 0, FALSE);
  2948.                 
  2949.                     // Otherwise, indicate that it is unavailable
  2950.                     else
  2951.                         shell->Write(L"%s = <unavailable>", local->m_name);
  2952.  
  2953.                     shell->Write(L"\n");
  2954.                 }
  2955.             
  2956.                 // Cleanup
  2957.                 delete [] localVars;
  2958.  
  2959.                 // Indicate if no vars available.
  2960.                 if ((function->IsStatic()) && (localVarCount == 0) &&
  2961.                     (function->GetArgumentCount() == 0))
  2962.                     shell->Write(L"No local variables in scope.\n");
  2963.             }
  2964.             else
  2965.             {
  2966.                 // No vars in scope, so dump all
  2967.                 // local variables, regardless of validity,etc.
  2968.                 hr = f->EnumerateLocalVariables( &pLocals );
  2969.                 if ( !SUCCEEDED( hr ) )
  2970.                 {
  2971.                     shell->Write( L"Unable to enumerate local variables!\n" );
  2972.                     goto LExit;
  2973.                 }
  2974.  
  2975.                 _ASSERTE( pLocals != NULL );
  2976.  
  2977.                 ULONG cAllLocalVars = 0;
  2978.                 hr =pLocals->GetCount( &cAllLocalVars );
  2979.                 if ( !SUCCEEDED( hr ) )
  2980.                 {
  2981.                     shell->Write( L"Unable to obtain count of local variables!\n");
  2982.                     goto LExit;
  2983.                 }
  2984.                 
  2985.                 ICorDebugValue* ival = NULL;
  2986.                 ULONG celtFetched = 0;
  2987.                 for ( int i = 0; i < cAllLocalVars; i++)
  2988.                 {
  2989.                     _ASSERTE( pLocals != NULL );
  2990.                     hr = pLocals->Next( 1, &ival, &celtFetched );
  2991.                     if ( FAILED( hr ) )
  2992.                     {
  2993.                         shell->Write( L"Var %d: Unavailable\n", i );
  2994.                     }
  2995.                     else
  2996.                     {
  2997.                         wsprintf( wsz, L"Var%d: ", i );
  2998.                         shell->PrintVariable( wsz, ival, 0, FALSE);
  2999.                         shell->Write( L"\n" );
  3000.                         //PrintVariable will Release ival for us
  3001.                     }
  3002.                 }
  3003.                 pLocals->Release();
  3004.                 pLocals = NULL;
  3005.             }
  3006.  
  3007.             // Print any current func eval result.
  3008.             ICorDebugValue *pResult;
  3009.             pResult = shell->EvaluateExpression(L"$result",
  3010.                                                 shell->m_currentFrame,
  3011.                                                 true);
  3012.  
  3013.             if (pResult != NULL)
  3014.             {
  3015.                 shell->PrintVariable(L"$result", pResult, 0, FALSE);
  3016.                 shell->Write( L"\n" );
  3017.             }
  3018.  
  3019.             // Print the current thread object
  3020.             pResult = shell->EvaluateExpression(L"$thread",
  3021.                                                 shell->m_currentFrame,
  3022.                                                 true);
  3023.  
  3024.             if (pResult != NULL)
  3025.             {
  3026.                 shell->PrintVariable(L"$thread", pResult, 0, FALSE);
  3027.                 shell->Write( L"\n" );
  3028.             }
  3029.  
  3030. LExit:
  3031.             // Print any current exception for this thread.
  3032.             pResult = shell->EvaluateExpression(L"$exception",
  3033.                                                 shell->m_currentFrame,
  3034.                                                 true);
  3035.  
  3036.             if (pResult != NULL)
  3037.             {
  3038.                 shell->PrintVariable(L"$exception", pResult, 0, FALSE);
  3039.                 shell->Write( L"\n" );
  3040.             }
  3041.         }
  3042.         
  3043.         shell->Write(L"\n");
  3044.  
  3045.         if (icode != NULL )
  3046.             RELEASE( icode );
  3047.  
  3048.         if (ifunction != NULL )
  3049.             RELEASE( ifunction );
  3050.  
  3051.         if (pArgs != NULL )
  3052.             RELEASE( pArgs );
  3053.         
  3054.         if( pLocals != NULL )
  3055.             RELEASE(pLocals);
  3056.     }
  3057.  
  3058.     // Provide help specific to this command
  3059.     void Help(Shell *shell)
  3060.     {
  3061.         ShellCommand::Help(shell);
  3062.         shell->Write(L" [<variable name>]");
  3063.         shell->Write(L"\nPrints the variable specified in the argument.");
  3064.         shell->Write(L"\nIf no argument is given, all local variables currently");
  3065.         shell->Write(L"\nactive within the current stack frame are printed.");
  3066.         shell->Write(L"\nIf the variable name refers to a global variable, but");
  3067.         shell->Write(L"\nnot a local variable, the global variable will be ");
  3068.         shell->Write(L"\nprinted.");
  3069.         shell->Write(L"\n");
  3070.         shell->Write(L"\nYou may specify variables within objects using dot");
  3071.         shell->Write(L"\nnotation, i.e., 'p obj.var1' or 'p obj1.obj2.var1'.");
  3072.         shell->Write(L"\n");
  3073.         shell->Write(L"\nYou may specify class static variables by prepending");
  3074.         shell->Write(L"\nthe class name to the variable as shown below:");
  3075.         shell->Write(L"\n'p MyClass::StaticVar1'");
  3076.         shell->Write(L"\n'p System::Boolean::True'");
  3077.         shell->Write(L"\n");
  3078.         shell->Write(L"\nNOTE: Indices to elements in arrays must be simple");
  3079.         shell->Write(L"\nexpressions. Thus, 'p arr[1]', 'p arr[i]' and");
  3080.         shell->Write(L"\n'p arr[arr2[1]]' are all valid arguments, but ");
  3081.         shell->Write(L"\n'p arr[i+1]' and 'p arr[1+2]' are both invalid arguments.");
  3082.         shell->Write(L"\n");
  3083.     }
  3084.  
  3085.     const WCHAR *ShortHelp(Shell *shell)
  3086.     {
  3087.         return L"Print variables (locals, args, statics, etc.)";
  3088.     }
  3089. };
  3090.  
  3091. class UpDebuggerCommand : public DebuggerCommand
  3092. {
  3093. public:
  3094.     UpDebuggerCommand(const WCHAR *name, int minMatchLength = 0)
  3095.         : DebuggerCommand(name, minMatchLength)
  3096.     {
  3097.         
  3098.     }
  3099.  
  3100.     void Do(DebuggerShell *shell, ICorDebug *cor, const WCHAR *args)
  3101.     {
  3102.         int count;
  3103.  
  3104.         if (!shell->GetIntArg(args, count))
  3105.             count = 1;
  3106.         else
  3107.         {
  3108.             if (count < 0)
  3109.                 count = 1;
  3110.         }
  3111.  
  3112.         if (shell->m_currentThread == NULL)
  3113.         {
  3114.             shell->Write(L"Thread no longer exists.\n");
  3115.             return;
  3116.         }
  3117.  
  3118.         while (count-- > 0)
  3119.         {
  3120.             bool goUpAChain = false;
  3121.             
  3122.             if (shell->m_rawCurrentFrame != NULL)
  3123.             {
  3124.                 ICorDebugFrame *iframe;
  3125.                 HRESULT hr = shell->m_rawCurrentFrame->GetCaller(&iframe);
  3126.  
  3127.                 if (FAILED(hr))
  3128.                 {
  3129.                     shell->ReportError(hr);
  3130.                     return;
  3131.                 }
  3132.  
  3133.                 if (iframe != NULL)
  3134.                 {
  3135.                     shell->SetCurrentFrame(iframe);
  3136.  
  3137.                     RELEASE(iframe);
  3138.                 }
  3139.                 else
  3140.                     goUpAChain = true;
  3141.             }
  3142.  
  3143.             if ((shell->m_rawCurrentFrame == NULL) || goUpAChain)
  3144.             {
  3145.                 if (shell->m_currentChain != NULL)
  3146.                 {
  3147.                     ICorDebugChain *ichain;
  3148.  
  3149.                     HRESULT hr = shell->m_currentChain->GetCaller(&ichain);
  3150.  
  3151.                     if (FAILED(hr))
  3152.                     {
  3153.                         shell->ReportError(hr);
  3154.                         return;
  3155.                     }
  3156.  
  3157.                     if (ichain == NULL)
  3158.                     {
  3159.                         shell->Error(L"Cannot go up farther: "
  3160.                                      L"at top of call stack.\n");
  3161.                         break;
  3162.                     }
  3163.  
  3164.                     ICorDebugFrame *iframe;
  3165.  
  3166.                     hr = ichain->GetActiveFrame(&iframe);
  3167.  
  3168.                     if (FAILED(hr))
  3169.                     {
  3170.                         shell->ReportError(hr);
  3171.                         RELEASE(ichain);
  3172.                         return;
  3173.                     }
  3174.  
  3175.                     shell->SetCurrentChain(ichain);
  3176.                     shell->SetCurrentFrame(iframe);
  3177.  
  3178.                     RELEASE(ichain);
  3179.                     if (iframe != NULL)
  3180.                         RELEASE(iframe);
  3181.  
  3182.                 }
  3183.                 else
  3184.                     shell->Error(L"No stack trace for thread.");
  3185.             }
  3186.         }
  3187.  
  3188.         // Print where we ended up
  3189.         if (!shell->PrintCurrentSourceLine(0))
  3190.             shell->PrintThreadState(shell->m_currentThread);
  3191.     }
  3192.  
  3193.     // Provide help specific to this command
  3194.     void Help(Shell *shell)
  3195.     {
  3196.         ShellCommand::Help(shell);
  3197.         shell->Write(L" [<frame count>]");
  3198.         shell->Write(L"\nMoves the current stack frame to the frame which ");
  3199.         shell->Write(L"\ncalled the current frame. If an argument is given,");
  3200.         shell->Write(L"\nmoves up multiple frames.");
  3201.         shell->Write(L"\n");
  3202.     }
  3203.  
  3204.     const WCHAR *ShortHelp(Shell *shell)
  3205.     {
  3206.         return L"Adjust the 'current stack frame' up one level";
  3207.     }
  3208. };
  3209.  
  3210. class DownDebuggerCommand : public DebuggerCommand
  3211. {
  3212. public:
  3213.     DownDebuggerCommand(const WCHAR *name, int minMatchLength = 0)
  3214.       : DebuggerCommand(name, minMatchLength)
  3215.     {
  3216.         
  3217.     }
  3218.  
  3219.     void Do(DebuggerShell *shell, ICorDebug *cor, const WCHAR *args)
  3220.     {
  3221.         int iCount;
  3222.  
  3223.         if (!shell->GetIntArg(args, iCount))
  3224.             iCount = 1;
  3225.         else
  3226.         {
  3227.             if (iCount < 0)
  3228.                 iCount = 1;
  3229.         }
  3230.  
  3231.         if (shell->m_currentThread == NULL)
  3232.         {
  3233.             shell->Write(L"Thread no longer exists.\n");
  3234.             return;
  3235.         }
  3236.  
  3237.         while (iCount-- > 0)
  3238.         {
  3239.             bool goDownAChain = false;
  3240.             
  3241.             if (shell->m_rawCurrentFrame != NULL)
  3242.             {
  3243.                 ICorDebugFrame *iframe;
  3244.  
  3245.                 HRESULT hr = shell->m_rawCurrentFrame->GetCallee(&iframe);
  3246.  
  3247.                 if (FAILED(hr))
  3248.                 {
  3249.                     shell->ReportError(hr);
  3250.                     return;
  3251.                 }
  3252.  
  3253.                 if (iframe != NULL)
  3254.                 {
  3255.                     shell->SetCurrentFrame(iframe);
  3256.  
  3257.                     RELEASE(iframe);
  3258.                 }
  3259.                 else
  3260.                     goDownAChain = true;
  3261.             }
  3262.  
  3263.             if ((shell->m_rawCurrentFrame == NULL) || goDownAChain)
  3264.             {
  3265.                 if (shell->m_currentChain != NULL)
  3266.                 {
  3267.                     ICorDebugChain *ichain;
  3268.  
  3269.                     HRESULT hr = shell->m_currentChain->GetCallee(&ichain);
  3270.  
  3271.                     if (FAILED(hr))
  3272.                     {
  3273.                         shell->ReportError(hr);
  3274.                         return;
  3275.                     }
  3276.  
  3277.                     if (ichain == NULL)
  3278.                     {
  3279.                         shell->Error(L"Cannot go down farther: "
  3280.                                      L"at bottom of call stack.\n");
  3281.                         break;
  3282.                     }
  3283.  
  3284.                     ICorDebugFrame *iframe;
  3285.  
  3286.                     {
  3287.                         ICorDebugFrameEnum *fe;
  3288.  
  3289.                         HRESULT hr = ichain->EnumerateFrames(&fe);
  3290.                         if (FAILED(hr))
  3291.                         {
  3292.                             shell->ReportError(hr);
  3293.                             RELEASE(ichain);
  3294.                             return;
  3295.                         }
  3296.  
  3297.                         ULONG count;
  3298.                         hr = fe->GetCount(&count);
  3299.                         if (FAILED(hr))
  3300.                         {
  3301.                             shell->ReportError(hr);
  3302.                             RELEASE(ichain);
  3303.                             RELEASE(fe);
  3304.                             return;
  3305.                         }
  3306.  
  3307.                         if (count == 0)
  3308.                             iframe = NULL;
  3309.                         else
  3310.                         {
  3311.                             hr = fe->Skip(count-1);
  3312.                             if (FAILED(hr))
  3313.                             {
  3314.                                 shell->ReportError(hr);
  3315.                                 RELEASE(ichain);
  3316.                                 RELEASE(fe);
  3317.                                 return;
  3318.                             }
  3319.  
  3320.                             hr = fe->Next(1, &iframe, &count);
  3321.                             if (FAILED(hr) || count != 1)
  3322.                             {
  3323.                                 shell->ReportError(hr);
  3324.                                 RELEASE(ichain);
  3325.                                 RELEASE(fe);
  3326.                                 return;
  3327.                             }
  3328.                         }
  3329.  
  3330.                         RELEASE(fe);
  3331.                     }
  3332.  
  3333.                     shell->SetCurrentChain(ichain);
  3334.                     shell->SetCurrentFrame(iframe);
  3335.  
  3336.                     RELEASE(ichain);
  3337.                     if (iframe != NULL)
  3338.                         RELEASE(iframe);
  3339.                 }
  3340.                 else
  3341.                     shell->Error(L"No stack trace for thread.");
  3342.             }
  3343.         }
  3344.  
  3345.         // Print where we ended up
  3346.         if (!shell->PrintCurrentSourceLine(0))
  3347.             shell->PrintThreadState(shell->m_currentThread);
  3348.     }
  3349.  
  3350.     // Provide help specific to this command
  3351.     void Help(Shell *shell)
  3352.     {
  3353.         ShellCommand::Help(shell);
  3354.         shell->Write(L" [<frame count>]");
  3355.         shell->Write(L"\nMoves the current stack frame to the frame which was ");
  3356.         shell->Write(L"\ncalled by the current frame. If an argument is given,");
  3357.         shell->Write(L"\nmoves down multiple frames.");
  3358.         shell->Write(L"\n");
  3359.     }
  3360.  
  3361.     const WCHAR *ShortHelp(Shell *shell)
  3362.     {
  3363.         return L"Adjust the 'current stack frame' down one level";
  3364.     }
  3365. };
  3366.  
  3367. class SuspendDebuggerCommand : public DebuggerCommand
  3368. {
  3369. public:
  3370.     SuspendDebuggerCommand(const WCHAR *name, int minMatchLength = 0)
  3371.         : DebuggerCommand(name, minMatchLength)
  3372.     {
  3373.         
  3374.     }
  3375.  
  3376.     void Do(DebuggerShell *shell, ICorDebug *cor, const WCHAR *args)
  3377.     {
  3378.         int                 id;
  3379.         bool                fSuspendAll = false;
  3380.         ICorDebugThread    *ithread = NULL;
  3381.         
  3382.         if (shell->m_currentProcess == NULL)
  3383.         {
  3384.             shell->Write(L"No current thread!\n");
  3385.             return;
  3386.         }
  3387.         
  3388.         if (*args == L'~')
  3389.         {
  3390.             fSuspendAll = true;
  3391.             args++;
  3392.         }
  3393.         
  3394.         if( shell->GetIntArg(args, id) )
  3395.         {
  3396.             if (FAILED(shell->m_currentProcess->GetThread(id, &ithread)))
  3397.             {
  3398.                 shell->Write(L"No such thread 0x%x.\n", id);
  3399.                 return;
  3400.             }
  3401.         }
  3402.         else
  3403.         {
  3404.             if (fSuspendAll == false)
  3405.             {
  3406.                 shell->Write(L"If we're not suspending all threads, we "
  3407.                     L"need a thread id\n");
  3408.                 return;
  3409.             }
  3410.             ithread = NULL;
  3411.         }
  3412.  
  3413.         if (fSuspendAll)
  3414.         {
  3415.             if(FAILED(shell->m_currentProcess->SetAllThreadsDebugState
  3416.                 (THREAD_SUSPEND,ithread)))
  3417.             {
  3418.                 if(ithread!=NULL)
  3419.                     RELEASE(ithread);
  3420.                 shell->Write(L"Unable to suspend all threads.\n");
  3421.                 return;
  3422.             }
  3423.             else
  3424.             {
  3425.                 if(ithread!=NULL)
  3426.                     RELEASE(ithread);
  3427.                 shell->Write(L"All threads except for 0x%x will "
  3428.                     L"be suspended.\n", id);
  3429.                 return;
  3430.             }
  3431.         }
  3432.         else
  3433.         {
  3434.             if(FAILED(ithread->SetDebugState(THREAD_SUSPEND)))
  3435.             {
  3436.                 RELEASE(ithread);
  3437.                 shell->Write(L"Unable to suspend thread 0x%x.\n", id);
  3438.                 return;
  3439.             }
  3440.             else
  3441.             {
  3442.                 RELEASE(ithread);
  3443.                 shell->Write(L"Will suspend thread 0x%x.\n", id);
  3444.                 return;
  3445.             }
  3446.         }
  3447.     }
  3448.  
  3449.     // Provide help specific to this command
  3450.     void Help(Shell *shell)
  3451.     {
  3452.         ShellCommand::Help(shell);
  3453.         shell->Write(L" [~][<id>]");
  3454.         shell->Write(L"\nSuspends the given thread from executing when the ");
  3455.         shell->Write(L"\ndebugger continues.  If ~ syntax is used, suspends all");
  3456.         shell->Write(L"\nthreads except for the given thread.");
  3457.         shell->Write(L" If no args are used, has no effect.");
  3458.         shell->Write(L"\n");
  3459.     }
  3460.  
  3461.     const WCHAR *ShortHelp(Shell *shell)
  3462.     {
  3463.         return L"Suspend a thread";
  3464.     }
  3465. };
  3466.  
  3467. class ResumeDebuggerCommand : public DebuggerCommand
  3468. {
  3469. public:
  3470.     ResumeDebuggerCommand(const WCHAR *name, int minMatchLength = 0)
  3471.         : DebuggerCommand(name, minMatchLength)
  3472.     {
  3473.         
  3474.     }
  3475.  
  3476.     void Do(DebuggerShell *shell, ICorDebug *cor, const WCHAR *args)
  3477.     {
  3478.         int                 id;
  3479.         bool                fResumeAll = false;
  3480.         ICorDebugThread    *ithread = NULL;
  3481.         
  3482.         if (shell->m_currentProcess == NULL)
  3483.         {
  3484.             shell->Write(L"No current thread!\n");
  3485.             return;
  3486.         }
  3487.         
  3488.         if (*args == L'~')
  3489.         {
  3490.             fResumeAll = true;
  3491.             args++;
  3492.         }
  3493.         
  3494.         if( shell->GetIntArg(args, id) )
  3495.         {
  3496.             if (FAILED(shell->m_currentProcess->GetThread(id, &ithread)))
  3497.             {
  3498.                 shell->Write(L"No such thread 0x%x.\n", id);
  3499.                 return;
  3500.             }
  3501.         }
  3502.         else
  3503.         {
  3504.             if (fResumeAll == false)
  3505.             {
  3506.                 shell->Write(L"If we're not resuming all threads, we "
  3507.                     L"need a thread id\n");
  3508.                 return;
  3509.             }
  3510.             ithread = NULL;
  3511.         }
  3512.  
  3513.         if (fResumeAll)
  3514.         {
  3515.             if(FAILED(shell->m_currentProcess->SetAllThreadsDebugState
  3516.                 (THREAD_RUN,ithread)))
  3517.             {
  3518.                 if(ithread!=NULL)
  3519.                     RELEASE(ithread);
  3520.                 shell->Write(L"Unable to resume all threads.\n");
  3521.                 return;
  3522.             }
  3523.             else
  3524.             {
  3525.                 if(ithread!=NULL)
  3526.                     RELEASE(ithread);
  3527.                 shell->Write(L"All threads except for 0x%x will "
  3528.                     L"be resumed.\n", id);
  3529.                 return;
  3530.             }
  3531.         }
  3532.         else
  3533.         {
  3534.             if(FAILED(ithread->SetDebugState(THREAD_RUN)))
  3535.             {
  3536.                 RELEASE(ithread);
  3537.                 shell->Write(L"Unable to resume thread 0x%x.\n", id);
  3538.                 return;
  3539.             }
  3540.             else
  3541.             {
  3542.                 RELEASE(ithread);
  3543.                 shell->Write(L"Will resume thread 0x%x.\n", id);
  3544.                 return;
  3545.             }
  3546.         }
  3547.     }
  3548.  
  3549.     // Provide help specific to this command
  3550.     void Help(Shell *shell)
  3551.     {
  3552.         ShellCommand::Help(shell);
  3553.         shell->Write(L" [~][<id>]");
  3554.         shell->Write(L"\nResumes the given thread from executing when the ");
  3555.         shell->Write(L"\ndebugger continues.  If ~ syntax is used, resumes all");
  3556.         shell->Write(L"\nthreads except for the given thread.");
  3557.         shell->Write(L" If no args are used, has no effect.");
  3558.         shell->Write(L"\n");
  3559.     }
  3560.  
  3561.     const WCHAR *ShortHelp(Shell *shell)
  3562.     {
  3563.         return L"Resume a thread";
  3564.     }
  3565. };
  3566.  
  3567.  
  3568.  
  3569. class CatchDebuggerCommand : public DebuggerCommand
  3570. {
  3571. public:
  3572.     CatchDebuggerCommand(const WCHAR *name, int minMatchLength = 0)
  3573.         : DebuggerCommand(name, minMatchLength)
  3574.     {
  3575.         
  3576.     }
  3577.  
  3578.     void Do(DebuggerShell *shell, ICorDebug *cor, const WCHAR *args)
  3579.     {
  3580.         WCHAR *what = NULL;
  3581.  
  3582.         if (!shell->GetStringArg(args, what))
  3583.         {
  3584.             Help(shell);
  3585.             return;
  3586.         }
  3587.  
  3588.         if (args > what)
  3589.         {
  3590.             // Figure out what event type to catch
  3591.             switch (*what)
  3592.             {
  3593.             case L'e':
  3594.                 if (wcsncmp(what, L"exception", wcslen(what)) != 0)
  3595.                     Help(shell);
  3596.                 else
  3597.                     shell->m_catchException = true;
  3598.                 break;
  3599.  
  3600.             case L'u':
  3601.                 if (wcsncmp(what, L"unhandled", wcslen(what)) != 0)
  3602.                     Help(shell);
  3603.                 else
  3604.                     shell->m_catchUnhandled = true;
  3605.                 break;
  3606.  
  3607.             case L'c':
  3608.                 if (wcsncmp(what, L"class", wcslen(what)) != 0)
  3609.                     Help(shell);
  3610.                 else
  3611.                     shell->m_catchClass = true;
  3612.                 break;
  3613.  
  3614.             case L'm':
  3615.                 if (wcsncmp(what, L"module", wcslen(what)) != 0)
  3616.                     Help(shell);
  3617.                 else
  3618.                     shell->m_catchModule = true;
  3619.                 break;
  3620.  
  3621.             case L't':
  3622.                 if (wcsncmp(what, L"thread", wcslen(what)) != 0)
  3623.                     Help(shell);
  3624.                 else
  3625.                     shell->m_catchThread = true;
  3626.                 break;
  3627.  
  3628.             default:
  3629.                 Help(shell);
  3630.             }
  3631.         }
  3632.         else
  3633.         {
  3634.             shell->Write(L"exception\t%s\n", shell->m_catchException ? L"on" : L"off");
  3635.             shell->Write(L"unhandled\t%s\n", shell->m_catchUnhandled ? L"on" : L"off");
  3636.             shell->Write(L"class\t\t%s\n", shell->m_catchClass ? L"on" : L"off");
  3637.             shell->Write(L"module\t\t%s\n", shell->m_catchModule ? L"on" : L"off");
  3638.             shell->Write(L"thread\t\t%s\n", shell->m_catchThread ? L"on" : L"off");
  3639.         }
  3640.     }
  3641.  
  3642.     // Provide help specific to this command
  3643.     void Help(Shell *shell)
  3644.     {
  3645.         ShellCommand::Help(shell);
  3646.         shell->Write(L"\nCauses the debugger to stop at certain events:");
  3647.         shell->Write(L"\n\texception\tAll exceptions");
  3648.         shell->Write(L"\n\tunhandled\tUnhandled exceptions");
  3649.         shell->Write(L"\n\tclass\tClass load events");
  3650.         shell->Write(L"\n\tmodule\tModule load events");
  3651.         shell->Write(L"\n\tthread\tThread start events");
  3652.         shell->Write(L"\n");
  3653.     }
  3654.  
  3655.     const WCHAR *ShortHelp(Shell *shell)
  3656.     {
  3657.         return L"Catch exception or load events";
  3658.     }
  3659. };
  3660.  
  3661. class IgnoreDebuggerCommand : public DebuggerCommand
  3662. {
  3663. public:
  3664.     IgnoreDebuggerCommand(const WCHAR *name, int minMatchLength = 0)
  3665.         : DebuggerCommand(name, minMatchLength)
  3666.     {
  3667.     }
  3668.  
  3669.     void Do(DebuggerShell *shell, ICorDebug *cor, const WCHAR *args)
  3670.     {
  3671.         WCHAR *what = NULL;
  3672.         shell->GetStringArg(args, what);
  3673.         if (args > what)
  3674.         {
  3675.             switch (*what)
  3676.             {
  3677.             case L'e':
  3678.                 if (wcsncmp(what, L"exception", wcslen(what)) != 0)
  3679.                     Help(shell);
  3680.                 else
  3681.                     shell->m_catchException = false;
  3682.                 break;
  3683.  
  3684.             case L'u':
  3685.                 if (wcsncmp(what, L"unhandled", wcslen(what)) != 0)
  3686.                     Help(shell);
  3687.                 else
  3688.                     shell->m_catchUnhandled = false;
  3689.                 break;
  3690.  
  3691.             case L'c':
  3692.                 if (wcsncmp(what, L"class", wcslen(what)) != 0)
  3693.                     Help(shell);
  3694.                 else
  3695.                     shell->m_catchClass = false;
  3696.                 break;
  3697.  
  3698.             case L'm':
  3699.                 if (wcsncmp(what, L"module", wcslen(what)) != 0)
  3700.                     Help(shell);
  3701.                 else
  3702.                     shell->m_catchModule = false;
  3703.                 break;
  3704.  
  3705.             case L't':
  3706.                 if (wcsncmp(what, L"thread", wcslen(what)) != 0)
  3707.                     Help(shell);
  3708.                 else
  3709.                     shell->m_catchThread = false;
  3710.                 break;
  3711.  
  3712.             default:
  3713.                 Help(shell);
  3714.             }
  3715.         }
  3716.         else
  3717.         {
  3718.             shell->Write(L"exception\t%s\n", shell->m_catchException ? L"on" : L"off");
  3719.             shell->Write(L"unhandled\t%s\n", shell->m_catchUnhandled ? L"on" : L"off");
  3720.             shell->Write(L"class\t\t%s\n", shell->m_catchClass ? L"on" : L"off");
  3721.             shell->Write(L"module\t\t%s\n", shell->m_catchModule ? L"on" : L"off");
  3722.             shell->Write(L"thread\t\t%s\n", shell->m_catchThread ? L"on" : L"off");
  3723.         }
  3724.     }
  3725.  
  3726.     // Provide help specific to this command
  3727.     void Help(Shell *shell)
  3728.     {
  3729.         ShellCommand::Help(shell);
  3730.         shell->Write(L"\nCauses the debugger to ignore certain events:");
  3731.         shell->Write(L"\n\texception\tAll exceptions");
  3732.         shell->Write(L"\n\tunhandled\tUnhandled exceptions");
  3733.         shell->Write(L"\n\tclass\t\tClass load events");
  3734.         shell->Write(L"\n\tmodule\t\tModule load events");
  3735.         shell->Write(L"\n\tthread\t\tThread start events");
  3736.         shell->Write(L"\n");
  3737.     }
  3738.  
  3739.     const WCHAR *ShortHelp(Shell *shell)
  3740.     {
  3741.         return L"Ignore exception or load events";
  3742.     }
  3743. };
  3744.  
  3745. class SetDefaultDebuggerCommand : public DebuggerCommand
  3746. {
  3747. public:
  3748.     SetDefaultDebuggerCommand(const WCHAR *name, int minMatchLength = 0)
  3749.         : DebuggerCommand(name, minMatchLength)
  3750.     {
  3751.     }
  3752.  
  3753.     void Do(DebuggerShell *shell, ICorDebug *cor, const WCHAR *args)
  3754.     {
  3755.         // Read the existing key first.
  3756.         WCHAR *realDbgCmd = NULL;
  3757.         HKEY key;
  3758.         DWORD disp;
  3759.  
  3760.         // Use create to be sure the key is there
  3761.         LONG result = RegCreateKeyExA(HKEY_CURRENT_USER, REG_COMPLUS_KEY,
  3762.                                       NULL, NULL, REG_OPTION_NON_VOLATILE,
  3763.                                       KEY_ALL_ACCESS, NULL, &key, &disp);
  3764.  
  3765.         if (result == ERROR_SUCCESS)
  3766.         {
  3767.             DWORD type;
  3768.             DWORD len;
  3769.  
  3770.             result = RegQueryValueExA(key, REG_COMPLUS_DEBUGGER_KEY,
  3771.                                       NULL, &type, NULL, &len);
  3772.  
  3773.             if ((result == ERROR_SUCCESS) && ((type == REG_SZ) ||
  3774.                                               (type == REG_EXPAND_SZ)))
  3775.             {
  3776.                 char *tmp = (char*) _alloca(len * sizeof (char));
  3777.  
  3778.                 result = RegQueryValueExA(key,
  3779.                                           REG_COMPLUS_DEBUGGER_KEY,
  3780.                                           NULL, &type,
  3781.                                           (BYTE*) tmp,
  3782.                                           &len);
  3783.  
  3784.                 if (result == ERROR_SUCCESS)
  3785.                 {
  3786.                     MAKE_WIDEPTR_FROMUTF8(tmpWStr, tmp);
  3787.                     realDbgCmd = new WCHAR[len];
  3788.                     wcscpy(realDbgCmd, tmpWStr);
  3789.                 }
  3790.             }
  3791.         }
  3792.         else
  3793.         {
  3794.             shell->Write(L"Error reading registry: %d", result);
  3795.             return;
  3796.         }
  3797.  
  3798.         bool setIt = false;
  3799.  
  3800.         // If there is an existing command, show it and don't override
  3801.         // unless we're forced to.
  3802.         if (realDbgCmd != NULL)
  3803.         {
  3804.             shell->Write(L"Current managed JIT debugger command='%s'\n",
  3805.                          realDbgCmd);
  3806.  
  3807.             WCHAR *what = NULL;
  3808.             shell->GetStringArg(args, what);
  3809.  
  3810.             if ((args > what) && (*what == L'f'))
  3811.                 setIt = true;
  3812.  
  3813.             delete realDbgCmd;
  3814.         }
  3815.         else
  3816.             setIt = true;
  3817.  
  3818.         // Set the new registry key.
  3819.         if (setIt)
  3820.         {
  3821.             MAKE_ANSIPTR_FROMWIDE(cmdA, shell->GetJITLaunchCommand());
  3822.             
  3823.             result = RegSetValueExA(key, REG_COMPLUS_DEBUGGER_KEY, NULL,
  3824.                                     REG_SZ,
  3825.                                     (const BYTE*) cmdA, strlen(cmdA) + 1);
  3826.  
  3827.             if (result != ERROR_SUCCESS)
  3828.                 shell->Write(L"Error updating registry: %d\n", result);
  3829.             else
  3830.                 shell->Write(L"Registry updated.\n");
  3831.         }
  3832.  
  3833.         RegCloseKey(key);
  3834.     }
  3835.  
  3836.     // Provide help specific to this command
  3837.     void Help(Shell *shell)
  3838.     {
  3839.         ShellCommand::Help(shell);
  3840.         shell->Write(L" [force]");
  3841.         shell->Write(L"\nSets the default NGWS managed JIT debugger command");
  3842.         shell->Write(L"\nto the necessary string to launch this debugger.");
  3843.         shell->Write(L"\nIf another debugger is already registered, does");
  3844.         shell->Write(L"\nnothing. Use the force option to overwrite another");
  3845.         shell->Write(L"\ndebugger's setting.");
  3846.         shell->Write(L"\n");
  3847.     }
  3848.  
  3849.     const WCHAR *ShortHelp(Shell *shell)
  3850.     {
  3851.         return L"Change the JIT debugger";
  3852.     }
  3853. };
  3854.  
  3855. // Infomation about all debugger shell modes.
  3856. struct DSMInfo g_DSMData[] = 
  3857. {
  3858.      {DSM_SHOW_SELECTIVE_APPDOMAIN, L"AppDSelect",
  3859.      L"Cordbg interacts with each AppDomain separately.",
  3860.      L"Cordbg interacts with the entire process holistically.",
  3861.      L"Selective AppDomain: cordbg should interact with each AppDomain separately.",
  3862.      L"\t\t"},
  3863.  
  3864.      {DSM_SHOW_APP_DOMAIN_ASSEMBLY_LOADS, L"AppDomainLoads",
  3865.      L"AppDomain and Assembly load events are displayed",
  3866.      L"AppDomain and Assembly load events are not displayed",
  3867.      L"Specifies if AppDomain and Assembly load events are displayed",
  3868.      L"\t"},
  3869.  
  3870.      {DSM_SHOW_CLASS_LOADS, L"ClassLoads",
  3871.      L"Class load events are displayed",
  3872.      L"Class load events are not displayed",
  3873.      L"Specifies if class load events are displayed",
  3874.      L"\t\t"},
  3875.  
  3876.     {DSM_ENABLE_EDIT_AND_CONTINUE, L"EditAndContinue",
  3877.      L"Edit & Continue is enabled",
  3878.      L"Edit & Continue is not enabled",
  3879.      L"Specifies if not E&C is enabled",
  3880.      L"\t"},
  3881.     
  3882.      {DSM_ENHANCED_DIAGNOSTICS, L"EnhancedDiag",
  3883.      L"Print extra diagnostic information",
  3884.      L"Suppress printing of diagnostic information",
  3885.      L"Specifies if Cordbg should print information that's useful for understanding internal functioning",
  3886.      L"\t\t"},
  3887.  
  3888.     {DSM_DISPLAY_REGISTERS_AS_HEX, L"HexDisplay",
  3889.      L"Numbers are displayed in hexadecimal",
  3890.      L"Numbers are displayed in decimal",
  3891.      L"Controls whether numbers are shown in hex or decimal",
  3892.      L"\t\t"},
  3893.  
  3894.     {DSM_IL_NATIVE_PRINTING, L"ILNatPrint",
  3895.      L"Offsets will be both IL- and native-relative",
  3896.      L"offsets will be IL- xor native-relative",
  3897.      L"Specifies if frame, thread, instruction offsets should be printed IL-relative/Native-relative, or both",
  3898.      L"\t\t"},
  3899.  
  3900.     {DSM_INTERCEPT_STOP_ALL, L"ISAll",
  3901.      L"All interceptors are stepped through",
  3902.      L"All interceptors are skipped",
  3903.      L"Specifies if Cordbg should step through all interceptors or over prologs while stepping through code",
  3904.      L"\t\t\t"},
  3905.  
  3906.      {DSM_INTERCEPT_STOP_CLASS_INIT, L"ISclinit",
  3907.      L"Class initialization is stepped through",
  3908.      L"Class initialization is skipped",
  3909.      L"Specifies if Cordbg should step through or over class initialization while stepping through code",
  3910.      L"\t\t"},
  3911.  
  3912.     {DSM_INTERCEPT_STOP_EXCEPTION_FILTER, L"ISExceptF",
  3913.      L"Exception filters are stepped through",
  3914.      L"Exception filters are skipped",
  3915.      L"Specifies if Cordbg should step through or over exception filters while stepping through code",
  3916.      L"\t\t"},
  3917.  
  3918.     {DSM_INTERCEPT_STOP_INTERCEPTION, L"ISInt",
  3919.      L"User interceptors are stepped through",
  3920.      L"User interceptors are skipped",
  3921.      L"Specifies if Cordbg should step through or over user interceptors while stepping through code",
  3922.      L"\t\t\t"},
  3923.  
  3924.     {DSM_INTERCEPT_STOP_CONTEXT_POLICY, L"ISPolicy",
  3925.      L"Context policies are stepped through",
  3926.      L"Context policies are skipped",
  3927.      L"Specifies if Cordbg should step through or over context policies while stepping through code",
  3928.      L"\t\t"},
  3929.  
  3930.     {DSM_INTERCEPT_STOP_SECURITY, L"ISSec",
  3931.      L"Security interceptors are stepped through",
  3932.      L"Security interceptors are skipped",
  3933.      L"Specifies if Cordbg should step through or over security interceptors while stepping through code",
  3934.      L"\t\t\t"},
  3935.  
  3936.     {DSM_ENABLE_JIT_OPTIMIZATIONS, L"JitOptimizations",
  3937.      L"JIT's will produce optimized code",
  3938.      L"JIT's will produce debuggable (non-optimized) code",
  3939.      L"Specifies if JIT's generate debuggable code",
  3940.      L"\t"},
  3941.         
  3942.      {DSM_LOGGING_MESSAGES, L"LoggingMessages",
  3943.      L"Managed log messages are output to console",
  3944.      L"Annoying log messages are brutally suppressed.",
  3945.      L"Specifies if managed logging messages are printed or not.",
  3946.      L"\t"},
  3947.  
  3948.     {DSM_SHOW_MODULE_LOADS, L"ModuleLoads",
  3949.      L"Module load events are displayed",
  3950.      L"Module load events are not displayed",
  3951.      L"Specifies if modules load events are displayed",
  3952.      L"\t\t"},
  3953.  
  3954.     {DSM_SEPARATE_CONSOLE, L"SeparateConsole",
  3955.      L"Debuggees get their own console",
  3956.      L"Debuggees share cordbg's console",
  3957.      L"Specifies if debuggees get their own console",
  3958.      L"\t"},
  3959.  
  3960.     {DSM_SHOW_ARGS_IN_STACK_TRACE, L"ShowArgs",
  3961.      L"Arguments will be shown in stack trace",
  3962.      L"Arguments will not be shown in stack trace",
  3963.      L"Specifies if arguments should be shown in stack trace",
  3964.      L"\t\t"},
  3965.  
  3966.      {DSM_SHOW_MODULES_IN_STACK_TRACE, L"ShowModules",
  3967.      L"Module names will be included in stack trace",
  3968.      L"Module names will not be shown in stack trace",
  3969.      L"Specifies if module names should be shown in stack trace",
  3970.      L"\t\t"},
  3971.      
  3972.     {DSM_SHOW_UNMANAGED_TRACE, L"UnmanagedTrace",
  3973.      L"Unmanaged debug events are displayed",
  3974.      L"Unmanaged debug events are not displayed",
  3975.      L"Specifies if unmanaged debug events are displayed",
  3976.      L"\t"},
  3977.  
  3978.     {DSM_UNMAPPED_STOP_ALL, L"USAll",
  3979.      L"Unmapped stop locations are stepped through",
  3980.      L"Unmapped stop locations are skipped",
  3981.      L"Specifies if Cordbg should step through (in native assembly) or over all unmapped stop locations"
  3982.       L"\n\t\t\t(ie, prolog, epilog, unmapped, no mapping regions) while stepping through code",
  3983.      L"\t\t\t"},
  3984.  
  3985.     {DSM_UNMAPPED_STOP_EPILOG, L"USEpi",
  3986.      L"Epilog is stepped through in disassembly",
  3987.      L"Epilog is skipped, returns to calling method",
  3988.      L"Specifies if Cordbg should step through (in native assembly) or over epilogs while stepping through code",
  3989.      L"\t\t\t"},
  3990.  
  3991.     {DSM_UNMAPPED_STOP_PROLOG, L"USPro",
  3992.      L"Prolog is stepped through in disassembly",
  3993.      L"Prolog is skipped",
  3994.      L"Specifies if Cordbg should step through (in native assembly) or over prologs while stepping through code",
  3995.      L"\t\t\t"},
  3996.  
  3997.     {DSM_UNMAPPED_STOP_UNMANAGED, L"USUnmanaged",
  3998.      L"Unmanaged code is stepped through in disassembly",
  3999.      L"Unmanaged code is skipped",
  4000.      L"Specifies if Cordbg should step through (in native assembly) or unmanaged code ",
  4001.      L"\t\t"},
  4002.  
  4003.    {DSM_WIN32_DEBUGGER, L"Win32Debugger",
  4004.      L"CorDbg is the Win32 debugger for all processes.\n"
  4005.        L"\t\t\t**NOTE: Win32 debugger mode is completely UNSUPPORTED.  Use at your own risk.",
  4006.      L"CorDbg is not the Win32 debugger for all processes",
  4007.      L"Controls if cordbg is the Win32 debugger. **UNSUPPORTED: use at your own risk.",
  4008.      L"\t\t"},
  4009.  
  4010.    {DSM_DUMP_MEMORY_IN_BYTES, L"DumpMemoryInBytes",
  4011.      L"Memory is dumped in BYTES",
  4012.      L"Memory is dumped in DWORDS",
  4013.      L"Controls if the 'dump' command should dump memory in BYTES/DWORDS",
  4014.      L"\t"},
  4015.  
  4016.    {DSM_SHOW_SUPERCLASS_ON_PRINT, L"ShowSuperClassOnPrint",
  4017.      L"Super class names are shown when printing objects",
  4018.      L"No super class names are shown when printing objects",
  4019.      L"Controls if super class names are shown when printing objects",
  4020.      L"\t"},
  4021.  
  4022.    {DSM_SHOW_STATICS_ON_PRINT, L"ShowStaticsOnPrint",
  4023.      L"Static fields are shown when printing objects",
  4024.      L"No static fields are shown when printing objects",
  4025.      L"Controls if static fields are shown when printing objects",
  4026.      L"\t"},
  4027. };
  4028.  
  4029. class SetModeDebuggerCommand : public DebuggerCommand
  4030. {
  4031. public:
  4032.     SetModeDebuggerCommand(const WCHAR *name, int minMatchLength =0)
  4033.         : DebuggerCommand(name, minMatchLength)
  4034.     {
  4035.     }
  4036.  
  4037.     void DisplayAllModes( DebuggerShell *shell  )
  4038.     {
  4039.         for (unsigned int i = 0; i < DSM_MAXIMUM_MODE; i++)
  4040.         {
  4041.             if (shell->m_rgfActiveModes & g_DSMData[i].modeFlag)
  4042.                 shell->Write(L"%s=1%s%s\n",
  4043.                              g_DSMData[i].name,
  4044.                              g_DSMData[i].descriptionPad,
  4045.                              g_DSMData[i].onDescription);
  4046.             else
  4047.                 shell->Write(L"%s=0%s%s\n",
  4048.                              g_DSMData[i].name,
  4049.                              g_DSMData[i].descriptionPad,
  4050.                              g_DSMData[i].offDescription);
  4051.         }
  4052.     }
  4053.  
  4054.     void Do(DebuggerShell *shell, ICorDebug *cor, const WCHAR *args)
  4055.     {
  4056.         WCHAR    *szMode;
  4057.         int     modeValue;
  4058.  
  4059.         _ASSERTE(DSM_MAXIMUM_MODE == (sizeof(g_DSMData) /
  4060.                                       sizeof(g_DSMData[0])));
  4061.  
  4062.         shell->GetStringArg(args, szMode);
  4063.         
  4064.         if (args != szMode)
  4065.         {
  4066.             int szModeLen = args - szMode;
  4067.             
  4068.             if (shell->GetIntArg(args, modeValue))
  4069.             {
  4070.                 for (unsigned int i = 0; i < DSM_MAXIMUM_MODE; i++)
  4071.                 {
  4072.                     if (_wcsnicmp(szMode, g_DSMData[i].name, szModeLen) == 0)
  4073.                     {
  4074.                         if (modeValue)
  4075.                         {
  4076.                             shell->m_rgfActiveModes |= g_DSMData[i].modeFlag;
  4077.                             shell->Write(g_DSMData[i].onDescription);
  4078.                             shell->Write(L"\n");
  4079.                         }
  4080.                         else
  4081.                         {
  4082.                             shell->m_rgfActiveModes &= ~g_DSMData[i].modeFlag;
  4083.                             shell->Write(g_DSMData[i].offDescription);
  4084.                             shell->Write(L"\n");
  4085.                         }
  4086.  
  4087.                         // Update the modes in the registry.
  4088.                         shell->WriteDebuggerModes();
  4089.                         break;
  4090.                     }
  4091.                 }
  4092.             }
  4093.             else
  4094.                 DisplayAllModes(shell);
  4095.         }
  4096.         else
  4097.             DisplayAllModes(shell);
  4098.     }
  4099.  
  4100.     // Provide help specific to this command
  4101.     void Help(Shell *shell)
  4102.     {
  4103.         ShellCommand::Help(shell);
  4104.         shell->Write(L" <mode name> 0|1");
  4105.         shell->Write(L"\nSets & queries modes for other debugger features.");
  4106.         shell->Write(L"\nTo set a value, type \"mode <mode name> {1 for on, 0 for off}\"");
  4107.         shell->Write(L"\nTo view all mode-settings, type \"mode\"");
  4108.  
  4109.         shell->Write(L"\nCurrently, the following modes are supported:\n");
  4110.  
  4111.         for (unsigned int i = 0; i < DSM_MAXIMUM_MODE; i++)
  4112.             shell->Write(L"\n%s: %s%s",
  4113.                          g_DSMData[i].name,
  4114.                          g_DSMData[i].descriptionPad,
  4115.                          g_DSMData[i].generalDescription);
  4116.     }
  4117.  
  4118.     const WCHAR *ShortHelp(Shell *shell)
  4119.     {
  4120.         return L"Modify various debugger-wide settings";
  4121.     }
  4122. };
  4123.  
  4124.  
  4125.  
  4126. //CountBits shamelessly stolen from Src/inc/UtilCode.h
  4127. // Count the bits in a value in order iBits time.
  4128. inline int CountBits(ULONG64 iNum)
  4129. {
  4130.     for (int iBits=0;  iNum;  iBits++)
  4131.         iNum = iNum & (iNum - 1);
  4132.     return (iBits);
  4133. }
  4134.  
  4135. class RegistersDebuggerCommand : public DebuggerCommand
  4136. {
  4137. public:
  4138.     RegistersDebuggerCommand(const WCHAR *name, int minMatchLength = 0)
  4139.         : DebuggerCommand(name, minMatchLength)
  4140.     {
  4141.     }
  4142.  
  4143.     void Do(Shell *shell, const WCHAR *args) 
  4144.     {
  4145.         DebuggerShell *dsh = static_cast<DebuggerShell *>(shell);
  4146.  
  4147.         Do(dsh, dsh->m_cor, args);
  4148.     }
  4149.  
  4150.     virtual void Do(DebuggerShell *shell, ICorDebug *cor, const WCHAR *args)
  4151.     {
  4152. #ifdef _X86_
  4153.         HRESULT hr = S_OK;
  4154.         bool    fPrintAll; //set to true if we want to print all regs
  4155.                             //false means print only the 1 requested
  4156.  
  4157.         WCHAR *szReg = NULL;
  4158.         shell->GetStringArg( args, szReg);
  4159.         //GetStringArg will point szReg to args (and not change) args if
  4160.         //there is no StringArg. Therefore, we print everything iff there
  4161.         //is no StringArg
  4162.         fPrintAll = (args == szReg);
  4163.  
  4164.         // If there is no current thread, cannot perform command
  4165.         if (shell->m_currentThread == NULL)
  4166.         {
  4167.             shell->Write(L"Thread no longer exists.\n");
  4168.             return;
  4169.         }
  4170.  
  4171.         ICorDebugRegisterSet *iRs;
  4172.  
  4173.         if (shell->m_rawCurrentFrame != NULL)
  4174.         {
  4175.             ICorDebugNativeFrame *inativeFrame;
  4176.             if ( shell->m_rawCurrentFrame == NULL )
  4177.             {
  4178.                 shell->Write( L"No current, native frame!" );
  4179.             }
  4180.  
  4181.             hr = shell->m_rawCurrentFrame->QueryInterface(IID_ICorDebugNativeFrame,
  4182.                                                           (void **)&inativeFrame);
  4183.  
  4184.             if (FAILED(hr))
  4185.             {
  4186.                 g_pShell->Write( L"The current frame isn't a native frame!\n" );
  4187.                 return;
  4188.             }
  4189.  
  4190.             hr = inativeFrame->GetRegisterSet( &iRs);
  4191.             if ( FAILED( hr ) )
  4192.             {
  4193.                 shell->Write( L"Unable to GetRegisterSet from the current, native frame\n" );
  4194.                 return;
  4195.             }
  4196.  
  4197.             inativeFrame->Release();
  4198.         }
  4199.         else if (shell->m_currentChain != NULL)
  4200.         {
  4201.             hr = shell->m_currentChain->GetRegisterSet( &iRs);
  4202.             if ( FAILED( hr ) )
  4203.             {
  4204.                 shell->Write( L"Unable to GetRegisterSet from the current chain");
  4205.                 return;
  4206.             }
  4207.         }
  4208.         else
  4209.         {
  4210.             hr = shell->m_currentThread->GetRegisterSet( &iRs);
  4211.             if ( FAILED( hr ) )
  4212.             {
  4213.                 shell->Write( L"Unable to GetRegisterSet from the current thread");
  4214.                 return;
  4215.             }
  4216.         }
  4217.  
  4218.         ULONG64 rgfReg;
  4219.  
  4220.         //which registers are available?
  4221.         hr = iRs->GetRegistersAvailable( &rgfReg );
  4222.         if ( !SUCCEEDED( hr ) )
  4223.         {
  4224.             shell->Write( L"Unable to GetRegistersAvailable from the current frame!\n" );
  4225.             return;
  4226.         }
  4227.  
  4228.         //Now get the registers
  4229.         SIZE_T regCount = CountBits( rgfReg );
  4230.         CORDB_REGISTER *regBuffer = (CORDB_REGISTER *)_alloca( regCount * sizeof ( CORDB_REGISTER) );
  4231.         hr = iRs->GetRegisters(rgfReg, regCount, regBuffer );
  4232.         if ( !SUCCEEDED( hr ) )
  4233.         {
  4234.             shell->Write( L"Unable to GetRegisters!\n" );
  4235.             return;
  4236.         }
  4237.  
  4238.         shell->Write( L"\n" );
  4239.         
  4240.         //Get the CONTEXT stuff
  4241.         CONTEXT context;
  4242.         context.ContextFlags = CONTEXT_FULL;
  4243.         CONTEXT *pContext = &context;
  4244.         hr = iRs->GetThreadContext( sizeof ( CONTEXT ), (BYTE*)&context );
  4245.         if ( !SUCCEEDED( hr ) )
  4246.         {
  4247.             shell->Write( L"Unable to GetThreadContext!\n" );
  4248.             return;
  4249.         }
  4250.  
  4251.         //the following asserts are sanity checks only,
  4252.         //to verify that these values are the same.
  4253.         _ASSERTE( (CORDB_REGISTER)pContext->Edi == regBuffer[REGISTER_X86_EDI] );
  4254.         _ASSERTE( (CORDB_REGISTER)pContext->Esi == regBuffer[REGISTER_X86_ESI] );
  4255.         _ASSERTE( (CORDB_REGISTER)pContext->Ebx == regBuffer[REGISTER_X86_EBX] );
  4256.         _ASSERTE( (CORDB_REGISTER)pContext->Edx == regBuffer[REGISTER_X86_EDX] );
  4257.         _ASSERTE( (CORDB_REGISTER)pContext->Ecx == regBuffer[REGISTER_X86_ECX] );
  4258.         _ASSERTE( (CORDB_REGISTER)pContext->Eax == regBuffer[REGISTER_X86_EAX] );
  4259.         _ASSERTE( (CORDB_REGISTER)pContext->Eip == regBuffer[REGISTER_X86_EIP] );
  4260.         _ASSERTE( (CORDB_REGISTER)pContext->Ebp == regBuffer[REGISTER_X86_EBP] );
  4261.         _ASSERTE( (CORDB_REGISTER)pContext->Esp == regBuffer[REGISTER_X86_ESP] );
  4262.  
  4263.         int nRegsWritten = 1;
  4264.  
  4265.         //write out all the registers, unless we were given a 
  4266.         //specific register to print out.
  4267.         if ( fPrintAll )
  4268.         {
  4269.             // Print the thread ID
  4270.             DWORD id;
  4271.             hr = shell->m_currentThread->GetID(&id);
  4272.             if (FAILED(hr))
  4273.             {
  4274.                 shell->ReportError(hr);
  4275.                 return;
  4276.             }
  4277.             // Output thread ID
  4278.             shell->Write(L"Thread 0x%x:\n", id);
  4279.  
  4280.             for ( int i = REGISTER_X86_EIP; i < REGISTER_X86_EFLAGS_OV;i++)
  4281.             {
  4282.                 WriteReg( i, regBuffer, rgfReg, pContext, shell );
  4283.                 if ( (nRegsWritten++ % 4) == 0 )
  4284.                     shell->Write( L"\n" );
  4285.                 else
  4286.                     shell->Write( L" " );
  4287.             }
  4288.             
  4289.         }
  4290.         else
  4291.         {
  4292.             if ( !WriteReg( LookupRegisterIndexByName( szReg ), regBuffer, rgfReg, pContext, shell ) )
  4293.                 shell->Write( L"Register %s unknown or unprintable\n", szReg );
  4294.         }
  4295.  
  4296.  
  4297.         shell->Write( L"\n" );
  4298.  
  4299.         WCHAR sz[20];
  4300.         int nBase;
  4301.         if ( shell->m_rgfActiveModes & DSM_DISPLAY_REGISTERS_AS_HEX)
  4302.             nBase = 16;
  4303.         else
  4304.             nBase = 10;
  4305.  
  4306.         if( fPrintAll )
  4307.             shell->Write( L"\t====== Registers Below This Point Not Individually Viewable ======\n" );
  4308.         if ( fPrintAll && pContext->ContextFlags & CONTEXT_DEBUG_REGISTERS )
  4309.         {
  4310.             shell->Write( L"Dr0 = %04s ", _itow( pContext->Dr0, sz, nBase ) );
  4311.             shell->Write( L"Dr1 = %04s ", _itow( pContext->Dr1, sz, nBase ) );
  4312.             shell->Write( L"Dr2 = %04s ", _itow( pContext->Dr2, sz, nBase ) );
  4313.             shell->Write( L"Dr3 = %04s ", _itow( pContext->Dr3, sz, nBase ) );
  4314.             shell->Write( L"Dr6 = %04s ", _itow( pContext->Dr6, sz, nBase ) );
  4315.             shell->Write( L"Dr7 = %04s\n", _itow( pContext->Dr7, sz, nBase ) );
  4316.         }
  4317.  
  4318.         if ( fPrintAll && pContext->ContextFlags & CONTEXT_FLOATING_POINT )
  4319.         {
  4320.             shell->Write( L"ControlWord = %04s ", 
  4321.                 _itow( pContext->FloatSave.ControlWord, sz, nBase ) );
  4322.             shell->Write( L"StatusWord = %04s ", 
  4323.                 _itow( pContext->FloatSave.StatusWord, sz, nBase ) );
  4324.             shell->Write( L"TagWord = %04s\n",
  4325.                 _itow( pContext->FloatSave.TagWord, sz, nBase ) );
  4326.             shell->Write( L"ErrorOffset = %04s ",
  4327.                 _itow( pContext->FloatSave.ErrorOffset, sz, nBase ) );
  4328.             shell->Write( L"ErrorSelector = %04s ", 
  4329.                 _itow( pContext->FloatSave.ErrorSelector, sz, nBase ) );
  4330.             shell->Write( L"DataOffset = %04s\n",
  4331.                 _itow( pContext->FloatSave.DataOffset, sz, nBase ) );
  4332.             shell->Write( L"DataSelector = %04s ", 
  4333.                 _itow( pContext->FloatSave.DataSelector, sz, nBase ) );
  4334.             shell->Write( L"Cr0NpxState = %04s\n", 
  4335.                 _itow( pContext->FloatSave.Cr0NpxState, sz, nBase ) );
  4336.         }
  4337.  
  4338.  
  4339.         iRs->Release();
  4340. #else // !_X86_
  4341.         _ASSERTE(!"@TODO Alpha - RegistersDebuggerCommand::Do (Commands.cpp)");
  4342. #endif // _X86_
  4343.     }
  4344.  
  4345.     // Provide help specific to this command
  4346.     void Help(Shell *shell)
  4347.     {
  4348.         ShellCommand::Help(shell);
  4349.         shell->Write(L"\nThis will print out the current registers.");
  4350.         shell->Write(L"\n");
  4351.     }
  4352.  
  4353.     const WCHAR *ShortHelp(Shell *shell)
  4354.     {
  4355.         return L"Display CPU registers";
  4356.     }
  4357.  
  4358.     int LookupRegisterIndexByName(  WCHAR *wszReg )
  4359.     {
  4360.         int numRegNames = REGISTER_X86_EFLAGS_OV+1;
  4361.         WCHAR rgwszRegName[REGISTER_X86_EFLAGS_OV+1][4] = { L"EIP", L"ESP", 
  4362.                                     L"EBP", L"EAX", L"ECX", 
  4363.                                     L"EDX", L"EBX", L"ESI", L"EDI", L"ST0",
  4364.                                     L"ST1", L"ST2", L"ST3", L"ST4", L"ST5",
  4365.                                     L"ST6", L"ST7", L"EFL", L"CS", L"DS",
  4366.                                     L"ES", L"FS", L"GS", L"SS", L"CY", L"PE",
  4367.                                     L"AC", L"ZR", L"PL", L"EI", L"UP", L"OV"};
  4368.  
  4369.         for (int i=0;i < numRegNames;i++)
  4370.         {
  4371.             if (!_wcsicmp( wszReg, rgwszRegName[i]) )
  4372.             {
  4373.                 return i;
  4374.             }
  4375.         }
  4376.  
  4377.         return INVALID_REGISTER;
  4378.     }
  4379.  
  4380.     enum DispRegRegisters
  4381.     {
  4382.         REGISTER_X86_EFL = REGISTER_X86_FPSTACK_7 +1,
  4383.         REGISTER_X86_CS,
  4384.         REGISTER_X86_DS,
  4385.         REGISTER_X86_ES,
  4386.         REGISTER_X86_FS,
  4387.         REGISTER_X86_GS,
  4388.         REGISTER_X86_SS,
  4389.         REGISTER_X86_EFLAGS_CY,
  4390.         REGISTER_X86_EFLAGS_PE,
  4391.         REGISTER_X86_EFLAGS_AC,
  4392.         REGISTER_X86_EFLAGS_ZR,
  4393.         REGISTER_X86_EFLAGS_PL,
  4394.         REGISTER_X86_EFLAGS_EI,
  4395.         REGISTER_X86_EFLAGS_UP,
  4396.         REGISTER_X86_EFLAGS_OV,
  4397.         INVALID_REGISTER
  4398.     };
  4399.  
  4400. #define X86_EFLAGS_CY    SETBITULONG64(0) //Carry Set
  4401. #define X86_EFLAGS_PE    SETBITULONG64(2)    //Parity Even?
  4402. #define X86_EFLAGS_AC    SETBITULONG64(4) //Aux. Carry
  4403. #define X86_EFLAGS_ZR    SETBITULONG64(6) //Zero Set
  4404. #define X86_EFLAGS_PL    SETBITULONG64(7) //Sign positive
  4405. #define X86_EFLAGS_EI    SETBITULONG64(9)    //Enabled Interrupt
  4406. #define X86_EFLAGS_UP    SETBITULONG64(10)    //Direction increment
  4407. #define X86_EFLAGS_OV    SETBITULONG64(11)    //Overflow Set
  4408.  
  4409.  
  4410. #define REGS_PER_LINE 4
  4411. #define WRITE_SPECIAL_REGISTER( shell, pContext, segmentflag, Name, fieldName, nBase, sz ) \
  4412.             if ( (pContext)->ContextFlags & (segmentflag) )              \
  4413.                 (shell)->Write( L#Name L" = %04s",                      \
  4414.                 _itow( (pContext)->##fieldName, sz, (nBase) ) );        \
  4415.             else                                                        \
  4416.                 shell->Write( L#Name L"=<?>" );                         \
  4417.     
  4418.  
  4419. #define WRITE_SPECIAL_BIT_REGISTER( shell, pContext, segmentFlag, fName, Name ) \
  4420.                 if ( (pContext)->ContextFlags & (segmentFlag))           \
  4421.                 {                                                       \
  4422.                     if ( (pContext)->EFlags & (X86_EFLAGS_##fName) )     \
  4423.                         shell->Write( L#Name L" = 1"  );             \
  4424.                     else                                                \
  4425.                         shell->Write( L#Name L" = 0"  );             \
  4426.                 }                                                       \
  4427.                 else                                                    \
  4428.                 {                                                       \
  4429.                     shell->Write( L#Name L"=<?>" );                     \
  4430.                 }                                                       \
  4431.  
  4432.  
  4433.     bool WriteReg( UINT iReg, CORDB_REGISTER *regBuffer, ULONG64 rgfReg, 
  4434.                     CONTEXT *pContext, DebuggerShell *shell)
  4435.     {
  4436.  
  4437. #ifdef _X86_
  4438.         WCHAR rgwszRegName[REGISTER_X86_EFLAGS_OV+1][4] = { L"EIP", L"ESP", 
  4439.                                     L"EBP", L"EAX", L"ECX", 
  4440.                                     L"EDX", L"EBX", L"ESI", L"EDI", L"ST0",
  4441.                                     L"ST1", L"ST2", L"ST3", L"ST4", L"ST5",
  4442.                                     L"ST6", L"ST7", L"EFL", L"CS", L"DS",
  4443.                                     L"ES", L"FS", L"GS", L"SS", L"CY", L"PE",
  4444.                                     L"AC", L"ZR", L"PL", L"EI", L"UP", L"OV"};
  4445.         WCHAR wszTemp[30];
  4446.         int nBase; //base 16 or base 10?
  4447.  
  4448.         _ASSERTE( pContext != NULL );
  4449.         _ASSERTE( regBuffer != NULL );
  4450.         _ASSERTE(sizeof (double) == sizeof (CORDB_REGISTER));
  4451.  
  4452.         if ( shell->m_rgfActiveModes & DSM_DISPLAY_REGISTERS_AS_HEX)
  4453.             nBase = 16;
  4454.         else
  4455.             nBase = 10;
  4456.  
  4457.         switch( iReg )
  4458.         {
  4459.         case REGISTER_X86_EAX:
  4460.         case REGISTER_X86_EBX:
  4461.         case REGISTER_X86_ECX:
  4462.         case REGISTER_X86_EDX:
  4463.         case REGISTER_X86_ESI:
  4464.         case REGISTER_X86_EDI:
  4465.         case REGISTER_X86_EIP:
  4466.         case REGISTER_X86_ESP:
  4467.         case REGISTER_X86_EBP:
  4468.             {
  4469.                 if ( rgfReg & SETBITULONG64(iReg) )
  4470.                     shell->Write( L"%s = %08s",rgwszRegName[iReg], 
  4471.                                 _ui64tow(regBuffer[iReg], wszTemp, nBase) );
  4472.                 else
  4473.                     shell->Write( L"%s=<?>",rgwszRegName[iReg] );
  4474.                 break;
  4475.             }
  4476.         case REGISTER_X86_FPSTACK_0:
  4477.         case REGISTER_X86_FPSTACK_1:
  4478.         case REGISTER_X86_FPSTACK_2:
  4479.         case REGISTER_X86_FPSTACK_3:
  4480.         case REGISTER_X86_FPSTACK_4:
  4481.         case REGISTER_X86_FPSTACK_5:
  4482.         case REGISTER_X86_FPSTACK_6:
  4483.         case REGISTER_X86_FPSTACK_7:
  4484.             {
  4485.                 if ( rgfReg & SETBITULONG64(iReg) )
  4486.                 {
  4487.                     double d = 0.0;
  4488.                     memcpy( &d, &(regBuffer[iReg]), sizeof (double));
  4489.  
  4490.                     shell->Write( L"%s = %.16g", rgwszRegName[iReg], d );
  4491.                 }
  4492.                 else
  4493.                     shell->Write( L"%s=<?>", rgwszRegName[iReg] );
  4494.                 break;
  4495.             }
  4496.  
  4497.         case REGISTER_X86_EFL:
  4498.             {
  4499.                 WRITE_SPECIAL_REGISTER( shell, pContext, CONTEXT_SEGMENTS, EFL, EFlags, nBase, wszTemp )
  4500.                 break;
  4501.             }
  4502.         case REGISTER_X86_CS:
  4503.             {
  4504.                 WRITE_SPECIAL_REGISTER( shell, pContext, CONTEXT_SEGMENTS, CS, SegCs, nBase, wszTemp )
  4505.                 break;
  4506.             }
  4507.         case REGISTER_X86_DS:
  4508.             {
  4509.                 WRITE_SPECIAL_REGISTER( shell, pContext, CONTEXT_SEGMENTS, DS, SegDs, nBase, wszTemp )
  4510.                 break;
  4511.             }
  4512.         case REGISTER_X86_ES:
  4513.             {
  4514.                 WRITE_SPECIAL_REGISTER( shell, pContext, CONTEXT_SEGMENTS, ES, SegEs, nBase, wszTemp )
  4515.                 break;
  4516.             }
  4517.         case REGISTER_X86_SS:
  4518.             {
  4519.                 WRITE_SPECIAL_REGISTER( shell, pContext, CONTEXT_CONTROL, SS, SegSs, nBase, wszTemp )
  4520.                 break;
  4521.             }
  4522.         case REGISTER_X86_FS:
  4523.             {
  4524.                 WRITE_SPECIAL_REGISTER( shell, pContext, CONTEXT_SEGMENTS, FS, SegFs, nBase, wszTemp )
  4525.                 break;
  4526.             }
  4527.         case REGISTER_X86_GS:
  4528.             {
  4529.                 WRITE_SPECIAL_REGISTER( shell, pContext, CONTEXT_SEGMENTS, GS, SegGs, nBase, wszTemp )
  4530.                 break;
  4531.             }
  4532.         case REGISTER_X86_EFLAGS_CY:
  4533.             {
  4534.                 WRITE_SPECIAL_BIT_REGISTER( shell, pContext,  CONTEXT_CONTROL, CY, CY )
  4535.                 break;
  4536.             }
  4537.         case REGISTER_X86_EFLAGS_PE:
  4538.             {
  4539.                 WRITE_SPECIAL_BIT_REGISTER( shell, pContext,  CONTEXT_CONTROL, PE, PE )
  4540.                 break;
  4541.             }
  4542.         case REGISTER_X86_EFLAGS_AC:
  4543.             {
  4544.                 WRITE_SPECIAL_BIT_REGISTER( shell, pContext,  CONTEXT_CONTROL, AC, AC )
  4545.                 break;
  4546.             }
  4547.         case REGISTER_X86_EFLAGS_ZR:
  4548.             {
  4549.                 WRITE_SPECIAL_BIT_REGISTER( shell, pContext,  CONTEXT_CONTROL, ZR, ZR )
  4550.                 break;
  4551.             }
  4552.         case REGISTER_X86_EFLAGS_PL:
  4553.             {
  4554.                 WRITE_SPECIAL_BIT_REGISTER( shell, pContext,  CONTEXT_CONTROL, PL, PL)
  4555.                 break;
  4556.             }
  4557.         case REGISTER_X86_EFLAGS_EI:
  4558.             {
  4559.                 WRITE_SPECIAL_BIT_REGISTER( shell, pContext,  CONTEXT_CONTROL, EI, EI )
  4560.                 break;
  4561.             }
  4562.         case REGISTER_X86_EFLAGS_UP:
  4563.             {
  4564.                 WRITE_SPECIAL_BIT_REGISTER( shell, pContext,  CONTEXT_CONTROL, UP, UP )
  4565.                 break;
  4566.             }
  4567.         case REGISTER_X86_EFLAGS_OV:
  4568.             {
  4569.                 WRITE_SPECIAL_BIT_REGISTER( shell, pContext,  CONTEXT_CONTROL, OV, OV )
  4570.                 break;
  4571.             }
  4572.         default:
  4573.             {
  4574.                 return false;
  4575.             }
  4576.         }
  4577. #else // !_X86_
  4578.         _ASSERTE(!"@TODO Alpha - WriteReg (Commands.cpp)");
  4579. #endif // _X86_
  4580.         return true;
  4581.     }
  4582. }; //RegistersDebuggerCommand 
  4583.  
  4584. #define ON_ERROR_EXIT() if(hr != S_OK) { shell->ReportError(hr); goto done; }
  4585. #define ON_ERROR_BREAK() if(hr != S_OK) { shell->ReportError(hr); break; }
  4586. #define EXIT_WITH_MESSAGE(msg) { shell->Error(msg); goto done; }
  4587. class WTDebuggerCommand : public DebuggerCommand
  4588. {
  4589.     int GetNestingLevel(DebuggerShell * shell)
  4590.     {
  4591.         HRESULT hr;
  4592.         int level = 0;
  4593.         SIZE_T count;
  4594.  
  4595.         ICorDebugChainEnum * ce;
  4596.         ICorDebugChain * chain;
  4597.  
  4598.         hr = shell->m_currentThread->EnumerateChains(&ce);
  4599.         if(hr == S_OK) {
  4600.             while (ce->Next(1, &chain, &count) == S_OK && count == 1)
  4601.             {
  4602.                 BOOL isManaged;
  4603.                 ICorDebugFrameEnum * fe;
  4604.                 ICorDebugFrame * frame;
  4605.  
  4606.                 if (chain->IsManaged(&isManaged) == S_OK && isManaged)
  4607.                 {
  4608.                     if (chain->EnumerateFrames(&fe) == S_OK)
  4609.                     {
  4610.                         while (fe->Next(1, &frame, &count) == S_OK && count == 1)
  4611.                         {
  4612.                             level++;
  4613.                             RELEASE(frame);
  4614.                         }
  4615.                         RELEASE(fe);
  4616.                     }
  4617.                 }
  4618.                 RELEASE(chain);
  4619.             }
  4620.             RELEASE(ce);
  4621.         }
  4622.  
  4623.         return level;
  4624.     }
  4625.  
  4626.     void FormatFunctionName(WCHAR * buffer, ICorDebugFunction * function)
  4627.     {
  4628.         DebuggerFunction * func;
  4629.  
  4630.         func = DebuggerFunction::FromCorDebug(function);
  4631.         if(func) 
  4632.         {
  4633.             wsprintf(buffer, L"%s::%s", func->GetClassName(), func->GetName());
  4634.         } else {
  4635.             lstrcpy(buffer, L"(nowhere::nomethod)");
  4636.         }
  4637.     }
  4638.  
  4639.     void OutputReportLine(DebuggerShell *shell, int startingLevel, int count, WCHAR * funcname)
  4640.     {
  4641.         int level = GetNestingLevel(shell);
  4642.         int i = 0;
  4643.         WCHAR levels[256];
  4644.  
  4645.         while(level-- > startingLevel)
  4646.         {
  4647.             levels[i++] = L' ';
  4648.         }
  4649.         levels[i] = L'\0';
  4650.         shell->Write(L"%8d\t%s%s\n", count, levels, funcname);
  4651.     }
  4652.  
  4653. public:
  4654.     WTDebuggerCommand(const WCHAR *name, int minMatchLength = 0)
  4655.         : DebuggerCommand(name, minMatchLength)
  4656.     {
  4657.     }
  4658.  
  4659.     void Do(DebuggerShell *shell, ICorDebug *cor, const WCHAR *args)
  4660.     {
  4661.         HRESULT hr;
  4662.  
  4663.         ICorDebugCode * corDebugCode    = NULL;
  4664.         ICorDebugFunction * ourFunc     = NULL;
  4665.         ICorDebugFunction * lruFunc     = NULL;
  4666.         BYTE * code                     = NULL;
  4667.         int count                       = 0;
  4668.         int funcCount                   = 0;
  4669.         int ourNestingLevel             = 0;
  4670.         WCHAR funcName[256];
  4671.         ULONG32 codeSize;
  4672.         bool needToSkipCompilerStubs;
  4673.         
  4674.         // Error if no currently running process
  4675.         if (shell->m_currentProcess == NULL)
  4676.             EXIT_WITH_MESSAGE(L"Process not running.\n");
  4677.         
  4678.         // Error if no currently running thread
  4679.         if (shell->m_currentThread == NULL)
  4680.             EXIT_WITH_MESSAGE(L"Thread no longer exists.\n");
  4681.  
  4682.         // See if we have good current frame pointer
  4683.         if (shell->m_currentFrame == NULL) 
  4684.             EXIT_WITH_MESSAGE(L"There is no current frame.\n");
  4685.  
  4686.         // Don't show any tracing activity
  4687.         shell->m_silentTracing = true;
  4688.  
  4689.         // Retrieve code for the current function
  4690.         hr = shell->m_rawCurrentFrame->GetCode(&corDebugCode);
  4691.         ON_ERROR_EXIT();
  4692.  
  4693.         // Retrieve code size
  4694.         hr = corDebugCode->GetSize(&codeSize);
  4695.         if (hr != S_OK || codeSize == 0) 
  4696.             EXIT_WITH_MESSAGE(L"Failure to retrieve function code size.\n");
  4697.  
  4698.         // Prepare buffer for code bytes
  4699.         code = new BYTE[codeSize];
  4700.         if (code == NULL) 
  4701.             EXIT_WITH_MESSAGE(L"Failure to allocate code array.\n");
  4702.  
  4703.         // Grab the code bytes
  4704.         hr = corDebugCode->GetCode(0, codeSize, codeSize, code, &codeSize);
  4705.         ON_ERROR_EXIT();
  4706.  
  4707.         // Remember in what function we started 
  4708.         hr = shell->m_rawCurrentFrame->GetFunction(&ourFunc);
  4709.         ON_ERROR_EXIT();
  4710.  
  4711.         lruFunc = ourFunc;
  4712.         lruFunc->AddRef();
  4713.         FormatFunctionName(funcName, lruFunc);
  4714.         ourNestingLevel = GetNestingLevel(shell);
  4715.  
  4716.         // Turn off compiler stub skipping.
  4717.         needToSkipCompilerStubs = shell->m_needToSkipCompilerStubs;
  4718.         shell->m_needToSkipCompilerStubs = false;
  4719.         
  4720.         // trace to return instruction in current frame
  4721.         for(;;)
  4722.         {
  4723.             ICorDebugStepper * pStepper;
  4724.             ICorDebugFunction * currentFunc;
  4725.  
  4726.             // Count in the instruction we are going to execute
  4727.             count++;
  4728.  
  4729.             // Retrieve function
  4730.             if (shell->m_rawCurrentFrame)
  4731.             {
  4732.                 hr = shell->m_rawCurrentFrame->GetFunction(¤tFunc);
  4733.                 ON_ERROR_BREAK();
  4734.  
  4735.                 // are we now in different function?
  4736.                 if(currentFunc != lruFunc)
  4737.                 {
  4738.                     WCHAR newFuncName[256];
  4739.  
  4740.                     FormatFunctionName(newFuncName, currentFunc);
  4741.  
  4742.                     // If this is new function, print stats and remember new function
  4743.                     if (lstrcmp(newFuncName, funcName) != 0) 
  4744.                     {
  4745.                         OutputReportLine(shell, ourNestingLevel, funcCount, funcName);
  4746.                         lstrcpy(funcName, newFuncName);
  4747.                        
  4748.                         lruFunc->Release();
  4749.                         lruFunc = currentFunc;
  4750.                         lruFunc->AddRef();
  4751.                         funcCount = 0;
  4752.                     }
  4753.  
  4754.                 }
  4755.  
  4756.                 // At least one instruction in this function
  4757.                 funcCount++;
  4758.  
  4759.                 // We won't deref this pointer anymore, just look at its value
  4760.                 currentFunc->Release();
  4761.  
  4762.                 // See if we are at the top level
  4763.                 if (currentFunc == ourFunc)
  4764.                 {
  4765.                     ULONG32 currentIP;
  4766.                     ICorDebugNativeFrame * nativeFrame;
  4767.  
  4768.                     // Obtain IP assuming jitted code
  4769.                     hr = shell->m_rawCurrentFrame->QueryInterface(
  4770.                       IID_ICorDebugNativeFrame,(void **)&nativeFrame);
  4771.                     ON_ERROR_BREAK();
  4772.  
  4773.                     hr = nativeFrame->GetIP(¤tIP);
  4774.                     nativeFrame->Release();
  4775.                     ON_ERROR_BREAK();
  4776.  
  4777.                     // Prevent accesses past the code array boundary
  4778.                     if(currentIP >= codeSize)
  4779.                     {
  4780.                         shell->Error(L"Stepped outside of function.\n");
  4781.                         break;
  4782.                     }
  4783.  
  4784.                     // Get the code byte
  4785.                     BYTE opcode = code[currentIP];
  4786.  
  4787.                     // Detect RET instruction
  4788.                     if (opcode == 0xC3 || opcode == 0xC2 || 
  4789.                         opcode == 0xCA || opcode == 0xCB )
  4790.                     {
  4791.                         //
  4792.                         // Only stop if we are at our nesting level
  4793.                         //
  4794.                         if (ourNestingLevel == GetNestingLevel(shell))
  4795.                             break;
  4796.                     }
  4797.                 }
  4798.             }
  4799.  
  4800.             // Create a stepper based on the current frame or thread
  4801.             if (shell->m_currentFrame != NULL)
  4802.                 hr = shell->m_currentFrame->CreateStepper(&pStepper);
  4803.             else
  4804.                 hr = shell->m_currentThread->CreateStepper(&pStepper);
  4805.             ON_ERROR_BREAK();
  4806.  
  4807.             // Make sure the stepper stops everywhere. Without this,
  4808.             // we 1) don't get an accurate count and 2) can't stop
  4809.             // when we get to the end of the method because we step
  4810.             // over the epilog automagically.
  4811.             hr = pStepper->SetUnmappedStopMask(STOP_ALL);
  4812.             hr = pStepper->SetInterceptMask(INTERCEPT_ALL);
  4813.             
  4814.             // Tell the stepper what to do
  4815.             hr = pStepper->Step(TRUE);
  4816.             ON_ERROR_BREAK();
  4817.  
  4818.             // Indicate the current stepper to the shell
  4819.             shell->StepStart(shell->m_currentThread, pStepper);
  4820.  
  4821.             // Continue the process
  4822.             shell->Run();
  4823.         }
  4824.  
  4825.         // Turn stub skipping back on if necessary.
  4826.         shell->m_needToSkipCompilerStubs = needToSkipCompilerStubs;
  4827.         
  4828.         // Report result
  4829.         OutputReportLine(shell, ourNestingLevel, funcCount, funcName);
  4830.         shell->Write(L"\n%8d instructions total\n", count);
  4831.  
  4832. done:
  4833.         shell->m_silentTracing = false;
  4834.  
  4835.         if (corDebugCode)
  4836.             RELEASE(corDebugCode);
  4837.  
  4838.         if (ourFunc)
  4839.             RELEASE(ourFunc);
  4840.  
  4841.         if (lruFunc)
  4842.             RELEASE(lruFunc);
  4843.  
  4844.         if (code)
  4845.             delete [] code;
  4846.     }
  4847.  
  4848.     // Provide help specific to this command
  4849.     void Help(Shell *shell)
  4850.     {
  4851.         ShellCommand::Help(shell);
  4852.         shell->Write(L"wt");
  4853.         shell->Write(L"\nSingle-steps until current method returns,"
  4854.                      L"\n(skipping unmanaged code)"
  4855.                      L"\nthen prints the resulting instruction count.\n");
  4856.     }
  4857.  
  4858.     const WCHAR *ShortHelp(Shell *shell)
  4859.     {
  4860.         return L"Count instructions while stepping";
  4861.     }
  4862.  
  4863. };
  4864. #undef ON_ERROR_EXIT
  4865. #undef ON_ERROR_BREAK
  4866. #undef EXIT_WITH_MESSAGE
  4867.  
  4868.  
  4869. #define DEFAULT_DUMP_BLOCK_SIZE 128
  4870.  
  4871. class DumpDebuggerCommand : public DebuggerCommand
  4872. {
  4873. public:
  4874.     DumpDebuggerCommand(const WCHAR *name, int minMatchLength = 0)
  4875.         : DebuggerCommand(name, minMatchLength)
  4876.     {
  4877.     }
  4878.  
  4879.     virtual void Do(DebuggerShell *shell, ICorDebug *cor, const WCHAR *args)
  4880.     {
  4881.         int WORD_SIZE = 4;
  4882.         int iMaxOnOneLine = 4;
  4883.  
  4884.         if ( shell->m_currentProcess == NULL )
  4885.         {
  4886.             shell->Error( L"Process not running!\n" );
  4887.             return ;
  4888.         }
  4889.  
  4890.         int        cwch = 80;
  4891.         int     ibPrev;
  4892.         int     iPadding;
  4893.  
  4894.         WCHAR    *wszArgsCopy = (WCHAR *)_alloca( sizeof (WCHAR)*80);
  4895.         if ( wcslen( args ) >= cwch )
  4896.         {
  4897.             shell->Write( L"Mode: input string too long!\n" );
  4898.             return;
  4899.         }
  4900.         wcscpy( wszArgsCopy, args );
  4901.  
  4902.         WCHAR *szAddrTemp = NULL;
  4903.         WCHAR szAddr [20];
  4904.         WCHAR *szByteCount = NULL;
  4905.  
  4906.         CORDB_ADDRESS addr = NULL;
  4907.         SIZE_T  cb = 0;
  4908.  
  4909.         int ib = 0;
  4910.         WCHAR  wsz[20];
  4911.         int nBase;
  4912.  
  4913.         szAddr [0] = L'\0';
  4914.         shell->GetStringArg( wszArgsCopy, szAddrTemp);
  4915.         if ( wszArgsCopy == szAddrTemp )
  4916.         {
  4917.             shell->Write( L"\n Memory address argument is required\n" );
  4918.             return;
  4919.         }
  4920.  
  4921.         // check to see if this is a valid number
  4922.         // if the first digit is '0', then this is an octal or hex number
  4923.  
  4924.         for (int i=0; i<wcslen(szAddrTemp); i++)
  4925.         {
  4926.             if ((szAddrTemp[i] != L' ') &&
  4927.                 (szAddrTemp[i] != L',') && (szAddrTemp[i] != L';'))
  4928.                 szAddr [i] = szAddrTemp [i];
  4929.             else
  4930.                 break;
  4931.         }
  4932.  
  4933.         szAddr [i] = L'\0';
  4934.  
  4935.         if (szAddr [0] == L'0')
  4936.         {
  4937.             if (szAddr [1] == L'x' || szAddr [1] == L'X')
  4938.             {
  4939.                 // it's a hex number
  4940.                 int iIndex = 2;
  4941.                 WCHAR ch;
  4942.                 while ((ch = szAddr [iIndex++]) != L'\0')
  4943.                 {    
  4944.                     if ((ch >= L'0' && ch <= '9')
  4945.                         || (ch >= 'a' && ch <= 'f')
  4946.                         || (ch >= 'A' && ch <= 'F'))
  4947.                     {
  4948.                         continue;
  4949.                     }
  4950.                     goto AddrError;
  4951.                 }
  4952.             }
  4953.             else
  4954.             {
  4955.                 // it's an octal number
  4956.                 int iIndex = 1;
  4957.                 WCHAR ch;
  4958.                 while ((ch = szAddr [iIndex++]) != L'\0')
  4959.                 {    
  4960.                     if (ch >= L'0' && ch <= '7')
  4961.                     {
  4962.                         continue;
  4963.                     }
  4964.                     goto AddrError;
  4965.                 }
  4966.             }
  4967.         }
  4968.         else
  4969.         {
  4970.             // this is a decimal number. Verify.
  4971.             int iIndex = 1;
  4972.             WCHAR ch;
  4973.             while ((ch = szAddr [iIndex++]) != L'\0')
  4974.             {    
  4975.                 if (ch >= L'0' && ch <= '9')
  4976.                 {
  4977.                     continue;
  4978.                 }
  4979.                 goto AddrError;
  4980.             }
  4981.         }
  4982.  
  4983.         WCHAR *pCh;
  4984.         addr = (CORDB_ADDRESS)wcstol( szAddr, &pCh, 0 );
  4985.         if ( wszArgsCopy[0] != NULL )
  4986.         {
  4987.             *(wszArgsCopy++) = NULL;
  4988.         }
  4989.  
  4990.         shell->GetStringArg( wszArgsCopy, szByteCount);
  4991.         if ( wszArgsCopy == szByteCount )
  4992.         {
  4993.             cb = DEFAULT_DUMP_BLOCK_SIZE;
  4994.         }
  4995.         else
  4996.         {
  4997.             cb = (CORDB_ADDRESS)wcstol( szByteCount, &pCh, 0 );
  4998.         }
  4999.  
  5000. AddrError:        
  5001.         if ( addr == 0 )
  5002.         {
  5003.             shell->Write( L"\n Address misformatted or invalid!\n");
  5004.             return;
  5005.         }
  5006.         if ( cb == 0 )
  5007.         {
  5008.             shell->Write( L"\n Byte count misformatted or invalid!\n");
  5009.             return;
  5010.         }
  5011.  
  5012.         // Get the display mode (Byte, dword)
  5013.         if (g_pShell->m_rgfActiveModes & DSM_DUMP_MEMORY_IN_BYTES)
  5014.         {
  5015.             WORD_SIZE = 1;
  5016.             iMaxOnOneLine = 16;
  5017.         }
  5018.  
  5019.         if (cb % WORD_SIZE)
  5020.         {
  5021.             cb += WORD_SIZE - (cb % WORD_SIZE);
  5022.         }
  5023.  
  5024.         _ASSERTE( shell->m_currentProcess != NULL );
  5025.         BYTE *pbMemory = new BYTE [ cb ];
  5026.         if ( pbMemory == NULL )
  5027.         {
  5028.             shell->Write( L"\n Unable to allocate the %d bytes needed!", cb );
  5029.             return;
  5030.         }
  5031.         memset( pbMemory, '?', cb );
  5032.  
  5033.         SIZE_T read = 10;
  5034.         HRESULT hr = shell->m_currentProcess->ReadMemory( addr, cb,
  5035.                                                           pbMemory, &read );
  5036.         if ( !SUCCEEDED( hr ) )
  5037.         {
  5038.             shell->Write( L"\n Couldn't read the asked-for memory" );
  5039.             goto LExit;
  5040.         }
  5041.  
  5042.         if (cb != read )
  5043.         {
  5044.             shell->Write( L"Only able to read %d of the %d requested bytes!\n", read, cb );
  5045.         }
  5046.  
  5047.         
  5048.         if ( shell->m_rgfActiveModes & DSM_DISPLAY_REGISTERS_AS_HEX)
  5049.             nBase = 16;
  5050.         else
  5051.             nBase = 10;
  5052.  
  5053. //everything from here down to LExit is just for pretty-printing
  5054. //@todo port
  5055.  
  5056.         while ( ib < cb )
  5057.         {
  5058.             if ( (ib % (WORD_SIZE * iMaxOnOneLine)) == 0 )
  5059.             {  //beginning or end of line
  5060.                 if ( ib != 0 )
  5061.                 {
  5062.                     if (WORD_SIZE == 1)
  5063.                     {
  5064.                         //end of 2nd+line: spit out bytes in ASCII/Unicode
  5065.                         shell->Write( L"  " );
  5066.                         for ( int ibPrev = ib - (WORD_SIZE*iMaxOnOneLine);ibPrev< ib;ibPrev++)
  5067.                         {
  5068.                             BYTE b = *(pbMemory+ibPrev);
  5069.                             if ( (b < 65 || b > 122) && (b != '?') )//ignore non-printing character
  5070.                                 shell->Write( L"." );
  5071.                             else
  5072.                                 shell->Write( L"%C", b);
  5073.                         }
  5074.                     }
  5075.                 }   //spit out address to be displayed
  5076.                 shell->Write( L"\n%8x", addr+ib );
  5077.             }
  5078.  
  5079.             if ( (ib % WORD_SIZE ) == 0 )
  5080.             { //put spaces between words
  5081.                 shell->Write( L" " );
  5082.             }
  5083.  
  5084.             //print bytes in hex
  5085.             BYTE *pThisByte = pbMemory + ib + ((ib % WORD_SIZE) - WORD_SIZE) + (((2 * WORD_SIZE) - 1) - ((ib % WORD_SIZE) * (WORD_SIZE -1)));
  5086.             _itow( (int)*pThisByte, wsz, nBase);
  5087.  
  5088.             //make sure to always print at least two characters
  5089.             if ( (*(pThisByte) < 0x10 && nBase == 16) ||
  5090.                 (*(pThisByte) < 10 && nBase == 10) )
  5091.                 shell->Write( L"0%s", wsz );
  5092.             else
  5093.                 shell->Write( L"%s", wsz );
  5094.             ib++;
  5095.         }
  5096.         if ( (ib % (WORD_SIZE * iMaxOnOneLine)) != 0 )
  5097.         {   //stopped halfway through last line
  5098.             //put the missing spaces in so this doesn't look weird
  5099.             for ( iPadding = (WORD_SIZE*iMaxOnOneLine)-(ib%(WORD_SIZE*iMaxOnOneLine));iPadding>0;iPadding--)
  5100.             {
  5101.                 if ( (iPadding%WORD_SIZE)==0 )
  5102.                     shell->Write( L" " );
  5103.                 shell->Write( L" " );
  5104.             }
  5105.             shell->Write( L" ");
  5106.         }
  5107.         //print out the characters for the final line
  5108.         ibPrev = ib - (ib%(WORD_SIZE*iMaxOnOneLine));
  5109.         if ( ibPrev == ib ) //we landed on the line edge
  5110.         {
  5111.             ibPrev = ib-(WORD_SIZE*iMaxOnOneLine); 
  5112.             shell->Write( L"  " );
  5113.         }
  5114.  
  5115.         if (WORD_SIZE == 1)
  5116.         {
  5117.             for ( ;ibPrev< ib; ibPrev++)
  5118.             {   
  5119.                 BYTE b = *(pbMemory+ibPrev);
  5120.                 if ( (b < 65 || b > 122) && (b != '?') )
  5121.                     shell->Write( L"." );
  5122.                 else
  5123.                     shell->Write( L"%C", b);
  5124.             }
  5125.         }
  5126. LExit:
  5127.         shell->Write( L"\n" );
  5128.         delete [] pbMemory;
  5129.     }
  5130.  
  5131.     // Provide help specific to this command
  5132.     void Help(Shell *shell)
  5133.     {
  5134.         ShellCommand::Help(shell);
  5135.         shell->Write(L"\n Dump out a block of memory, with the output either in hex" );
  5136.         shell->Write(L"\n or decimal, depending on which mode (see \"mode\") the shell" );
  5137.         shell->Write(L"\n is in.  Requires one argument (the address)," );
  5138.         shell->Write(L"\n and has one optional argument (the number of bytes" );
  5139.         shell->Write(L"\n to print)." );
  5140.         shell->Write(L"\n Example:\n\tdump 03fa 17\n" );
  5141.     }
  5142.  
  5143.     const WCHAR *ShortHelp(Shell *shell)
  5144.     {
  5145.         return L"Dump memory";
  5146.     }
  5147. };
  5148.     
  5149.  
  5150. class WriteMemoryDebuggerCommand : public DebuggerCommand
  5151. {
  5152. public:
  5153.     WriteMemoryDebuggerCommand(const WCHAR *name, int minMatchLength = 0)
  5154.         : DebuggerCommand(name, minMatchLength)
  5155.     {
  5156.     }
  5157.  
  5158.     virtual void Do(DebuggerShell *shell, ICorDebug *cor, const WCHAR *args)
  5159.     {
  5160.         if ( shell->m_currentProcess == NULL )
  5161.         {
  5162.             shell->Error( L"Process not running!\n" );
  5163.             return ;
  5164.         }
  5165.  
  5166.         WCHAR *szAddr = NULL;
  5167.         WCHAR *szRange = NULL;
  5168.         WCHAR *szValue = NULL;
  5169.  
  5170.         CORDB_ADDRESS addr = NULL;
  5171.         UINT    cValue = 0;
  5172.         BYTE    *rgbValue = NULL;
  5173.  
  5174.         HRESULT hr = S_OK;
  5175.         int iFirstRepeated = -1; //-1 => no values yet repeated
  5176.         int iValue =0;
  5177.  
  5178.         SIZE_T written = 10;
  5179.         
  5180.         //get target address
  5181.         shell->GetStringArg( args, szAddr);
  5182.         if ( args == szAddr )
  5183.         {
  5184.             shell->Write( L"\n Memory address argument is required\n" );
  5185.             return;
  5186.         }
  5187.  
  5188.         WCHAR *pCh;
  5189.         addr = (CORDB_ADDRESS)wcstol( szAddr, &pCh, 0 );
  5190.  
  5191.         if ( addr == NULL )
  5192.         {
  5193.             shell->Write( L"\n Address misformatted or invalid!\n");
  5194.             return;
  5195.         }
  5196.  
  5197.         //get count of values
  5198.         shell->GetStringArg( args, szRange);
  5199.         if ( args == szRange )
  5200.         {
  5201.             shell->Write( L"\n Count of Values argument is required\n" );
  5202.             return;
  5203.         }
  5204.         cValue = (CORDB_ADDRESS)wcstol( szRange, &pCh, 0 );
  5205.  
  5206.         if ( cValue == 0 )
  5207.         {
  5208.             shell->Write( L"\n Byte value misformatted or invalid!\n");
  5209.             return;
  5210.         }
  5211.         
  5212.         //get byte-pattern
  5213.         rgbValue = (BYTE *)malloc( sizeof(BYTE) * cValue );
  5214.         if ( rgbValue == NULL )
  5215.         {
  5216.             shell->Write( L"\nCan't allocate enough memory for writing space!\n" );
  5217.             return;
  5218.         }
  5219.     
  5220.         shell->GetStringArg( args, szValue);
  5221.         if ( args == szValue )
  5222.         { 
  5223.             shell->Write( L"\nNeed at least one byte for pattern!\n" );
  5224.             goto LExit;
  5225.         }
  5226.         args = szValue;
  5227.         iFirstRepeated = -1;
  5228.         for ( iValue = 0; iValue< cValue;iValue++)
  5229.         {
  5230.             shell->GetStringArg( args, szValue);
  5231.             if ( args == szValue )
  5232.             {   //ran out of arguments
  5233.                 //this is slow, but how many characters can
  5234.                 //people type?
  5235.                 if ( iFirstRepeated == -1 )
  5236.                 {
  5237.                     iFirstRepeated = 0;
  5238.                 }
  5239.                 rgbValue[iValue] = rgbValue[iFirstRepeated++];
  5240.             }
  5241.             else
  5242.                 rgbValue[iValue] = (CORDB_ADDRESS)wcstol( szValue, &pCh, 0 );
  5243.         }
  5244.  
  5245.         hr = shell->m_currentProcess->WriteMemory( addr, cValue,
  5246.                                                    rgbValue, &written );
  5247.         if ( !SUCCEEDED( hr ) )
  5248.         {
  5249.             shell->Write( L"\n Couldn't write the target memory\n" );
  5250.             goto LExit;
  5251.         }
  5252.  
  5253.         if (cValue != written )
  5254.         {
  5255.             shell->Write( L"Only able to write %d of the %d requested bytes!\n", written, cValue );
  5256.         }
  5257.         
  5258.         _ASSERTE( g_pShell != NULL );
  5259.         g_pShell->m_invalidCache = true;
  5260.  
  5261.         shell->Write( L"\nMemory written!\n" );
  5262. LExit:
  5263.         free( rgbValue );
  5264.     }
  5265.  
  5266.     // Provide help specific to this command
  5267.     void Help(Shell *shell)
  5268.     {
  5269.         ShellCommand::Help(shell);
  5270.         shell->Write(L"\n Write the given bytes into the target process" );
  5271.         shell->Write(L"\n Requires: the address," );
  5272.         shell->Write(L"\n how many bytes to write over, and a ");
  5273.         shell->Write(L"\n list of bytes");
  5274.         shell->Write(L"\n to overwrite memory with.  If the number of ");
  5275.         shell->Write(L"\n bytes in the list is smaller than the second ");
  5276.         shell->Write(L"\n argument, then the byte-list will be wrapped &");
  5277.         shell->Write(L"\n copied again.  If the list is too long, the");
  5278.         shell->Write(L"\n extra bytes will be ignored" );
  5279.         shell->Write(L"\n Example:\n\twritememory 03fa 17 0xae 21\n" );
  5280.     }
  5281.  
  5282.     const WCHAR *ShortHelp(Shell *shell)
  5283.     {
  5284.         return L"Write memory";
  5285.     }
  5286. };
  5287.  
  5288.  
  5289. class AssociateSourceFileCommand: public DebuggerCommand
  5290. {
  5291. public:
  5292.     AssociateSourceFileCommand(const WCHAR *name, int minMatchLength = 0)
  5293.         : DebuggerCommand(name, minMatchLength)
  5294.     {
  5295.     }
  5296.  
  5297.     virtual void Do(DebuggerShell *shell, ICorDebug *cor, const WCHAR *args)
  5298.     {
  5299.         // Get the file name to associate
  5300.         WCHAR* fileName = NULL;
  5301.         WCHAR* cmdName = NULL;
  5302.         int        iCount;
  5303.  
  5304.         // the first character in args should be "b" or "s" and the next char
  5305.         // should be a blank
  5306.         if (wcslen(args))
  5307.         {
  5308.             if (((args [0] == L'b') || (args [0] == L's')) && (args [1]==L' '))
  5309.             {
  5310.                 if (args [0] == L'b')
  5311.                 {
  5312.                     args += 2;
  5313.  
  5314.                     // breakpoint
  5315.                     if (!shell->GetIntArg(args, iCount))
  5316.                     {
  5317.                         Help(shell);
  5318.                         return;
  5319.                     }
  5320.  
  5321.                     shell->GetStringArg (args, fileName);
  5322.  
  5323.                     if (wcslen(fileName) != 0)
  5324.                     {
  5325.                         DebuggerBreakpoint *breakpoint = shell->FindBreakpoint(iCount);
  5326.  
  5327.                         if (breakpoint != NULL)
  5328.                         {
  5329.                             breakpoint->ChangeSourceFile (fileName);
  5330.                         }                    
  5331.                         else
  5332.                             shell->Error (L"Breakpoint with this id does not exist.\n");
  5333.                     }
  5334.                     else
  5335.                     {
  5336.                         Help(shell);
  5337.                     }
  5338.                 }
  5339.                 else    // stack frame
  5340.                 {
  5341.                     args += 2;
  5342.  
  5343.                     // get file name
  5344.                     shell->GetStringArg (args, fileName);
  5345.  
  5346.                     if (wcslen(fileName) != 0)
  5347.                     {
  5348.                         shell->ChangeCurrStackFile (fileName);
  5349.                     }
  5350.                     else
  5351.                     {
  5352.                         Help(shell);
  5353.                     }
  5354.                 }
  5355.             }
  5356.             else
  5357.                 Help(shell);
  5358.         }
  5359.         else
  5360.             Help(shell);
  5361.     }
  5362.  
  5363.     // Provide help specific to this command
  5364.     void Help(Shell *shell)
  5365.     {
  5366.         ShellCommand::Help(shell);
  5367.         shell->Write(L"\n");
  5368.         shell->Write(L"Associates the given filename with the specified breakpoint or current stack frame pointer.\n");
  5369.         shell->Write(L"as [s|b] [<breakpoint number>] filename\n");
  5370.         shell->Write(L"For eg., \n");
  5371.         shell->Write(L"as s d:\\com99\\src\\foo.cpp\n");
  5372.         shell->Write(L"associates the given source file with the current stack frame.\n");
  5373.     }
  5374.  
  5375.     const WCHAR *ShortHelp(Shell *shell)
  5376.     {
  5377.         return L"Locate source files";
  5378.     }
  5379. };
  5380.  
  5381. class FuncEvalDebuggerCommand : public DebuggerCommand
  5382. {
  5383. public:
  5384.     FuncEvalDebuggerCommand(const WCHAR *name, int minMatchLength = 0)
  5385.         : DebuggerCommand(name, minMatchLength)
  5386.     {
  5387.     }
  5388.  
  5389.     virtual void Do(DebuggerShell *shell, ICorDebug *cor, const WCHAR *args)
  5390.     {
  5391.         if (shell->m_currentProcess == NULL)
  5392.         {
  5393.             shell->Error(L"Process not running.\n");
  5394.             return;
  5395.         }
  5396.  
  5397.         if (shell->m_currentThread == NULL)
  5398.         {
  5399.             shell->Error(L"No current thread.\n");
  5400.             return;
  5401.         }
  5402.  
  5403.         // Grab the method name.
  5404.         WCHAR *methodName = NULL;
  5405.  
  5406.         shell->GetStringArg(args, methodName);
  5407.  
  5408.         if (wcslen(methodName) == 0)
  5409.         {
  5410.             shell->Error(L"Function name is required.\n");
  5411.             return;
  5412.         }
  5413.  
  5414.         // Null terminate the method name.
  5415.         if (*args)
  5416.             *((WCHAR*)args++) = L'\0';
  5417.  
  5418.         // Create the eval object.
  5419.         ICorDebugEval *pEval = NULL;
  5420.         
  5421.         HRESULT hr =
  5422.             shell->m_currentThread->CreateEval(&pEval);
  5423.  
  5424.         if (FAILED(hr))
  5425.         {
  5426.             shell->Error(L"CreateEval failed.\n");
  5427.             shell->ReportError(hr);
  5428.             goto ErrExit;
  5429.         }
  5430.  
  5431.          // Grab each argument.
  5432.         unsigned int argCount;
  5433.         ICorDebugValue *argArray[256];
  5434.  
  5435.         argCount = 0;
  5436.         
  5437.         while (*args)
  5438.         {
  5439.             if (argCount >= 256)
  5440.             {
  5441.                 shell->Error(L"Too many arguments to function.\n");
  5442.                 goto ErrExit;
  5443.             }
  5444.             
  5445.             WCHAR *argName;
  5446.             shell->GetStringArg(args, argName);
  5447.  
  5448.             if (*args)
  5449.                 *((WCHAR*)args++) = L'\0';
  5450.  
  5451.             argArray[argCount] =
  5452.                 shell->EvaluateExpression(argName,
  5453.                                           shell->m_currentFrame,
  5454.                                           true);
  5455.  
  5456.             // If that didn't work, then see if its a literal value.
  5457.             // @todo: this is only gonna do I4's and NULL for now...
  5458.             if (argArray[argCount] == NULL)
  5459.             {
  5460.                 unsigned int genVal4;
  5461.                 unsigned __int64 genVal8;
  5462.                 void *pNewVal;
  5463.                 bool isNullRef = false;
  5464.  
  5465.                 if ((argName[0] == L'n') ||
  5466.                     (argName[0] == L'N'))
  5467.                 {
  5468.                     // Create a null reference.
  5469.                     isNullRef = true;
  5470.                     
  5471.                     hr = pEval->CreateValue(ELEMENT_TYPE_CLASS,
  5472.                                             NULL,
  5473.                                             &(argArray[argCount]));
  5474.                 }
  5475.                 else
  5476.                 {
  5477.                     if (!shell->GetInt64Arg(argName, genVal8))
  5478.                     {
  5479.                         shell->Error(L"Argument '%s' could not be evaluated\n",
  5480.                                      argName);
  5481.                         goto ErrExit;
  5482.                     }
  5483.  
  5484.                     // Make sure it will fit in an I4.
  5485.                     if (genVal8 <= 0xFFFFFFFF)
  5486.                     {
  5487.                         genVal4 = (unsigned int)genVal8;
  5488.                         pNewVal = &genVal4;
  5489.                     }
  5490.                     else
  5491.                     {
  5492.                         shell->Error(L"The value 0x%08x is too large.\n",
  5493.                                      genVal8);
  5494.                         goto ErrExit;
  5495.                     }
  5496.  
  5497.                     // Create a literal.
  5498.                     hr = pEval->CreateValue(ELEMENT_TYPE_I4,
  5499.                                             NULL,
  5500.                                             &(argArray[argCount]));
  5501.                 }
  5502.                 
  5503.                 if (FAILED(hr))
  5504.                 {
  5505.                     shell->Error(L"CreateValue failed.\n");
  5506.                     shell->ReportError(hr);
  5507.                     goto ErrExit;
  5508.                 }
  5509.  
  5510.                 if (!isNullRef)
  5511.                 {
  5512.                     ICorDebugGenericValue *pGenValue;
  5513.                 
  5514.                     hr = argArray[argCount]->QueryInterface(
  5515.                                                    IID_ICorDebugGenericValue,
  5516.                                                    (void**)&pGenValue);
  5517.                     _ASSERTE(SUCCEEDED(hr));
  5518.                 
  5519.                     // Set the literal value.
  5520.                     hr = pGenValue->SetValue(pNewVal);
  5521.  
  5522.                     pGenValue->Release();
  5523.                 
  5524.                     if (FAILED(hr))
  5525.                     {
  5526.                         shell->Error(L"SetValue failed.\n");
  5527.                         shell->ReportError(hr);
  5528.                         goto ErrExit;
  5529.                     }
  5530.                 }
  5531.             }
  5532.  
  5533.             argCount++;
  5534.         }
  5535.  
  5536.         // Find the function by name.
  5537.         ICorDebugFunction *pFunc;
  5538.         
  5539.         hr = shell->ResolveFullyQualifiedMethodName(methodName,
  5540.                                                     &pFunc);
  5541.  
  5542.         if (FAILED(hr))
  5543.         {
  5544.             shell->Error(L"Could not find function: %s\n", methodName);
  5545.  
  5546.             if (hr != E_INVALIDARG)
  5547.                 shell->ReportError(hr);
  5548.  
  5549.             goto ErrExit;
  5550.         }
  5551.  
  5552.         // Call the function. No args for now.
  5553.         hr = pEval->CallFunction(pFunc, argCount, argArray);
  5554.  
  5555.         pFunc->Release();
  5556.         
  5557.         if (FAILED(hr))
  5558.         {
  5559.             shell->Error(L"CallFunction failed.\n");
  5560.             shell->ReportError(hr);
  5561.  
  5562.             pEval->Release();
  5563.             
  5564.             goto ErrExit;
  5565.         }
  5566.  
  5567.         shell->m_pCurrentEval = pEval;
  5568.         
  5569.         // Let the process run. We'll let the callback cleanup the
  5570.         // func eval on this thread.
  5571.         shell->Run();
  5572.  
  5573.     ErrExit:
  5574.         if (FAILED(hr) && pEval)
  5575.             pEval->Release();
  5576.     }
  5577.  
  5578.     // Provide help specific to this command
  5579.     void Help(Shell *shell)
  5580.     {
  5581.         ShellCommand::Help(shell);
  5582.         shell->Write(L" [<classname>::]<function name> [<arg0> <arg1> ...]\n");
  5583.         shell->Write(L"Evaluate a function on the current thread.\n");
  5584.     }
  5585.  
  5586.     const WCHAR *ShortHelp(Shell *shell)
  5587.     {
  5588.         return L"Function evaluation";
  5589.     }
  5590. };
  5591.  
  5592. class NewStringDebuggerCommand : public DebuggerCommand
  5593. {
  5594. public:
  5595.     NewStringDebuggerCommand(const WCHAR *name, int minMatchLength = 0)
  5596.         : DebuggerCommand(name, minMatchLength)
  5597.     {
  5598.     }
  5599.  
  5600.     virtual void Do(DebuggerShell *shell, ICorDebug *cor, const WCHAR *args)
  5601.     {
  5602.         if (shell->m_currentProcess == NULL)
  5603.         {
  5604.             shell->Error(L"Process not running.\n");
  5605.             return;
  5606.         }
  5607.  
  5608.         if (shell->m_currentThread == NULL)
  5609.         {
  5610.             shell->Error(L"No current thread.\n");
  5611.             return;
  5612.         }
  5613.  
  5614.         // Create the eval.
  5615.         ICorDebugEval *pEval = NULL;
  5616.         
  5617.         HRESULT hr = shell->m_currentThread->CreateEval(&pEval);
  5618.  
  5619.         if (FAILED(hr))
  5620.         {
  5621.             shell->Error(L"CreateEval failed.\n");
  5622.             shell->ReportError(hr);
  5623.             return;
  5624.         }
  5625.  
  5626.         // Create the string
  5627.         hr = pEval->NewString(args);
  5628.  
  5629.         if (FAILED(hr))
  5630.         {
  5631.             shell->Error(L"CreateString failed.\n");
  5632.             shell->ReportError(hr);
  5633.  
  5634.             pEval->Release();
  5635.             
  5636.             return;
  5637.         }
  5638.  
  5639.         // Let the process run. We'll let the callback cleanup the
  5640.         // func eval on this thread.
  5641.         shell->Run();
  5642.     }
  5643.  
  5644.     // Provide help specific to this command
  5645.     void Help(Shell *shell)
  5646.     {
  5647.         ShellCommand::Help(shell);
  5648.         shell->Write(L" <string>\n");
  5649.         shell->Write(L"Create a new string using the current thread.\n");
  5650.     }
  5651.  
  5652.     const WCHAR *ShortHelp(Shell *shell)
  5653.     {
  5654.         return L"Create a new string via function evaluation";
  5655.     }
  5656. };
  5657.  
  5658. class NewObjectDebuggerCommand : public DebuggerCommand
  5659. {
  5660. public:
  5661.     NewObjectDebuggerCommand(const WCHAR *name, int minMatchLength = 0)
  5662.         : DebuggerCommand(name, minMatchLength)
  5663.     {
  5664.     }
  5665.  
  5666.     virtual void Do(DebuggerShell *shell, ICorDebug *cor, const WCHAR *args)
  5667.     {
  5668.         if (shell->m_currentProcess == NULL)
  5669.         {
  5670.             shell->Error(L"Process not running.\n");
  5671.             return;
  5672.         }
  5673.  
  5674.         if (shell->m_currentThread == NULL)
  5675.         {
  5676.             shell->Error(L"No current thread.\n");
  5677.             return;
  5678.         }
  5679.  
  5680.         // Grab the method name.
  5681.         WCHAR *methodName = NULL;
  5682.  
  5683.         shell->GetStringArg(args, methodName);
  5684.  
  5685.         if (wcslen(methodName) == 0)
  5686.         {
  5687.             shell->Error(L"Class name is required.\n");
  5688.             return;
  5689.         }
  5690.  
  5691.         // Null terminate the method name.
  5692.         if (*args)
  5693.             *((WCHAR*)args++) = L'\0';
  5694.         
  5695.         // Grab each argument.
  5696.         unsigned int argCount = 0;
  5697.         ICorDebugValue *argArray[256];
  5698.         
  5699.         while (*args)
  5700.         {
  5701.             if (argCount >= 256)
  5702.             {
  5703.                 shell->Error(L"Too many arguments to function.\n");
  5704.                 return;
  5705.             }
  5706.             
  5707.             WCHAR *argName;
  5708.             shell->GetStringArg(args, argName);
  5709.  
  5710.             if (*args)
  5711.                 *((WCHAR*)args++) = L'\0';
  5712.  
  5713.             argArray[argCount] =
  5714.                 shell->EvaluateExpression(argName, shell->m_currentFrame);
  5715.  
  5716.             if (argArray[argCount] == NULL)
  5717.             {
  5718.                 shell->Error(L"Argument '%s' could not be evaluated.\n",
  5719.                              argName);
  5720.                 return;
  5721.             }
  5722.  
  5723.             argCount++;
  5724.         }
  5725.  
  5726.         ICorDebugEval *pEval = NULL;
  5727.         
  5728.         HRESULT hr =
  5729.             shell->m_currentThread->CreateEval(&pEval);
  5730.  
  5731.         if (FAILED(hr))
  5732.         {
  5733.             shell->Error(L"CreateEval failed.\n");
  5734.             shell->ReportError(hr);
  5735.             return;
  5736.         }
  5737.  
  5738.         // Find the constructor by name.
  5739.         WCHAR consName[256];
  5740.         swprintf(consName, L"%s::%S",
  5741.                  methodName,
  5742.                  COR_CTOR_METHOD_NAME);
  5743.  
  5744.         ICorDebugFunction *pFunc = NULL;
  5745.         
  5746.         hr = shell->ResolveFullyQualifiedMethodName(consName, &pFunc);
  5747.  
  5748.         if (FAILED(hr))
  5749.         {
  5750.             shell->Error(L"Could not find class: %s\n", methodName);
  5751.  
  5752.             if (hr != E_INVALIDARG)
  5753.                 shell->ReportError(hr);
  5754.  
  5755.             return;
  5756.         }
  5757.  
  5758.         // Call the function. No args for now.
  5759.         hr = pEval->NewObject(pFunc, argCount, argArray);
  5760.  
  5761.         pFunc->Release();
  5762.         
  5763.         if (FAILED(hr))
  5764.         {
  5765.             shell->Error(L"NewObject failed.\n");
  5766.             shell->ReportError(hr);
  5767.  
  5768.             pEval->Release();
  5769.             
  5770.             return;
  5771.         }
  5772.  
  5773.         // Let the process run. We'll let the callback cleanup the
  5774.         // func eval on this thread.
  5775.         shell->Run();
  5776.     }
  5777.  
  5778.     // Provide help specific to this command
  5779.     void Help(Shell *shell)
  5780.     {
  5781.         ShellCommand::Help(shell);
  5782.         shell->Write(L" <classname>\n");
  5783.         shell->Write(L"Create a new object using the current thread.\n");
  5784.         shell->Write(L"Don't supply a 'this' argument. A new object\n");
  5785.         shell->Write(L"will be created and the default constructor run.\n");
  5786.     }
  5787.  
  5788.     const WCHAR *ShortHelp(Shell *shell)
  5789.     {
  5790.         return L"Create a new object via function evaluation";
  5791.     }
  5792. };
  5793.  
  5794. class NewObjectNCDebuggerCommand : public DebuggerCommand
  5795. {
  5796. public:
  5797.     NewObjectNCDebuggerCommand(const WCHAR *name, int minMatchLength = 0)
  5798.         : DebuggerCommand(name, minMatchLength)
  5799.     {
  5800.     }
  5801.  
  5802.     virtual void Do(DebuggerShell *shell, ICorDebug *cor, const WCHAR *args)
  5803.     {
  5804.         if (shell->m_currentProcess == NULL)
  5805.         {
  5806.             shell->Error(L"Process not running.\n");
  5807.             return;
  5808.         }
  5809.  
  5810.         if (shell->m_currentThread == NULL)
  5811.         {
  5812.             shell->Error(L"No current thread.\n");
  5813.             return;
  5814.         }
  5815.  
  5816.         // Grab the class name.
  5817.         WCHAR *methodName = NULL;
  5818.  
  5819.         shell->GetStringArg(args, methodName);
  5820.  
  5821.         if (wcslen(methodName) == 0)
  5822.         {
  5823.             shell->Error(L"Class name is required.\n");
  5824.             return;
  5825.         }
  5826.  
  5827.         // Null terminate the method name.
  5828.         if (*args)
  5829.             *((WCHAR*)args++) = L'\0';
  5830.         
  5831.         ICorDebugEval *pEval = NULL;
  5832.         
  5833.         HRESULT hr =
  5834.             shell->m_currentThread->CreateEval(&pEval);
  5835.  
  5836.         if (FAILED(hr))
  5837.         {
  5838.             shell->Error(L"CreateEval failed.\n");
  5839.             shell->ReportError(hr);
  5840.             return;
  5841.         }
  5842.  
  5843.         // Find the class by name.
  5844.         DebuggerModule *pDM;
  5845.         mdTypeDef td;
  5846.         
  5847.         hr = shell->ResolveClassName(methodName, &pDM, &td);
  5848.  
  5849.         if (FAILED(hr))
  5850.         {
  5851.             shell->Error(L"Could not find class: %s\n", methodName);
  5852.  
  5853.             if (hr != E_INVALIDARG)
  5854.                 shell->ReportError(hr);
  5855.  
  5856.             return;
  5857.         }
  5858.         
  5859.         ICorDebugClass *pClass = NULL;
  5860.         hr = pDM->GetICorDebugModule()->GetClassFromToken(td, &pClass);
  5861.  
  5862.         if (FAILED(hr))
  5863.         {
  5864.             shell->Error(L"Could not find class: %s\n", methodName);
  5865.  
  5866.             if (hr != E_INVALIDARG)
  5867.                 shell->ReportError(hr);
  5868.  
  5869.             return;
  5870.         }
  5871.         
  5872.         // Call the function. No args for now.
  5873.         hr = pEval->NewObjectNoConstructor(pClass);
  5874.  
  5875.         pClass->Release();
  5876.         
  5877.         if (FAILED(hr))
  5878.         {
  5879.             shell->Error(L"NewObjectNoConstructor failed.\n");
  5880.             shell->ReportError(hr);
  5881.  
  5882.             pEval->Release();
  5883.             
  5884.             return;
  5885.         }
  5886.  
  5887.         // Let the process run. We'll let the callback cleanup the
  5888.         // func eval on this thread.
  5889.         shell->Run();
  5890.     }
  5891.  
  5892.     // Provide help specific to this command
  5893.     void Help(Shell *shell)
  5894.     {
  5895.         ShellCommand::Help(shell);
  5896.         shell->Write(L" <classname>\n");
  5897.         shell->Write(L"Create a new object using the current thread.\n");
  5898.         shell->Write(L"Don't supply a 'this' argument. A new object\n");
  5899.         shell->Write(L"will be created.\n");
  5900.     }
  5901.  
  5902.     const WCHAR *ShortHelp(Shell *shell)
  5903.     {
  5904.         return L"Create a new object via function evaluation, no constructor";
  5905.     }
  5906. };
  5907.  
  5908. class SetValueDebuggerCommand : public DebuggerCommand
  5909. {
  5910. public:
  5911.     SetValueDebuggerCommand(const WCHAR *name, int minMatchLength = 0)
  5912.         : DebuggerCommand(name, minMatchLength)
  5913.     {
  5914.     }
  5915.  
  5916.     virtual void Do(DebuggerShell *shell, ICorDebug *cor, const WCHAR *args)
  5917.     {
  5918.         ICorDebugValue *ivalue = NULL;
  5919.         ICorDebugGenericValue *pGenValue = NULL;
  5920.         ICorDebugReferenceValue *pRefValue = NULL;
  5921.         HRESULT hr = S_OK;
  5922.  
  5923.         if (shell->m_currentProcess == NULL)
  5924.         {
  5925.             shell->Error(L"Process not running.\n");
  5926.             goto Exit;
  5927.         }
  5928.  
  5929.         if (shell->m_currentThread == NULL)
  5930.         {
  5931.             shell->Error(L"No current thread.\n");
  5932.             goto Exit;
  5933.         }
  5934.  
  5935.         // Get the name of the variable to print.
  5936.         WCHAR* varName;
  5937.         shell->GetStringArg(args, varName);
  5938.  
  5939.         if ((args - varName) == 0)
  5940.         {
  5941.             shell->Error(L"A variable name is required.\n");
  5942.             goto Exit;
  5943.         }
  5944.         
  5945.         WCHAR *varNameEnd;
  5946.         varNameEnd = (WCHAR*) args;
  5947.             
  5948.         // Get the value to set the variable to.
  5949.         WCHAR *valString;
  5950.         shell->GetStringArg(args, valString);
  5951.  
  5952.         if ((args - valString) == 0)
  5953.         {
  5954.             shell->Error(L"A value is required.\n");
  5955.             goto Exit;
  5956.         }
  5957.  
  5958.         *varNameEnd = L'\0';
  5959.  
  5960.         // Get the value for the name provided
  5961.         ivalue = shell->EvaluateExpression(varName, shell->m_currentFrame);
  5962.  
  5963.         // If the name provided is valid, print it!
  5964.         if (ivalue == NULL)
  5965.         {
  5966.             shell->Error(L"Variable unavailable, or not valid\n");
  5967.             goto Exit;
  5968.         }
  5969.  
  5970.         // Grab the element type of this value...
  5971.         CorElementType type;
  5972.         hr = ivalue->GetType(&type);
  5973.  
  5974.         if (FAILED(hr))
  5975.         {
  5976.             shell->Error(L"Problem accessing type info of the variable.\n");
  5977.             shell->ReportError(hr);
  5978.             goto Exit;
  5979.         }
  5980.  
  5981.         // Update the variable with the new value. We get the value
  5982.         // converted to whatever we need it to be then we call
  5983.         // SetValue with that. There are a lot of possibilities for
  5984.         // what the proper form of the value could be...
  5985.         void *pNewVal;
  5986.         
  5987.         // Get the specific kind of value we have, generic or reference.
  5988.         hr = ivalue->QueryInterface(IID_ICorDebugGenericValue,
  5989.                                     (void**)&pGenValue);
  5990.  
  5991.         if (FAILED(hr))
  5992.         {
  5993.             hr = ivalue->QueryInterface(IID_ICorDebugReferenceValue,
  5994.                                         (void**)&pRefValue);
  5995.  
  5996.             _ASSERTE(SUCCEEDED(hr));
  5997.         }
  5998.             
  5999.         unsigned char    genVal1;
  6000.         unsigned short   genVal2;
  6001.         unsigned int     genVal4;
  6002.         unsigned __int64 genVal8;
  6003.         float            genValR4;
  6004.         double           genValR8;
  6005.         CORDB_ADDRESS    refVal;
  6006.  
  6007.         // Only need to pre-init these two, since all others are
  6008.         // copied from these.
  6009.         genVal8 = 0;
  6010.         genValR8 = 0;
  6011.  
  6012.         // Could the value be another variable? (A little eaiser to
  6013.         // check than looking for a literal.)
  6014.         ICorDebugValue *pAnotherVarValue;
  6015.         pAnotherVarValue = shell->EvaluateExpression(valString,
  6016.                                                      shell->m_currentFrame,
  6017.                                                      true);
  6018.  
  6019.         if (pAnotherVarValue != NULL)
  6020.         {
  6021.             // Ah, it is another variable. Lets grab the value. Is it
  6022.             // a generic value or a reference value?
  6023.             ICorDebugGenericValue *pAnotherGenValue;
  6024.             hr = pAnotherVarValue->QueryInterface(IID_ICorDebugGenericValue,
  6025.                                                   (void**)&pAnotherGenValue);
  6026.             if (SUCCEEDED(hr))
  6027.             {
  6028.                 RELEASE(pAnotherVarValue);
  6029.  
  6030.                 // How big is this thing?
  6031.                 ULONG32 valSize;
  6032.                 hr = pAnotherGenValue->GetSize(&valSize);
  6033.  
  6034.                 if (SUCCEEDED(hr))
  6035.                 {
  6036.                     pNewVal = _alloca(valSize);
  6037.  
  6038.                     hr = pAnotherGenValue->GetValue(pNewVal);
  6039.                 }
  6040.  
  6041.                 RELEASE(pAnotherGenValue);
  6042.             }
  6043.             else
  6044.             {
  6045.                 ICorDebugReferenceValue *pAnotherRefValue;
  6046.                 hr = pAnotherVarValue->QueryInterface(
  6047.                                             IID_ICorDebugReferenceValue,
  6048.                                             (void**)&pAnotherRefValue);
  6049.                 RELEASE(pAnotherVarValue);
  6050.  
  6051.                 // If its not a generic value, it had better be a
  6052.                 // reference value.
  6053.                 _ASSERTE(SUCCEEDED(hr));
  6054.  
  6055.                 // Grab the value.
  6056.                 hr = pAnotherRefValue->GetValue(&refVal);
  6057.                 RELEASE(pAnotherRefValue);
  6058.             }
  6059.  
  6060.             if (FAILED(hr))
  6061.             {
  6062.                 shell->Error(L"Error accessing new variable.\n");
  6063.                 shell->ReportError(hr);
  6064.                 goto Exit;
  6065.             }
  6066.         }
  6067.         else
  6068.         {
  6069.             // Must be some type of literal...
  6070.             switch (type)
  6071.             {
  6072.             case ELEMENT_TYPE_BOOLEAN:
  6073.                 _ASSERTE(pGenValue != NULL);
  6074.  
  6075.                 if ((valString[0] == L't') || (valString[0] == L'T'))
  6076.                 {
  6077.                     genVal1 = 1;
  6078.                     pNewVal = &genVal1;
  6079.                 }
  6080.                 else if ((valString[0] == L'f') || (valString[0] == L'F'))
  6081.                 {
  6082.                     genVal1 = 0;
  6083.                     pNewVal = &genVal1;
  6084.                 }
  6085.                 else
  6086.                 {
  6087.                     shell->Error(L"The value should be 'true' or 'false'\n");
  6088.                     goto Exit;
  6089.                 }
  6090.  
  6091.                 break;
  6092.                 
  6093.             case ELEMENT_TYPE_I1:
  6094.             case ELEMENT_TYPE_U1:
  6095.                 _ASSERTE(pGenValue != NULL);
  6096.  
  6097.                 if (!shell->GetInt64Arg(valString, genVal8))
  6098.                 {
  6099.                     shell->Error(L"The value must be a number.\n");
  6100.                     goto Exit;
  6101.                 }
  6102.  
  6103.                 if (genVal8 <= 0xFF)
  6104.                 {
  6105.                     genVal1 = (unsigned char)genVal8;
  6106.                     pNewVal = &genVal1;
  6107.                 }
  6108.                 else
  6109.                 {
  6110.                     shell->Error(L"The value 0x%08x is too large.\n",
  6111.                                  genVal8);
  6112.                     goto Exit;
  6113.                 }
  6114.  
  6115.                 break;
  6116.  
  6117.             case ELEMENT_TYPE_CHAR:
  6118.                 _ASSERTE(pGenValue != NULL);
  6119.  
  6120.                 if ((valString[0] == L'\'') && (valString[1] != L'\0'))
  6121.                 {
  6122.                     genVal2 = valString[1];
  6123.                     pNewVal = &genVal2;
  6124.                 }
  6125.                 else
  6126.                 {
  6127.                     shell->Error(L"The value is not a character literal.\n");
  6128.                     goto Exit;
  6129.                 }
  6130.  
  6131.                 break;
  6132.                 
  6133.             case ELEMENT_TYPE_I2:
  6134.             case ELEMENT_TYPE_U2:
  6135.                 _ASSERTE(pGenValue != NULL);
  6136.  
  6137.                 if (!shell->GetInt64Arg(valString, genVal8))
  6138.                 {
  6139.                     shell->Error(L"The value must be a number.\n");
  6140.                     goto Exit;
  6141.                 }
  6142.  
  6143.                 if (genVal8 <= 0xFFFF)
  6144.                 {
  6145.                     genVal2 = (unsigned short)genVal8;
  6146.                     pNewVal = &genVal2;
  6147.                 }
  6148.                 else
  6149.                 {
  6150.                     shell->Error(L"The value 0x%08x is too large.\n",
  6151.                                  genVal8);
  6152.                     goto Exit;
  6153.                 }
  6154.  
  6155.                 break;
  6156.  
  6157.             case ELEMENT_TYPE_I4:
  6158.             case ELEMENT_TYPE_U4:
  6159.                 _ASSERTE(pGenValue != NULL);
  6160.  
  6161.                 if (!shell->GetInt64Arg(valString, genVal8))
  6162.                 {
  6163.                     shell->Error(L"The value must be a number.\n");
  6164.                     goto Exit;
  6165.                 }
  6166.  
  6167.                 if (genVal8 <= 0xFFFFFFFF)
  6168.                 {
  6169.                     genVal4 = (unsigned int)genVal8;
  6170.                     pNewVal = &genVal4;
  6171.                 }
  6172.                 else
  6173.                 {
  6174.                     shell->Error(L"The value 0x%08x is too large.\n",
  6175.                                  genVal8);
  6176.                     goto Exit;
  6177.                 }
  6178.  
  6179.                 break;
  6180.  
  6181.             case ELEMENT_TYPE_I8:
  6182.             case ELEMENT_TYPE_U8:
  6183.                 _ASSERTE(pGenValue != NULL);
  6184.  
  6185.                 if (!shell->GetInt64Arg(valString, genVal8))
  6186.                 {
  6187.                     shell->Error(L"The value must be a number.\n");
  6188.                     goto Exit;
  6189.                 }
  6190.                 
  6191.                 pNewVal = &genVal8;
  6192.  
  6193.                 break;
  6194.  
  6195.             case ELEMENT_TYPE_R4:
  6196.                 _ASSERTE(pGenValue != NULL);
  6197.  
  6198.                 if (!iswdigit(valString[0]))
  6199.                 {
  6200.                     shell->Error(L"The value must be a number.\n");
  6201.                     goto Exit;
  6202.                 }
  6203.  
  6204.                 genValR8 = wcstod(valString, NULL);
  6205.                 genValR4 = (float) genValR8;
  6206.                 pNewVal = &genValR4;
  6207.  
  6208.                 break;
  6209.                 
  6210.             case ELEMENT_TYPE_R8:
  6211.                 _ASSERTE(pGenValue != NULL);
  6212.  
  6213.                 if (!iswdigit(valString[0]))
  6214.                 {
  6215.                     shell->Error(L"The value must be a number.\n");
  6216.                     goto Exit;
  6217.                 }
  6218.  
  6219.                 genValR8 = wcstod(valString, NULL);
  6220.                 pNewVal = &genValR8;
  6221.  
  6222.                 break;
  6223.  
  6224.             case ELEMENT_TYPE_CLASS:
  6225.             case ELEMENT_TYPE_STRING:
  6226.             case ELEMENT_TYPE_SZARRAY:
  6227.             case ELEMENT_TYPE_ARRAY:
  6228.             case ELEMENT_TYPE_GENERICARRAY:
  6229.                 _ASSERTE(pRefValue != NULL);
  6230.  
  6231.                 if (!shell->GetInt64Arg(valString, genVal8))
  6232.                 {
  6233.                     shell->Error(L"The value must be a number.\n");
  6234.                     goto Exit;
  6235.                 }
  6236.  
  6237.                 refVal = (CORDB_ADDRESS)genVal8;
  6238.  
  6239.                 break;
  6240.  
  6241.             default:
  6242.                 shell->Error(L"Can't set value of variable with type 0x%x\n",
  6243.                              type);
  6244.                 goto Exit;
  6245.             }
  6246.         }
  6247.         
  6248.         // Update which every type of value we found.
  6249.         if (pGenValue != NULL)
  6250.             hr = pGenValue->SetValue(pNewVal);
  6251.         else
  6252.         {
  6253.             _ASSERTE(pRefValue != NULL);
  6254.             hr = pRefValue->SetValue(refVal);
  6255.         }
  6256.  
  6257.         if (SUCCEEDED(hr))
  6258.         {
  6259.             // Note: PrintVariable releases ivalue.
  6260.             shell->PrintVariable(varName, ivalue, 0, TRUE);
  6261.             shell->Write(L"\n");
  6262.  
  6263.             ivalue = NULL;
  6264.         }
  6265.         else
  6266.         {
  6267.             shell->Error(L"Update failed.\n");
  6268.             shell->ReportError(hr);
  6269.         }
  6270.  
  6271.     Exit:
  6272.         if (ivalue)
  6273.             RELEASE(ivalue);
  6274.  
  6275.         if (pGenValue)
  6276.             RELEASE(pGenValue);
  6277.  
  6278.         if (pRefValue)
  6279.             RELEASE(pRefValue);
  6280.     }
  6281.  
  6282.     // Provide help specific to this command
  6283.     void Help(Shell *shell)
  6284.     {
  6285.         ShellCommand::Help(shell);
  6286.         shell->Write(L" <variable specifier> <value>");
  6287.         shell->Write(L"\nSet the value of the given variable.");
  6288.         shell->Write(L"\nThe value can be a literal or another variable.");
  6289.         shell->Write(L"\nExamples:");
  6290.         shell->Write(L"\n\tset int1 0x2a");
  6291.         shell->Write(L"\n\tset float1 3.1415");
  6292.         shell->Write(L"\n\tset char1 'a'");
  6293.         shell->Write(L"\n\tset bool1 true");
  6294.         shell->Write(L"\n\tset obj1 0x12345678");
  6295.         shell->Write(L"\n\tset obj1 obj2");
  6296.         shell->Write(L"\n\tset obj1.m_foo[obj1.m_bar] obj3.m_foo[2]");
  6297.     }
  6298.  
  6299.     const WCHAR *ShortHelp(Shell *shell)
  6300.     {
  6301.         return L"Change the value of a variable (locals, statics, etc.)";
  6302.     }
  6303. };
  6304.  
  6305.  
  6306. class ProcessesEnumDebuggerCommand: public DebuggerCommand
  6307. {
  6308. public:
  6309.     ProcessesEnumDebuggerCommand(const WCHAR *name, int minMatchLength = 0)
  6310.         : DebuggerCommand(name, minMatchLength)
  6311.     {
  6312.     }
  6313.  
  6314.     virtual void Do(DebuggerShell *shell, ICorDebug *cor, const WCHAR *args)
  6315.     {
  6316.         BOOL fPidSpecified = TRUE;
  6317.         int ulPid;
  6318.         if (!shell->GetIntArg(args, ulPid))
  6319.             fPidSpecified = FALSE;        
  6320.         
  6321.         ICorPublish *pPublish;
  6322.  
  6323.         HRESULT hr = ::CoCreateInstance (CLSID_CorpubPublish, 
  6324.                                         NULL,
  6325.                                         CLSCTX_INPROC_SERVER,
  6326.                                         IID_ICorPublish,
  6327.                                         (LPVOID *)&pPublish);
  6328.  
  6329.         if (SUCCEEDED (hr))
  6330.         {
  6331.             ICorPublishProcessEnum *pProcessEnum = NULL;
  6332.             ICorPublishProcess    *pProcess [1];
  6333.             BOOL fAtleastOne = FALSE;
  6334.  
  6335.             if (fPidSpecified == FALSE)
  6336.             {
  6337.                 hr = pPublish->EnumProcesses (COR_PUB_MANAGEDONLY,
  6338.                                                 &pProcessEnum);
  6339.             }
  6340.             else
  6341.             {
  6342.                 hr = pPublish->GetProcess (ulPid,
  6343.                                            pProcess);                    
  6344.             }
  6345.  
  6346.             if (SUCCEEDED (hr))
  6347.             {
  6348.                 ULONG ulElemsFetched;
  6349.  
  6350.                 if (fPidSpecified == FALSE)
  6351.                 {
  6352.                     pProcessEnum->Next (1, pProcess, &ulElemsFetched);
  6353.                 }
  6354.                 else
  6355.                 {
  6356.                     ulElemsFetched = 1;
  6357.                 }
  6358.  
  6359.                 while (ulElemsFetched != 0)
  6360.                 {
  6361.                     UINT    pid;
  6362.                     WCHAR    szName [64];
  6363.                     ULONG32    ulNameLength;
  6364.                     BOOL    fIsManaged;
  6365.  
  6366.                     pProcess [0]->GetProcessID (&pid);
  6367.                     pProcess [0]->GetDisplayName (64, &ulNameLength, szName);
  6368.                     pProcess [0]->IsManaged (&fIsManaged);
  6369.  
  6370.                     if ((fPidSpecified == FALSE) || (pid == ulPid))
  6371.                     {
  6372.  
  6373.                         shell->Write (L"\nProcessId = %d  ProcessName = %s\n", 
  6374.                                                             pid, szName);
  6375.  
  6376.                         fAtleastOne = TRUE;
  6377.  
  6378.  
  6379.                         ICorPublishAppDomainEnum *pAppDomainEnum;
  6380.  
  6381.                         hr = pProcess [0]->EnumAppDomains (&pAppDomainEnum);
  6382.  
  6383.                         if (SUCCEEDED (hr))
  6384.                         {
  6385.                             ICorPublishAppDomain    *pAppDomain [1];
  6386.                             ULONG ulAppDomainsFetched;
  6387.  
  6388.                             pAppDomainEnum->Next (1, pAppDomain, &ulAppDomainsFetched);
  6389.  
  6390.                             while (ulAppDomainsFetched != 0)
  6391.                             {
  6392.                                 ULONG32    uId;
  6393.                                 WCHAR    szName [64];
  6394.                                 ULONG32    ulNameLength;
  6395.  
  6396.                                 pAppDomain [0]->GetID (&uId);
  6397.                                 pAppDomain [0]->GetName (64, &ulNameLength, szName);
  6398.  
  6399.                                 shell->Write (L"\tID = %d  AppDomainName = %s\n", uId, szName);
  6400.  
  6401.                                 pAppDomain [0]->Release();
  6402.  
  6403.                                 pAppDomainEnum->Next (1, pAppDomain, &ulAppDomainsFetched);
  6404.                             }
  6405.                         }
  6406.                     }
  6407.  
  6408.                     pProcess [0]->Release();
  6409.  
  6410.                     if (fPidSpecified == FALSE)
  6411.                     {
  6412.                         pProcessEnum->Next (1, pProcess, &ulElemsFetched);
  6413.                     }
  6414.                     else
  6415.                     {
  6416.                         ulElemsFetched--;
  6417.                     }
  6418.                 }
  6419.  
  6420.                 if (!fAtleastOne)
  6421.                 {
  6422.                     if (fPidSpecified)
  6423.                         shell->Error (L"No managed process with given ProcessId found\n");
  6424.                     else
  6425.                         shell->Error (L"No managed process found\n");
  6426.                 }
  6427.  
  6428.             }
  6429.             if (pProcessEnum != NULL)
  6430.                 pProcessEnum->Release();
  6431.         }            
  6432.     }
  6433.  
  6434.     // Provide help specific to this command
  6435.     void Help(Shell *shell)
  6436.     {
  6437.         ShellCommand::Help(shell);
  6438.         shell->Write(L"\n");
  6439.         shell->Write(L"Enumerates all managed processes along with the list of appdomains in each process.\n");
  6440.     }
  6441.  
  6442.  
  6443.     const WCHAR *ShortHelp(Shell *shell)
  6444.     {
  6445.         return L"Show all managed processes running on the system";
  6446.     }
  6447. };
  6448.  
  6449. #define MAX_APP_DOMAINS        64
  6450.  
  6451. enum ADC_PRINT
  6452. {
  6453.     ADC_PRINT_APP_DOMAINS = 0, 
  6454.     ADC_PRINT_ASSEMBLIES,
  6455.     ADC_PRINT_MODULES,
  6456.     ADC_PRINT_ALL,
  6457. };
  6458.  
  6459. class AppDomainChooser
  6460. {
  6461.     ICorDebugAppDomain *m_pAD [MAX_APP_DOMAINS];
  6462.     BOOL m_fAttachStatus [MAX_APP_DOMAINS];
  6463.     ULONG m_ulAppDomainCount;
  6464.  
  6465. public:
  6466.     AppDomainChooser()
  6467.         : m_ulAppDomainCount(0)
  6468.     {
  6469.         memset(m_pAD, 0, sizeof(ICorDebugAppDomain*)*MAX_APP_DOMAINS);
  6470.         memset(m_fAttachStatus, 0, sizeof(BOOL)*MAX_APP_DOMAINS);
  6471.     }
  6472.  
  6473.     virtual ~AppDomainChooser()
  6474.     {
  6475.         for (int i = 0; i < m_ulAppDomainCount; i++)
  6476.         {
  6477.             if (m_pAD[i] != NULL)
  6478.                 m_pAD[i]->Release();
  6479.         }
  6480.     }
  6481.  
  6482.     void PrintAppDomains(ICorDebugAppDomainEnum *pADEnum,
  6483.                          ICorDebugAppDomain *pAppDomainCur,
  6484.                          ADC_PRINT iPrintVal,
  6485.                          DebuggerShell *shell)
  6486.     {
  6487.         WCHAR   szName [64];
  6488.         UINT    ulNameLength;
  6489.         ULONG32   id;
  6490.         HRESULT hr = S_OK;
  6491.         ULONG   ulCount;
  6492.             
  6493.         hr = pADEnum->Next (MAX_APP_DOMAINS, &m_pAD [0], &m_ulAppDomainCount);
  6494.  
  6495.         for (int iADIndex=0; iADIndex < m_ulAppDomainCount; iADIndex++)
  6496.         {
  6497.             WCHAR    *pszAttachString;
  6498.             WCHAR   *pszActiveString;
  6499.             bool    fUuidToString = false;
  6500.             
  6501.             m_pAD [iADIndex]->GetName (64, &ulNameLength, (WCHAR *)szName);
  6502.             m_pAD [iADIndex]->IsAttached (&m_fAttachStatus [iADIndex]);
  6503.             m_pAD [iADIndex]->GetID(&id);
  6504.  
  6505.             if (m_fAttachStatus [iADIndex] == TRUE)
  6506.                 pszAttachString = L" Attached ";
  6507.             else
  6508.                 pszAttachString = L" Not Attached ";
  6509.  
  6510.             if (pAppDomainCur != NULL && pAppDomainCur == m_pAD [iADIndex])
  6511.                 pszActiveString = L"*";
  6512.             else
  6513.                 pszActiveString = L" ";
  6514.  
  6515.             shell->Write (L"\n%d) %s AppDomainName = <%s>\n\tDebugStatus"
  6516.                 L": <Debugger%s>\n\tID: %d\n", iADIndex+1, 
  6517.                 pszActiveString, szName, pszAttachString, id);
  6518.             
  6519.             if (iPrintVal >= ADC_PRINT_ASSEMBLIES)
  6520.             {
  6521.                 ICorDebugAssemblyEnum *pAssemblyEnum = NULL;
  6522.                 hr = m_pAD [iADIndex]->EnumerateAssemblies (&pAssemblyEnum);
  6523.  
  6524.                 if (SUCCEEDED (hr))
  6525.                 {
  6526.                     ICorDebugAssembly *pAssembly [1];
  6527.  
  6528.                     hr = pAssemblyEnum->Next (1, pAssembly, &ulCount);
  6529.                     while (ulCount > 0)
  6530.                     {
  6531.                         pAssembly [0]->GetName (64, &ulNameLength, (WCHAR *)szName);
  6532.                         shell->Write (L"\tAssembly Name : %s\n", szName);
  6533.  
  6534.                         if (iPrintVal >= ADC_PRINT_MODULES)
  6535.                         {
  6536.  
  6537.                             ICorDebugModuleEnum *pModuleEnum = NULL;
  6538.                             hr = pAssembly [0]->EnumerateModules (&pModuleEnum);
  6539.  
  6540.                             if (SUCCEEDED (hr))
  6541.                             {
  6542.                                 ICorDebugModule *pModule [1];
  6543.  
  6544.                                 hr = pModuleEnum->Next (1, pModule, &ulCount);
  6545.  
  6546.                                 while (ulCount > 0)
  6547.                                 {
  6548.                                     pModule [0]->GetName (64, &ulNameLength, (WCHAR *)szName);
  6549.                                     shell->Write (L"\t\tModule Name : %s\n", szName);
  6550.  
  6551.                                     pModule [0]->Release();
  6552.                                     hr = pModuleEnum->Next (1, pModule, &ulCount);
  6553.                                 }
  6554.  
  6555.                                 pModuleEnum->Release();
  6556.                             }
  6557.                             else
  6558.                             {
  6559.                                 shell->Error (L"ICorDebugAssembly::EnumerateModules() failed!! \n");
  6560.                                 shell->ReportError (hr);
  6561.                             }
  6562.                         }
  6563.  
  6564.                         pAssembly [0]->Release();
  6565.                         hr = pAssemblyEnum->Next (1, &pAssembly [0], &ulCount);            
  6566.                     }
  6567.                     pAssemblyEnum->Release();
  6568.                 }
  6569.                 else
  6570.                 {
  6571.                     shell->Error (L"ICorDebugAppDomain::EnumerateAssemblies() failed!! \n");
  6572.                     shell->ReportError (hr);
  6573.                 }
  6574.             }
  6575.         }
  6576.     }
  6577.  
  6578. #define ADC_CHOICE_ERROR (-1)
  6579.     ICorDebugAppDomain *GetConsoleChoice(int *piR, DebuggerShell *shell)
  6580.     {
  6581.         WCHAR strTemp [10+1];
  6582.         int iResult;
  6583.         if (shell->ReadLine (strTemp, 10))
  6584.         {
  6585.             WCHAR *p = strTemp;
  6586.             if (shell->GetIntArg (p, iResult))
  6587.             {
  6588.                 iResult--; // Since the input is count, and this is index
  6589.                 if (iResult < 0 || iResult >= m_ulAppDomainCount)
  6590.                 {
  6591.                     shell->Error (L"\nInvalid selection.\n");
  6592.                     (*piR) = ADC_CHOICE_ERROR;
  6593.                     return NULL;
  6594.                 }
  6595.  
  6596.                (*piR) = iResult; 
  6597.                 return m_pAD[iResult];
  6598.             }
  6599.             else
  6600.             {
  6601.                 shell->Error (L"\nInvalid (non numeric) selection.\n");
  6602.                 
  6603.                 (*piR) = ADC_CHOICE_ERROR;
  6604.                 return NULL;
  6605.             }
  6606.         }
  6607.         return NULL;
  6608.     }
  6609.  
  6610.     BOOL GetAttachStatus(int iResult)
  6611.     {
  6612.         return m_fAttachStatus[iResult];
  6613.     }
  6614. };
  6615.  
  6616. enum EADDC_CHOICE
  6617. {
  6618.     EADDC_NONE = 0,
  6619.     EADDC_ATTACH,
  6620.     EADDC_DETACH,
  6621.     EADDC_ASYNC_BREAK,
  6622. };
  6623.  
  6624. class EnumAppDomainsDebuggerCommand: public DebuggerCommand
  6625. {
  6626. public:
  6627.     EnumAppDomainsDebuggerCommand(const WCHAR *name, int minMatchLength = 0)
  6628.         : DebuggerCommand(name, minMatchLength)
  6629.     {
  6630.     }
  6631.  
  6632. virtual void Do(DebuggerShell *shell, ICorDebug *cor, const WCHAR *args)
  6633.     {
  6634.         HRESULT hr = S_OK;
  6635.         EADDC_CHOICE choice = EADDC_NONE;
  6636.         
  6637.         if (shell->m_currentProcess == NULL)
  6638.         {
  6639.             shell->Error(L"Process not running.\n");
  6640.             return;
  6641.         }
  6642.  
  6643.         int iPrintVal;
  6644.         if (!shell->GetIntArg(args, iPrintVal))
  6645.         {
  6646.             WCHAR *szAttachDetach;
  6647.             if (!shell->GetStringArg(args, szAttachDetach))
  6648.             {
  6649.                 shell->Write( L"First arg is neither number nor string!\n");
  6650.                 return;
  6651.             }
  6652.  
  6653.             iPrintVal = ADC_PRINT_APP_DOMAINS;
  6654.             
  6655.             if (szAttachDetach[0] == 'a' ||
  6656.                 szAttachDetach[0] == 'A')
  6657.             {
  6658.                 choice = EADDC_ATTACH;
  6659.             }
  6660.             else if (szAttachDetach[0] == 'd' ||
  6661.                      szAttachDetach[0] == 'D')
  6662.             {
  6663.                 choice = EADDC_DETACH;   
  6664.             }
  6665.             else if (szAttachDetach[0] == 's' ||
  6666.                      szAttachDetach[0] == 'S')
  6667.             {
  6668.                 choice = EADDC_ASYNC_BREAK;   
  6669.             }
  6670.             else
  6671.             {
  6672.                 iPrintVal = ADC_PRINT_ALL;
  6673.             }
  6674.         }
  6675.         else
  6676.         {
  6677.             if (iPrintVal > ADC_PRINT_ALL ||
  6678.                 iPrintVal < ADC_PRINT_APP_DOMAINS)
  6679.             {
  6680.                 shell->Write( L"Command is not recognized.\n");
  6681.                 return;
  6682.             }
  6683.         }
  6684.         
  6685.         ICorDebugAppDomain *pAppDomainCur = NULL;
  6686.         if (shell->m_currentThread != NULL)
  6687.         {
  6688.             hr = shell->m_currentThread->GetAppDomain(&pAppDomainCur);
  6689.             if (FAILED(hr))
  6690.                 pAppDomainCur = NULL;
  6691.             else
  6692.             {   
  6693.                 BOOL fAttached;
  6694.  
  6695.                 if (FAILED(pAppDomainCur->IsAttached(&fAttached)))
  6696.                     pAppDomainCur = NULL;
  6697.  
  6698.                 if (!fAttached)
  6699.                     pAppDomainCur = NULL;
  6700.             }
  6701.         }
  6702.  
  6703.         ICorDebugAppDomainEnum *pADEnum = NULL;
  6704.         hr = shell->m_currentProcess->EnumerateAppDomains (&pADEnum);
  6705.         AppDomainChooser adc;
  6706.  
  6707.         ICorDebugAppDomain *pADChosen = NULL;
  6708.         
  6709.         if (SUCCEEDED (hr))
  6710.         {
  6711.             adc.PrintAppDomains(pADEnum, pAppDomainCur, (ADC_PRINT)iPrintVal, shell);
  6712.             
  6713.             pADEnum->Release();
  6714.  
  6715.             if (choice != EADDC_NONE)
  6716.             {
  6717.                 WCHAR *szAction;
  6718.                 switch(choice)
  6719.                 {
  6720.                     case EADDC_ATTACH:
  6721.                         szAction = L"attach to";
  6722.                         break;
  6723.                     case EADDC_DETACH:
  6724.                         szAction = L"detach from";
  6725.                         break;
  6726.                     case EADDC_ASYNC_BREAK:
  6727.                         szAction = L"break into";
  6728.                         break;
  6729.                 }
  6730.                 
  6731.                 // prompt the user to select one of the app domains to act upon:
  6732.                 shell->Write (L"\nPlease select the app domain to %s by "
  6733.                     L"number.\n", szAction);
  6734.  
  6735.                 int iResult;
  6736.                 pADChosen = adc.GetConsoleChoice(&iResult, shell);
  6737.                 if (NULL == pADChosen)
  6738.                     return;
  6739.                     
  6740.                 switch(choice)
  6741.                 {
  6742.                     case EADDC_ATTACH:
  6743.                         if (adc.GetAttachStatus(iResult) == FALSE)
  6744.                         {
  6745.                             pADChosen->Attach();
  6746.                         }
  6747.                         else
  6748.                         {
  6749.                             shell->Write (L"Already attached to specified "
  6750.                                 L"app domain.\n");
  6751.                         }
  6752.                         break;
  6753.                         
  6754.                     case EADDC_DETACH:
  6755.                         if (adc.GetAttachStatus(iResult) == TRUE)
  6756.                         {
  6757.                             pADChosen->Detach();
  6758.                         }
  6759.                         else
  6760.                         {
  6761.                             shell->Write (L"Already detached from specified "
  6762.                                 L"app domain.\n");
  6763.                         }
  6764.                         break;
  6765.                         
  6766.                     case EADDC_ASYNC_BREAK:
  6767.                         hr = shell->AsyncStop(pADChosen);
  6768.                         break;
  6769.                 }
  6770.             }
  6771.         }
  6772.         else
  6773.         {
  6774.             shell->Error (L"ICorDebugProcess::EnumerateAppDomains() failed!! \n");
  6775.             shell->ReportError (hr);
  6776.         }
  6777.  
  6778.         if (pAppDomainCur != NULL)
  6779.             pAppDomainCur->Release();
  6780.     }
  6781.  
  6782.     // Provide help specific to this command
  6783.     void Help(Shell *shell)
  6784.     {
  6785.         ShellCommand::Help(shell);
  6786.         shell->Write(L"\n");
  6787.         shell->Write(L"Enumerates all appdomains, assemblies and modules in the current process.\n");
  6788.         shell->Write(L"After detaching/attaching, you must \"go\" in order to resume execution.\n");
  6789.         shell->Write(L"Usage : \n");
  6790.         shell->Write(L"app attach <This command lists the app domains in the process and prompts\n");
  6791.         shell->Write(L"          the user to select the app domain to attach to.>\n");
  6792.         shell->Write(L"app detach <This command lists the app domains in the process and prompts\n");
  6793.         shell->Write(L"          the user to select the app domain to detach from.>\n");
  6794.         shell->Write(L"app 0 <This command lists only the app domains in the process.>\n");
  6795.         shell->Write(L"app 1 <This lists the app domains and assemblies in the current process.>\n");
  6796.         shell->Write(L"app   <Lists all the app domains, assemblies and modules in the current process>\n");
  6797.     }
  6798.  
  6799.  
  6800.     const WCHAR *ShortHelp(Shell *shell)
  6801.     {
  6802.         return L"Show all appdomains in the current program";
  6803.     }
  6804. };
  6805.  
  6806.  
  6807.  
  6808. class ListDebuggerCommand: public DebuggerCommand
  6809. {
  6810. public:
  6811.     ListDebuggerCommand(const WCHAR *name, int minMatchLength = 0)
  6812.         : DebuggerCommand(name, minMatchLength)
  6813.     {
  6814.     }
  6815.  
  6816.     virtual void Do(DebuggerShell *shell, ICorDebug *cor, const WCHAR *args)
  6817.     {
  6818.         if (shell->m_currentProcess == NULL)
  6819.         {
  6820.             shell->Error(L"Process not running.\n");
  6821.             return;
  6822.         }
  6823.  
  6824.  
  6825.         // Get the type to list.
  6826.         WCHAR* varName;
  6827.         shell->GetStringArg(args, varName);
  6828.  
  6829.         if ((args - varName) == 0)
  6830.         {
  6831.             shell->Error(L"Incorrect/no arguments specified.\n");
  6832.             Help (shell);
  6833.             return;
  6834.         }
  6835.  
  6836.         if ((varName [0] == L'm' || varName [0] == L'M')
  6837.             &&
  6838.             (varName [1] == L'o' || varName [1] == L'O'))
  6839.         {
  6840.             g_pShell->ListAllModules (LIST_MODULES);
  6841.         }
  6842.         else if ((varName [0] == L'c' || varName [0] == L'C')
  6843.                  &&
  6844.                  (varName [1] == L'l' || varName [1] == L'L'))
  6845.         {
  6846.             g_pShell->ListAllModules (LIST_CLASSES);
  6847.         }
  6848.         else if ((varName[0] == L'f' || varName [0] == L'F')
  6849.                  &&
  6850.                  (varName [1] == L'u' || varName [1] == L'U'))
  6851.         {
  6852.             g_pShell->ListAllModules (LIST_FUNCTIONS);
  6853.         }
  6854.  
  6855.         else
  6856.             Help (shell);        
  6857.     }
  6858.  
  6859.     // Provide help specific to this command
  6860.     void Help(Shell *shell)
  6861.     {
  6862.         ShellCommand::Help(shell);
  6863.         shell->Write(L"\n");
  6864.         shell->Write(L"Displays the requested list.\n");
  6865.         shell->Write(L"Usage : \n");
  6866.         shell->Write(L"list mod <This command lists the loaded modules in the process.>\n");
  6867.         shell->Write(L"list cl  <This command lists the loaded classes in the process.>\n");
  6868.         shell->Write(L"list fu  <This command lists any global functions.>\n");
  6869.     }
  6870.  
  6871.  
  6872.     const WCHAR *ShortHelp(Shell *shell)
  6873.     {
  6874.         return L"Show loaded modules, classes or functions";
  6875.     }
  6876. };
  6877.  
  6878.  
  6879. /* ------------------------------------------------------------------------- *
  6880.  * ReadCommandFromFile is used to read commands from a file and execute. 
  6881.  * ------------------------------------------------------------------------- */
  6882.  
  6883. class ReadCommandFromFile : public DebuggerCommand
  6884. {
  6885. private:
  6886.     FILE *savOld;
  6887.     FILE *newFile;
  6888.  
  6889. public:
  6890.     ReadCommandFromFile(const WCHAR *name, int minMatchLength = 0)
  6891.         : DebuggerCommand(name, minMatchLength)
  6892.     {
  6893.     }
  6894.  
  6895.     void Do(DebuggerShell *shell, ICorDebug *cor, const WCHAR *args)
  6896.     {
  6897.         WCHAR* fileName;
  6898.  
  6899.         shell->GetStringArg(args, fileName);
  6900.         
  6901.         if (fileName != args)
  6902.         {
  6903.             MAKE_ANSIPTR_FROMWIDE (fnameA, fileName);
  6904.             _ASSERTE (fnameA != NULL);
  6905.  
  6906.             newFile = fopen(fnameA, "r");
  6907.  
  6908.             if (newFile != NULL)
  6909.             {
  6910.                 savOld = g_pShell->GetM_in();
  6911.                 g_pShell->PutM_in(newFile);
  6912.  
  6913.                 while (!feof(newFile))
  6914.                     shell->ReadCommand();
  6915.  
  6916.                 g_pShell->PutM_in(savOld);
  6917.                 fclose(newFile);
  6918.             }
  6919.             else
  6920.                 shell->Write(L"Unable to open input file.\n");
  6921.         }
  6922.         else
  6923.             Help(shell);
  6924.     }
  6925.  
  6926.     // Provide help specific to this command
  6927.     void Help(Shell *shell)
  6928.     {
  6929.         ShellCommand::Help(shell);
  6930.         shell->Write(L" <input filename>");
  6931.         shell->Write(L"\nReads commands from the given file and ");
  6932.         shell->Write(L"\nexecutes them.");
  6933.         shell->Write(L"\n");
  6934.     }
  6935.  
  6936.     const WCHAR *ShortHelp(Shell *shell)
  6937.     {
  6938.         return L"Read commands from a file";
  6939.     }
  6940. };
  6941.  
  6942. /* ------------------------------------------------------------------------- *
  6943.  * SaveCommandsToFile is used to save commands to a file and execute. 
  6944.  * ------------------------------------------------------------------------- */
  6945.  
  6946. class SaveCommandsToFile : public DebuggerCommand
  6947. {
  6948. private:
  6949.     FILE *savFile;
  6950.  
  6951. public:
  6952.     SaveCommandsToFile(const WCHAR *name, int minMatchLength = 0)
  6953.         : DebuggerCommand(name, minMatchLength)
  6954.     {
  6955.         savFile = NULL;
  6956.     }
  6957.  
  6958.     void Do(DebuggerShell *shell, ICorDebug *cor, const WCHAR *args)
  6959.     {
  6960.  
  6961.         if (savFile == NULL)
  6962.         {
  6963.             WCHAR* fileName;
  6964.  
  6965.             shell->GetStringArg(args, fileName);
  6966.             
  6967.             if (fileName != args)
  6968.             {
  6969.                 MAKE_ANSIPTR_FROMWIDE (fnameA, fileName);
  6970.                 _ASSERTE (fnameA != NULL);
  6971.                 
  6972.                 savFile = fopen(fnameA, "w");
  6973.  
  6974.                 if (savFile != NULL)
  6975.                 {
  6976.                     shell->Write(L"Outputing commands to file %S\n",
  6977.                                  fnameA);
  6978.                     
  6979.                     while (!shell->m_quit && (savFile != NULL))
  6980.                     {
  6981.                         shell->ReadCommand();
  6982.  
  6983.                         // Write the command into the file.
  6984.                         if (savFile != NULL)
  6985.                             shell->PutCommand(savFile);
  6986.                     }
  6987.  
  6988.                     shell->Write(L"No longer outputing commands to file %S\n",
  6989.                                  fnameA);
  6990.                 }
  6991.                 else
  6992.                     shell->Write(L"Unable to open output file.\n");
  6993.             }
  6994.             else
  6995.                 Help(shell);
  6996.         }
  6997.         else
  6998.         {
  6999.             if (savFile != NULL)
  7000.             {
  7001.                 fclose(savFile);
  7002.                 savFile = NULL;
  7003.             }
  7004.             else
  7005.                 Help(shell);
  7006.         }
  7007.     }
  7008.  
  7009.     // Provide help specific to this command
  7010.     void Help(Shell *shell)
  7011.     {
  7012.         ShellCommand::Help(shell);
  7013.         shell->Write(L" [<output filename>]");
  7014.         shell->Write(L"\nGiven a filename, all commands executed will be");
  7015.         shell->Write(L"\nwritten to the file. Without a file name, the");
  7016.         shell->Write(L"\ncommand stops writing commands to the file.");
  7017.         shell->Write(L"\n");
  7018.     }
  7019.  
  7020.     const WCHAR *ShortHelp(Shell *shell)
  7021.     {
  7022.         return L"Write commands to a file";
  7023.     }
  7024. };
  7025.  
  7026.  
  7027. class XtendedSymbolsInfoDebuggerCommand : public DebuggerCommand
  7028. {
  7029. public:
  7030.     XtendedSymbolsInfoDebuggerCommand(const WCHAR *name, int minMatchLength = 0)
  7031.         : DebuggerCommand(name, minMatchLength)
  7032.     {
  7033.     }
  7034.  
  7035.     void Do(DebuggerShell *shell, ICorDebug *cor, const WCHAR *args)
  7036.     {
  7037.         if (shell->m_currentProcess == NULL)
  7038.         {
  7039.             shell->Error(L"Process not running.\n");
  7040.             return;
  7041.         }
  7042.  
  7043.  
  7044.         // Get the modulename and string to look for.
  7045.         WCHAR* varName;
  7046.         shell->GetStringArg(args, varName);
  7047.  
  7048.         if ((args - varName) == 0)
  7049.         {
  7050.             shell->Error(L"Incorrect/no arguments specified.\n");
  7051.             Help (shell);
  7052.             return;
  7053.         }
  7054.  
  7055.         shell->MatchAndPrintSymbols (varName, TRUE);
  7056.     }
  7057.  
  7058.     // Provide help specific to this command
  7059.     void Help(Shell *shell)
  7060.     {
  7061.         ShellCommand::Help(shell);
  7062.         shell->Write(L"\nPrints out the symbols matching the pattern in the given module.");
  7063.         shell->Write(L"\nUsage:");
  7064.         shell->Write(L"\nx <modulename>!<string_to_look_for>");
  7065.         shell->Write(L"\neg:");
  7066.         shell->Write(L"\nx mscorlib.dll!Method1");
  7067.         shell->Write(L"\nx mscorlib.dll!Method*");
  7068.         shell->Write(L"\nx Method*");
  7069.         shell->Write(L"\n");
  7070.     }
  7071.  
  7072.     const WCHAR *ShortHelp(Shell *shell)
  7073.     {
  7074.         return L"Show symbols matching a given pattern";
  7075.     }
  7076. };
  7077.  
  7078. class DetachDebuggerCommand : public DebuggerCommand
  7079. {
  7080. public:
  7081.     DetachDebuggerCommand(const WCHAR *name, int minMatchLength = 0)
  7082.         : DebuggerCommand(name, minMatchLength)
  7083.     {
  7084.     }
  7085.  
  7086.     void Do(DebuggerShell *shell, ICorDebug *cor, const WCHAR *args)
  7087.     {
  7088.         if (shell->m_currentProcess == NULL)
  7089.         {
  7090.             shell->Error(L"Process not running.\n");
  7091.             return;
  7092.         }
  7093.  
  7094.         HRESULT hr = shell->m_currentProcess->Detach();
  7095.         _ASSERTE(!FAILED(hr));
  7096.  
  7097.         shell->SetTargetProcess(NULL);
  7098.         shell->SetCurrentThread(NULL, NULL, NULL);
  7099.     }
  7100.  
  7101.     // Provide help specific to this command
  7102.     void Help(Shell *shell)
  7103.         {
  7104.             ShellCommand::Help(shell);
  7105.             shell->Write(L"\nDetaches from the process to which cordbg is currently attached");
  7106.             shell->Write(L"\n");
  7107.     }
  7108.  
  7109.     const WCHAR *ShortHelp(Shell *shell)
  7110.     {
  7111.         return L"Detach from the current process";
  7112.     }
  7113. };
  7114.  
  7115. void DebuggerShell::AddCommands()
  7116. {
  7117.     AddCommand(new HelpShellCommand(L"help", 1));
  7118.     AddCommand(new HelpShellCommand(L"?", 1));
  7119.     AddCommand(new RunDebuggerCommand(L"run", 1));
  7120.     AddCommand(new AttachDebuggerCommand(L"attach", 1));
  7121.     AddCommand(new DetachDebuggerCommand(L"detach", 2));
  7122.     AddCommand(new KillDebuggerCommand(L"kill", 1));
  7123.     AddCommand(new QuitDebuggerCommand(L"quit", 1));
  7124.     AddCommand(new QuitDebuggerCommand(L"exit", 2));
  7125.     AddCommand(new GoDebuggerCommand(L"go", 1));
  7126.     AddCommand(new GoDebuggerCommand(L"cont", 3));
  7127.     AddCommand(new StepDebuggerCommand(L"step", true, 1));
  7128.     AddCommand(new StepDebuggerCommand(L"in", true, 1));
  7129.     AddCommand(new StepDebuggerCommand(L"si", true));
  7130.     AddCommand(new StepDebuggerCommand(L"next", false, 1));
  7131.     AddCommand(new StepDebuggerCommand(L"so", false));
  7132.     AddCommand(new StepOutDebuggerCommand(L"out", 1));
  7133.     AddCommand(new StepSingleDebuggerCommand(L"ssingle", true, 2));
  7134.     AddCommand(new StepSingleDebuggerCommand(L"nsingle", false, 2));
  7135.     AddCommand(new BreakpointDebuggerCommand(L"break", 1));
  7136.     AddCommand(new BreakpointDebuggerCommand(L"stop"));
  7137.     AddCommand(new RemoveBreakpointDebuggerCommand(L"remove", 3));
  7138.     AddCommand(new RemoveBreakpointDebuggerCommand(L"delete", 3));
  7139.     AddCommand(new ThreadsDebuggerCommand(L"threads", 1));
  7140.     AddCommand(new WhereDebuggerCommand(L"where", 1));
  7141.     AddCommand(new PrintDebuggerCommand(L"print", 1));
  7142.     AddCommand(new UpDebuggerCommand(L"up", 1));
  7143.     AddCommand(new DownDebuggerCommand(L"down", 1));
  7144.     AddCommand(new SuspendDebuggerCommand(L"suspend", 2));
  7145.     AddCommand(new ResumeDebuggerCommand(L"resume",2));
  7146.     AddCommand(new ShowDebuggerCommand(L"show", 2));
  7147.     AddCommand(new PathDebuggerCommand(L"path", 2));
  7148.     AddCommand(new RefreshSourceDebuggerCommand(L"refreshsource", 3));
  7149.     AddCommand(new CatchDebuggerCommand(L"catch", 2));
  7150.     AddCommand(new IgnoreDebuggerCommand(L"ignore", 2));
  7151.     AddCommand(new SetModeDebuggerCommand(L"mode", 1) );
  7152.     AddCommand(new RegistersDebuggerCommand(L"registers", 3));
  7153.     AddCommand(new DumpDebuggerCommand(L"dump", 2));
  7154.     AddCommand(new SetDefaultDebuggerCommand(L"regdefault", 4));
  7155.     AddCommand(new WriteMemoryDebuggerCommand( L"writememory", 2) ); 
  7156.     AddCommand(new WTDebuggerCommand(L"wt", 2));
  7157.     AddCommand(new AssociateSourceFileCommand(L"associatesource", 2));
  7158.     AddCommand(new SetIpDebuggerCommand(L"setip", 5));
  7159.     AddCommand(new FuncEvalDebuggerCommand(L"funceval", 1));
  7160.     AddCommand(new NewStringDebuggerCommand(L"newstr", 4));
  7161.     AddCommand(new NewObjectDebuggerCommand(L"newobj", 4));
  7162.     AddCommand(new NewObjectNCDebuggerCommand(L"newobjnc", 8));
  7163.     AddCommand(new ProcessesEnumDebuggerCommand(L"processenum", 3));
  7164.     AddCommand(new EnumAppDomainsDebuggerCommand(L"appdomainenum", 2));
  7165.     AddCommand(new SetValueDebuggerCommand(L"set", 3));
  7166.     AddCommand(new ListDebuggerCommand(L"list", 1));
  7167.     AddCommand(new ReadCommandFromFile(L"<", 1));
  7168.     AddCommand(new SaveCommandsToFile(L">", 1));
  7169.     AddCommand(new XtendedSymbolsInfoDebuggerCommand(L"x", 1));
  7170.     
  7171. #ifdef _INTERNAL_DEBUG_SUPPORT_
  7172.     AddCommand(new UnmanagedThreadsDebuggerCommand(L"uthreads", 2));
  7173.     AddCommand(new UnmanagedWhereDebuggerCommand(L"uwhere", 2));
  7174.     AddCommand(new DisassembleDebuggerCommand(L"disassemble", 3));
  7175.  
  7176. #ifdef _DEBUG
  7177.     // this is only valid in debug mode becuase relies on debug-only support in metadata
  7178.     // and Iceefilegen
  7179.     AddCommand(new CompileForEditAndContinueCommand(L"zcompileForEnC", 2));
  7180.  
  7181.     // These are here so that we don't ship these commands in the retail version
  7182.     // of cordbg.exe
  7183.     AddCommand(new EditAndContinueDebuggerCommand(L"zEnC", 2));
  7184.     AddCommand(new ClearUnmanagedExceptionCommand(L"zcce", 4));
  7185.     AddCommand(new ClearManagedExceptionCommand(L"clear", 1));
  7186.     AddCommand(new SyncAttachDebuggerAtRTStartupCommand(L"syncattach", 2));
  7187. #endif
  7188. #endif
  7189.  
  7190. }
  7191.  
  7192.