| /****************************************************************************  | 
| **  | 
| ** Copyright (C) 2018 The Qt Company Ltd.  | 
| ** Contact: https://www.qt.io/licensing/  | 
| **  | 
| ** This file is part of the Qt VS Tools.  | 
| **  | 
| ** $QT_BEGIN_LICENSE:GPL-EXCEPT$  | 
| ** Commercial License Usage  | 
| ** Licensees holding valid commercial Qt licenses may use this file in  | 
| ** accordance with the commercial license agreement provided with the  | 
| ** Software or, alternatively, in accordance with the terms contained in  | 
| ** a written agreement between you and The Qt Company. For licensing terms  | 
| ** and conditions see https://www.qt.io/terms-conditions. For further  | 
| ** information use the contact form at https://www.qt.io/contact-us.  | 
| **  | 
| ** GNU General Public License Usage  | 
| ** Alternatively, this file may be used under the terms of the GNU  | 
| ** General Public License version 3 as published by the Free Software  | 
| ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT  | 
| ** included in the packaging of this file. Please review the following  | 
| ** information to ensure the GNU General Public License requirements will  | 
| ** be met: https://www.gnu.org/licenses/gpl-3.0.html.  | 
| **  | 
| ** $QT_END_LICENSE$  | 
| **  | 
| ****************************************************************************/  | 
|   | 
| using System;  | 
| using System.Collections.Generic;  | 
| using System.IO;  | 
| using System.Linq;  | 
| using System.Text;  | 
| using System.Threading;  | 
| using System.Windows.Threading;  | 
| using Microsoft.VisualStudio;  | 
| using Microsoft.VisualStudio.Debugger.Interop;  | 
| using Microsoft.VisualStudio.Shell;  | 
| using Microsoft.VisualStudio.Shell.Interop;  | 
| using QtVsTools.VisualStudio;  | 
|   | 
| namespace QtVsTools.Qml.Debug.AD7  | 
| {  | 
|     sealed partial class Program : Disposable, IDebuggerEventSink,  | 
|   | 
|         IDebugProgramNode2,  // "This interface represents a program that can be debugged."  | 
|   | 
|         IDebugProgram3,      // "This interface represents a program that is running in a process."  | 
|   | 
|         IDebugProcess2,      // "This interface represents a process running on a port. If the  | 
|                              //  port is the local port, then IDebugProcess2 usually represents  | 
|                              //  a physical process on the local machine."  | 
|   | 
|         IDebugThread2,       // "This interface represents a thread running in a program."  | 
|         IDebugThread100,  | 
|   | 
|         IDebugModule3,       // "This interface represents a module -- that is, an executable unit  | 
|                              //  of a program -- such as a DLL."  | 
|   | 
|         IDebugEventCallback2 // "This interface is used by the debug engine (DE) to send debug  | 
|                              //  events to the session debug manager (SDM)."  | 
|     {  | 
|         public QmlDebugger Debugger { get; private set; }  | 
|   | 
|         public QmlEngine Engine { get; private set; }  | 
|   | 
|         public List<StackFrame> CurrentFrames { get; private set; }  | 
|   | 
|         public const string Name = "QML Debugger";  | 
|         public Guid ProcessId { get; private set; }  | 
|         public Guid ProgramId { get; set; }  | 
|         public IDebugProcess2 NativeProc { get; private set; }  | 
|         public uint NativeProcId { get; private set; }  | 
|         public string ExecPath { get; private set; }  | 
|         public string ExecArgs { get; private set; }  | 
|         public IVsDebugger VsDebugger { get; private set; }  | 
|         Dispatcher vsDebuggerThreadDispatcher;  | 
|   | 
|         private readonly static object criticalSectionGlobal = new object();  | 
|         static bool originalBreakAllProcesses = BreakAllProcesses;  | 
|         static int runningPrograms = 0;  | 
|   | 
|         public static Program Create(  | 
|             QmlEngine engine,  | 
|             IDebugProcess2 nativeProc,  | 
|             string execPath,  | 
|             string execArgs)  | 
|         {  | 
|             var _this = new Program();  | 
|             return _this.Initialize(engine, nativeProc, execPath, execArgs) ? _this : null;  | 
|         }  | 
|   | 
|         private Program()  | 
|         { }  | 
|   | 
|         private bool Initialize(  | 
|             QmlEngine engine,  | 
|             IDebugProcess2 nativeProc,  | 
|             string execPath,  | 
|             string execArgs)  | 
|         {  | 
|             Engine = engine;  | 
|             NativeProc = nativeProc;  | 
|   | 
|             var nativeProcId = new AD_PROCESS_ID[1];  | 
|             nativeProc.GetPhysicalProcessId(nativeProcId);  | 
|             NativeProcId = nativeProcId[0].dwProcessId;  | 
|   | 
|             ExecPath = execPath;  | 
|             ExecArgs = execArgs;  | 
|   | 
|             Debugger = QmlDebugger.Create(this, execPath, execArgs);  | 
|             if (Debugger == null)  | 
|                 return false;  | 
|   | 
|             VsDebugger = VsServiceProvider.GetService<IVsDebugger>();  | 
|             if (VsDebugger != null)  | 
|                 VsDebugger.AdviseDebugEventCallback(this as IDebugEventCallback2);  | 
|             vsDebuggerThreadDispatcher = Dispatcher.CurrentDispatcher;  | 
|   | 
|             ProcessId = Guid.NewGuid();  | 
|             CurrentFrames = new List<StackFrame>();  | 
|   | 
|             lock (criticalSectionGlobal) {  | 
|                 if (runningPrograms == 0)  | 
|                     originalBreakAllProcesses = BreakAllProcesses;  | 
|                 runningPrograms++;  | 
|             }  | 
|   | 
|             return true;  | 
|         }  | 
|   | 
|         public override bool CanDispose  | 
|         {  | 
|             get  | 
|             {  | 
|                 return !Engine.ProgramIsRunning(this);  | 
|             }  | 
|         }  | 
|   | 
|         protected override void DisposeManaged()  | 
|         {  | 
|             Debugger.Dispose();  | 
|             if (VsDebugger != null)  | 
|                 VsDebugger.UnadviseDebugEventCallback(this as IDebugEventCallback2);  | 
|   | 
|             lock (criticalSectionGlobal) {  | 
|                 runningPrograms--;  | 
|                 if (runningPrograms == 0)  | 
|                     BreakAllProcesses = originalBreakAllProcesses;  | 
|             }  | 
|         }  | 
|   | 
|         public void OutputWriteLine(string msg)  | 
|         {  | 
|             var execFileName = Path.GetFileName(ExecPath);  | 
|             Engine.OutputWriteLine(string.Format("'{0}' (QML): {1}", execFileName, msg));  | 
|         }  | 
|   | 
|         bool IDebuggerEventSink.QueryRuntimeFrozen()  | 
|         {  | 
|             var debugMode = new DBGMODE[1];  | 
|             int res = VSConstants.S_FALSE;  | 
|             vsDebuggerThreadDispatcher  | 
|                 .BeginInvoke(new Action(() => res = VsDebugger.GetMode(debugMode)), new object[0])  | 
|                 .Wait();  | 
|   | 
|             if (res != VSConstants.S_OK)  | 
|                 return false;  | 
|             return (debugMode[0] != DBGMODE.DBGMODE_Run);  | 
|         }  | 
|   | 
|         void IDebuggerEventSink.NotifyError(string errorMessage)  | 
|         {  | 
|             OutputWriteLine(errorMessage);  | 
|         }  | 
|   | 
|         int IDebugEventCallback2.Event(  | 
|             IDebugEngine2 pEngine,  | 
|             IDebugProcess2 pProcess,  | 
|             IDebugProgram2 pProgram,  | 
|             IDebugThread2 pThread,  | 
|             IDebugEvent2 pEvent,  | 
|             ref Guid riidEvent,  | 
|             uint dwAttrib)  | 
|         {  | 
|             if (pEngine == Engine)  | 
|                 return VSConstants.S_OK;  | 
|   | 
|             if (pProcess == null && pProgram == null)  | 
|                 return VSConstants.S_OK;  | 
|   | 
|             if (pProcess == null) {  | 
|                 if (pProgram.GetProcess(out pProcess) != VSConstants.S_OK || pProcess == null)  | 
|                     return VSConstants.S_OK;  | 
|             }  | 
|   | 
|             var pProcessId = new AD_PROCESS_ID[1];  | 
|             if (pProcess.GetPhysicalProcessId(pProcessId) != VSConstants.S_OK)  | 
|                 return VSConstants.S_OK;  | 
|   | 
|             if (pProcessId[0].dwProcessId != NativeProcId)  | 
|                 return VSConstants.S_OK;  | 
|   | 
|             if (riidEvent == typeof(IDebugProgramDestroyEvent2).GUID)  | 
|                 TerminateProcess();  | 
|   | 
|             return VSConstants.S_OK;  | 
|         }  | 
|   | 
|         void IDebuggerEventSink.NotifyClientDisconnected()  | 
|         {  | 
|             TerminateProcess();  | 
|         }  | 
|   | 
|         bool terminated = false;  | 
|         void TerminateProcess()  | 
|         {  | 
|             if (!terminated) {  | 
|                 terminated = true;  | 
|                 var engineLaunch = Engine as IDebugEngineLaunch2;  | 
|                 engineLaunch.TerminateProcess(this as IDebugProcess2);  | 
|             }  | 
|         }  | 
|   | 
|   | 
|         #region //////////////////// Execution Control ////////////////////////////////////////////  | 
|   | 
|         public int /*IDebugProgram3*/ Continue(IDebugThread2 pThread)  | 
|         {  | 
|             Debugger.Run();  | 
|             return VSConstants.S_OK;  | 
|         }  | 
|   | 
|         public int /*IDebugProgram3*/ ExecuteOnThread(IDebugThread2 pThread)  | 
|         {  | 
|             Debugger.Run();  | 
|             return VSConstants.S_OK;  | 
|         }  | 
|   | 
|         public int /*IDebugProgram3*/ Step(  | 
|             IDebugThread2 pThread,  | 
|             enum_STEPKIND sk,  | 
|             enum_STEPUNIT Step)  | 
|         {  | 
|             if (sk == enum_STEPKIND.STEP_OVER)  | 
|                 Debugger.StepOver();  | 
|             else if (sk == enum_STEPKIND.STEP_INTO)  | 
|                 Debugger.StepInto();  | 
|             else if (sk == enum_STEPKIND.STEP_OUT)  | 
|                 Debugger.StepOut();  | 
|             else  | 
|                 return VSConstants.E_FAIL;  | 
|             return VSConstants.S_OK;  | 
|         }  | 
|   | 
|         void IDebuggerEventSink.NotifyBreak()  | 
|         {  | 
|             BreakAllProcesses = false;  | 
|             DebugEvent.Send(new StepCompleteEvent(this));  | 
|         }  | 
|   | 
|         #endregion //////////////////// Execution Control /////////////////////////////////////////  | 
|   | 
|   | 
|         #region //////////////////// Breakpoints //////////////////////////////////////////////////  | 
|   | 
|         public void SetBreakpoint(Breakpoint breakpoint)  | 
|         {  | 
|             Debugger.SetBreakpoint(breakpoint);  | 
|         }  | 
|   | 
|         public void NotifyBreakpointSet(Breakpoint breakpoint)  | 
|         {  | 
|             DebugEvent.Send(new BreakpointBoundEvent(breakpoint));  | 
|         }  | 
|   | 
|         public void ClearBreakpoint(Breakpoint breakpoint)  | 
|         {  | 
|             Debugger.ClearBreakpoint(breakpoint);  | 
|         }  | 
|   | 
|         public void NotifyBreakpointCleared(Breakpoint breakpoint)  | 
|         {  | 
|             breakpoint.Parent.DisposeBreakpoint(breakpoint);  | 
|         }  | 
|   | 
|         public void NotifyBreakpointHit(Breakpoint breakpoint)  | 
|         {  | 
|             BreakAllProcesses = false;  | 
|             DebugEvent.Send(new BreakpointEvent(this, BoundBreakpointsEnum.Create(breakpoint)));  | 
|         }  | 
|   | 
|         static bool BreakAllProcesses  | 
|         {  | 
|             get  | 
|             {  | 
|                 return ((bool)QtVsToolsPackage.Instance.Dte  | 
|                     .Properties["Debugging", "General"]  | 
|                     .Item("BreakAllProcesses")  | 
|                     .Value);  | 
|             }  | 
|             set  | 
|             {  | 
|                 QtVsToolsPackage.Instance.Dte  | 
|                     .Properties["Debugging", "General"]  | 
|                     .Item("BreakAllProcesses")  | 
|                     .let_Value(value ? "True" : "False");  | 
|             }  | 
|         }  | 
|   | 
|         #endregion //////////////////// Breakpoints ///////////////////////////////////////////////  | 
|   | 
|   | 
|         #region //////////////////// Call Stack ///////////////////////////////////////////////////  | 
|   | 
|         void IDebuggerEventSink.NotifyStackContext(IList<FrameInfo> frames)  | 
|         {  | 
|             CurrentFrames.Clear();  | 
|             foreach (var frame in frames) {  | 
|                 CurrentFrames.Add(StackFrame.Create(frame.Name, frame.Number, frame.Scopes,  | 
|                     CodeContext.Create(Engine, this,  | 
|                     Engine.FileSystem[frame.QrcPath].FilePath, (uint)frame.Line)));  | 
|             }  | 
|         }  | 
|   | 
|         public void Refresh()  | 
|         {  | 
|             CurrentFrames.ForEach(x => x.Refresh());  | 
|         }  | 
|   | 
|         int IDebugThread2.EnumFrameInfo(  | 
|             enum_FRAMEINFO_FLAGS dwFieldSpec,  | 
|             uint nRadix,  | 
|             out IEnumDebugFrameInfo2 ppEnum)  | 
|         {  | 
|             ppEnum = null;  | 
|   | 
|             if (CurrentFrames == null || CurrentFrames.Count == 0) {  | 
|                 ppEnum = FrameInfoEnum.Create();  | 
|                 return VSConstants.S_OK;  | 
|             }  | 
|   | 
|             var frameInfos = new List<FRAMEINFO>();  | 
|             foreach (var frame in CurrentFrames) {  | 
|                 var frameInfo = new FRAMEINFO[1];  | 
|                 (frame as IDebugStackFrame2).GetInfo(dwFieldSpec, nRadix, frameInfo);  | 
|                 frameInfos.Add(frameInfo[0]);  | 
|             }  | 
|   | 
|             ppEnum = FrameInfoEnum.Create(frameInfos);  | 
|             return VSConstants.S_OK;  | 
|         }  | 
|   | 
|         #endregion //////////////////// Call Stack ////////////////////////////////////////////////  | 
|   | 
|   | 
|         #region //////////////////// Info /////////////////////////////////////////////////////////  | 
|   | 
|         class ProgramInfo : InfoHelper<ProgramInfo>  | 
|         {  | 
|             public uint? ThreadId { get; set; }  | 
|             public uint? SuspendCount { get; set; }  | 
|             public uint? ThreadState { get; set; }  | 
|             public string Priority { get; set; }  | 
|             public string Name { get; set; }  | 
|             public string Location { get; set; }  | 
|             public string DisplayName { get; set; }  | 
|             public uint? DisplayNamePriority { get; set; }  | 
|             public uint? ThreadCategory { get; set; }  | 
|             public uint? AffinityMask { get; set; }  | 
|             public int? PriorityId { get; set; }  | 
|             public string ModuleName { get; set; }  | 
|             public string ModuleUrl { get; set; }  | 
|         }  | 
|   | 
|         ProgramInfo Info  | 
|         {  | 
|             get  | 
|             {  | 
|                 return new ProgramInfo  | 
|                 {  | 
|                     ThreadId = Debugger.ThreadId,  | 
|                     SuspendCount = 0,  | 
|                     ThreadCategory = 0,  | 
|                     AffinityMask = 0,  | 
|                     PriorityId = 0,  | 
|                     ThreadState = (uint)enum_THREADSTATE.THREADSTATE_RUNNING,  | 
|                     Priority = "Normal",  | 
|                     Location = "",  | 
|                     Name = Name,  | 
|                     DisplayName = Name,  | 
|                     DisplayNamePriority = 10, // Give this display name a higher priority  | 
|                                               // than the default (0) so that it will  | 
|                                               // actually be displayed  | 
|                     ModuleName = Path.GetFileName(ExecPath),  | 
|                     ModuleUrl = ExecPath  | 
|   | 
|                 };  | 
|             }  | 
|         }  | 
|   | 
|         static readonly ProgramInfo.Mapping MappingToTHREADPROPERTIES =  | 
|   | 
|         #region //////////////////// THREADPROPERTIES <-- ProgramInfo /////////////////////////////  | 
|             // r: Ref<THREADPROPERTIES>  | 
|             // f: enum_THREADPROPERTY_FIELDS  | 
|             // i: ProgramInfo  | 
|             // v: value of i.<<property>>  | 
|   | 
|             new ProgramInfo.Mapping<THREADPROPERTIES, enum_THREADPROPERTY_FIELDS>  | 
|                 ((r, f) => r.s.dwFields |= f)  | 
|             {  | 
|                 { enum_THREADPROPERTY_FIELDS.TPF_ID,  | 
|                     (r, v) => r.s.dwThreadId = v, i => i.ThreadId },  | 
|   | 
|                 { enum_THREADPROPERTY_FIELDS.TPF_SUSPENDCOUNT,  | 
|                     (r, v) => r.s.dwSuspendCount = v, i => i.SuspendCount },  | 
|   | 
|                 { enum_THREADPROPERTY_FIELDS.TPF_STATE,  | 
|                     (r, v) => r.s.dwThreadState = v, i => i.ThreadState },  | 
|   | 
|                 { enum_THREADPROPERTY_FIELDS.TPF_PRIORITY,  | 
|                     (r, v) => r.s.bstrPriority = v, i => i.Priority },  | 
|   | 
|                 { enum_THREADPROPERTY_FIELDS.TPF_NAME,  | 
|                     (r, v) => r.s.bstrName = v, i => i.Name },  | 
|   | 
|                 { enum_THREADPROPERTY_FIELDS.TPF_LOCATION,  | 
|                     (r, v) => r.s.bstrLocation = v, i => i.Location },  | 
|             };  | 
|   | 
|         #endregion //////////////////// THREADPROPERTIES <-- ProgramInfo //////////////////////////  | 
|   | 
|   | 
|         static readonly ProgramInfo.Mapping MappingToTHREADPROPERTIES100 =  | 
|   | 
|         #region //////////////////// THREADPROPERTIES100 <-- ProgramInfo //////////////////  | 
|             // r: Ref<THREADPROPERTIES100>  | 
|             // f: enum_THREADPROPERTY_FIELDS100  | 
|             // i: ProgramInfo  | 
|             // v: value of i.<<property>>  | 
|   | 
|             new ProgramInfo.Mapping<THREADPROPERTIES100, enum_THREADPROPERTY_FIELDS100>  | 
|                 ((r, f) => r.s.dwFields |= (uint)f)  | 
|             {  | 
|                 { enum_THREADPROPERTY_FIELDS100.TPF100_ID,  | 
|                     (r, v) => r.s.dwThreadId = v, i => i.ThreadId },  | 
|   | 
|                 { enum_THREADPROPERTY_FIELDS100.TPF100_SUSPENDCOUNT,  | 
|                     (r, v) => r.s.dwSuspendCount = v, i => i.SuspendCount },  | 
|   | 
|                 { enum_THREADPROPERTY_FIELDS100.TPF100_STATE,  | 
|                     (r, v) => r.s.dwThreadState = v, i => i.ThreadState },  | 
|   | 
|                 { enum_THREADPROPERTY_FIELDS100.TPF100_PRIORITY,  | 
|                     (r, v) => r.s.bstrPriority = v, i => i.Priority },  | 
|   | 
|                 { enum_THREADPROPERTY_FIELDS100.TPF100_NAME,  | 
|                     (r, v) => r.s.bstrName = v, i => i.Name },  | 
|   | 
|                 { enum_THREADPROPERTY_FIELDS100.TPF100_LOCATION,  | 
|                     (r, v) => r.s.bstrLocation = v, i => i.Location },  | 
|   | 
|                 { enum_THREADPROPERTY_FIELDS100.TPF100_DISPLAY_NAME,  | 
|                     (r, v) => r.s.bstrDisplayName = v, i => i.DisplayName },  | 
|   | 
|                 { enum_THREADPROPERTY_FIELDS100.TPF100_DISPLAY_NAME,  | 
|                   enum_THREADPROPERTY_FIELDS100.TPF100_DISPLAY_NAME_PRIORITY,  | 
|                     (r, v) => r.s.DisplayNamePriority = v, i => i.DisplayNamePriority },  | 
|   | 
|                 { enum_THREADPROPERTY_FIELDS100.TPF100_CATEGORY,  | 
|                     (r, v) => r.s.dwThreadCategory = v, i => i.ThreadCategory },  | 
|   | 
|                 { enum_THREADPROPERTY_FIELDS100.TPF100_AFFINITY,  | 
|                     (r, v) => r.s.AffinityMask = v, i => i.AffinityMask },  | 
|   | 
|                 { enum_THREADPROPERTY_FIELDS100.TPF100_PRIORITY_ID,  | 
|                     (r, v) => r.s.priorityId = v, i => i.PriorityId },  | 
|             };  | 
|   | 
|         #endregion //////////////////// THREADPROPERTIES100 <-- ProgramInfo ///////////////////////  | 
|   | 
|   | 
|         static readonly ProgramInfo.Mapping MappingToMODULE_INFO =  | 
|   | 
|         #region //////////////////// MODULE_INFO <-- ProgramInfo //////////////////////////////////  | 
|             // r: Ref<MODULE_INFO>  | 
|             // f: enum_MODULE_INFO_FIELDS  | 
|             // i: ProgramInfo  | 
|             // v: value of i.<<property>>  | 
|   | 
|             new ProgramInfo.Mapping<MODULE_INFO, enum_MODULE_INFO_FIELDS>  | 
|                 ((r, bit) => r.s.dwValidFields |= bit)  | 
|             {  | 
|                 { enum_MODULE_INFO_FIELDS.MIF_NAME,  | 
|                     (r, v) => r.s.m_bstrName = v, i => i.ModuleName },  | 
|   | 
|                 { enum_MODULE_INFO_FIELDS.MIF_URL,  | 
|                     (r, v) => r.s.m_bstrUrl = v, i => i.ModuleUrl },  | 
|             };  | 
|   | 
|         #endregion //////////////////// MODULE_INFO <-- ProgramInfo ///////////////////////////////  | 
|   | 
|   | 
|         public int /*IDebugProgram3*/ GetName(out string pbstrName)  | 
|         {  | 
|             pbstrName = Program.Name;  | 
|             return VSConstants.S_OK;  | 
|         }  | 
|   | 
|         int IDebugProgramNode2.GetProgramName(out string pbstrProgramName)  | 
|         {  | 
|             return GetName(out pbstrProgramName);  | 
|         }  | 
|   | 
|         int IDebugProgramNode2.GetEngineInfo(out string pbstrEngine, out Guid pguidEngine)  | 
|         {  | 
|             pbstrEngine = "QML";  | 
|             pguidEngine = QmlEngine.Id;  | 
|             return VSConstants.S_OK;  | 
|         }  | 
|   | 
|         int IDebugProgramNode2.GetHostPid(AD_PROCESS_ID[] pHostProcessId)  | 
|         {  | 
|             pHostProcessId[0].ProcessIdType = (uint)enum_AD_PROCESS_ID.AD_PROCESS_ID_GUID;  | 
|             pHostProcessId[0].guidProcessId = ProcessId;  | 
|             return VSConstants.S_OK;  | 
|         }  | 
|   | 
|         int IDebugProcess2.GetPhysicalProcessId(AD_PROCESS_ID[] pProcessId)  | 
|         {  | 
|             pProcessId[0].ProcessIdType = (uint)enum_AD_PROCESS_ID.AD_PROCESS_ID_GUID;  | 
|             pProcessId[0].guidProcessId = ProcessId;  | 
|             return VSConstants.S_OK;  | 
|         }  | 
|   | 
|         int IDebugProcess2.GetProcessId(out Guid pguidProcessId)  | 
|         {  | 
|             pguidProcessId = ProcessId;  | 
|             return VSConstants.S_OK;  | 
|         }  | 
|   | 
|         int IDebugProcess2.GetPort(out IDebugPort2 ppPort)  | 
|         {  | 
|             return NativeProc.GetPort(out ppPort);  | 
|         }  | 
|   | 
|         public int /*IDebugProgram3*/ GetProgramId(out Guid pguidProgramId)  | 
|         {  | 
|             pguidProgramId = ProgramId;  | 
|             return VSConstants.S_OK;  | 
|         }  | 
|   | 
|         int IDebugThread2.GetThreadProperties(  | 
|             enum_THREADPROPERTY_FIELDS dwFields,  | 
|             THREADPROPERTIES[] ptp)  | 
|         {  | 
|             Info.Map(MappingToTHREADPROPERTIES, dwFields, out ptp[0]);  | 
|             return VSConstants.S_OK;  | 
|         }  | 
|   | 
|         int IDebugThread100.GetThreadProperties100(uint dwFields, THREADPROPERTIES100[] ptp)  | 
|         {  | 
|             Info.Map(MappingToTHREADPROPERTIES100, dwFields, out ptp[0]);  | 
|             return VSConstants.S_OK;  | 
|         }  | 
|   | 
|         int IDebugThread2.GetName(out string pbstrName)  | 
|         {  | 
|             pbstrName = Name;  | 
|             return VSConstants.S_OK;  | 
|         }  | 
|   | 
|         int IDebugThread2.GetThreadId(out uint pdwThreadId)  | 
|         {  | 
|             pdwThreadId = (uint)Debugger.ThreadId;  | 
|             return VSConstants.S_OK;  | 
|         }  | 
|   | 
|         public int /*IDebugModule3*/ GetInfo(enum_MODULE_INFO_FIELDS dwFields, MODULE_INFO[] pinfo)  | 
|         {  | 
|             Info.Map(MappingToMODULE_INFO, dwFields, out pinfo[0]);  | 
|             return VSConstants.S_OK;  | 
|         }  | 
|   | 
|         public int /*IDebugProgram3*/ EnumThreads(out IEnumDebugThreads2 ppEnum)  | 
|         {  | 
|             ppEnum = ThreadEnum.Create(this);  | 
|             return VSConstants.S_OK;  | 
|         }  | 
|   | 
|         public int /*IDebugProgram3*/ EnumModules(out IEnumDebugModules2 ppEnum)  | 
|         {  | 
|             ppEnum = ModuleEnum.Create(this);  | 
|             return VSConstants.S_OK;  | 
|         }  | 
|   | 
|         #endregion //////////////////// Info //////////////////////////////////////////////////////  | 
|   | 
|     }  | 
| }  |