| /****************************************************************************  | 
| **  | 
| ** 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.Linq;  | 
| using System.Runtime.InteropServices;  | 
| using Microsoft.VisualStudio;  | 
| using Microsoft.VisualStudio.Debugger.Interop;  | 
|   | 
| namespace QtVsTools.Qml.Debug.AD7  | 
| {  | 
|     [ComVisible(true)]  | 
|     [Guid(CLSID_ENGINE)]  | 
|     sealed partial class QmlEngine :  | 
|   | 
|         IDebugEngine2,       // "This interface represents a debug engine (DE). It is used to manage  | 
|                              //  various aspects of a debugging session, from creating breakpoints  | 
|                              //  to setting and clearing exceptions."  | 
|   | 
|         IDebugEngineLaunch2, // "Used by a debug engine (DE) to launch and terminate programs."  | 
|   | 
|         IDebugEventCallback2 // "This interface is used by the debug engine (DE) to send debug  | 
|                              //  events to the session debug manager (SDM)."  | 
|     {  | 
|         const string CLSID_ENGINE = "fa2993e3-8b2a-40a6-8853-ac2db2daed5a";  | 
|         public static readonly Guid ClassId = new Guid(CLSID_ENGINE);  | 
|   | 
|         const string ID_ENGINE = "86102a1b-4378-4964-a7ed-21852a8afb7f";  | 
|         public static readonly Guid Id = new Guid(ID_ENGINE);  | 
|   | 
|         public IDebugEventCallback2 Callback { get; private set; }  | 
|   | 
|         public FileSystem FileSystem { get; private set; }  | 
|   | 
|         int IDebugEventCallback2.Event(  | 
|             IDebugEngine2 pEngine,  | 
|             IDebugProcess2 pProcess,  | 
|             IDebugProgram2 pProgram,  | 
|             IDebugThread2 pThread,  | 
|             IDebugEvent2 pEvent,  | 
|             ref Guid riidEvent,  | 
|             uint dwAttrib)  | 
|         {  | 
|             if (Callback == null)  | 
|                 return VSConstants.S_OK;  | 
|             return Callback.Event(pEngine, pProcess,  | 
|                 pProgram, pThread, pEvent, ref riidEvent, dwAttrib);  | 
|         }  | 
|   | 
|         public QmlEngine()  | 
|         {  | 
|             FileSystem = FileSystem.Create();  | 
|         }  | 
|   | 
|         Dictionary<Guid, Program> programs = new Dictionary<Guid, Program>();  | 
|         public IEnumerable<Program> Programs  | 
|         {  | 
|             get { return ThreadSafe(() => programs.Values.ToList()); }  | 
|         }  | 
|   | 
|         HashSet<PendingBreakpoint> pendingBreakpoints = new HashSet<PendingBreakpoint>();  | 
|         public IEnumerable<PendingBreakpoint> PendingBreakpoints  | 
|         {  | 
|             get { return ThreadSafe(() => pendingBreakpoints.ToList()); }  | 
|         }  | 
|   | 
|         int IDebugEngine2.GetEngineId(out Guid pguidEngine)  | 
|         {  | 
|             pguidEngine = Id;  | 
|             return VSConstants.S_OK;  | 
|         }  | 
|   | 
|         int IDebugEngineLaunch2.LaunchSuspended(string pszServer, IDebugPort2 pPort,  | 
|             string pszExe, string pszArgs, string pszDir, string bstrEnv, string pszOptions,  | 
|             enum_LAUNCH_FLAGS dwLaunchFlags, uint hStdInput, uint hStdOutput, uint hStdError,  | 
|             IDebugEventCallback2 pCallback, out IDebugProcess2 ppProcess)  | 
|         {  | 
|             ppProcess = null;  | 
|   | 
|             if (string.IsNullOrEmpty(pszOptions))  | 
|                 return VSConstants.E_FAIL;  | 
|   | 
|             uint procId;  | 
|             if (!uint.TryParse(pszOptions, out procId))  | 
|                 return VSConstants.E_FAIL;  | 
|   | 
|             var env = bstrEnv.Split(new[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries)  | 
|                 .Select(x => x.Split(new[] { '=' }))  | 
|                 .Where(x => x.Length >= 2)  | 
|                 .ToDictionary(x => x[0], x => x[1].Split(new[] { ';' }));  | 
|   | 
|             if (env.ContainsKey("QTRCC")) {  | 
|                 foreach (var rccFile in env["QTRCC"])  | 
|                     FileSystem.RegisterRccFile(rccFile);  | 
|             }  | 
|   | 
|             IDebugProcess2 nativeProc;  | 
|             var nativeProcId = new AD_PROCESS_ID  | 
|             {  | 
|                 ProcessIdType = (uint)enum_AD_PROCESS_ID.AD_PROCESS_ID_SYSTEM,  | 
|                 dwProcessId = procId  | 
|             };  | 
|             if (pPort.GetProcess(nativeProcId, out nativeProc) != VSConstants.S_OK)  | 
|                 return VSConstants.E_FAIL;  | 
|   | 
|             var program = Program.Create(this, nativeProc, pszExe, pszArgs);  | 
|             if (program == null)  | 
|                 return VSConstants.E_FAIL;  | 
|   | 
|             programs.Add(program.ProcessId, program);  | 
|             ppProcess = program;  | 
|             return VSConstants.S_OK;  | 
|         }  | 
|   | 
|         int IDebugEngineLaunch2.ResumeProcess(IDebugProcess2 process)  | 
|         {  | 
|             var program = process as Program;  | 
|             if (program == null)  | 
|                 return VSConstants.E_FAIL;  | 
|   | 
|             IDebugPort2 port;  | 
|             if (process.GetPort(out port) != VSConstants.S_OK)  | 
|                 return VSConstants.E_FAIL;  | 
|   | 
|             string portName;  | 
|             port.GetPortName(out portName);  | 
|   | 
|             Guid guidPort;  | 
|             port.GetPortId(out guidPort);  | 
|   | 
|             IDebugDefaultPort2 defaultPort = (IDebugDefaultPort2)port;  | 
|   | 
|             IDebugPortNotify2 portNotify;  | 
|             if (defaultPort.GetPortNotify(out portNotify) != VSConstants.S_OK)  | 
|                 return VSConstants.E_FAIL;  | 
|   | 
|             if (portNotify.AddProgramNode(program) != VSConstants.S_OK)  | 
|                 return VSConstants.E_FAIL;  | 
|   | 
|             return VSConstants.S_OK;  | 
|         }  | 
|   | 
|         int IDebugEngine2.Attach(  | 
|             IDebugProgram2[] rgpPrograms,  | 
|             IDebugProgramNode2[] rgpProgramNodes,  | 
|             uint celtPrograms,  | 
|             IDebugEventCallback2 pCallback,  | 
|             enum_ATTACH_REASON dwReason)  | 
|         {  | 
|             var program = rgpProgramNodes[0] as Program;  | 
|             if (program == null)  | 
|                 return VSConstants.E_FAIL;  | 
|   | 
|             Callback = pCallback;  | 
|   | 
|             DebugEvent.Send(new EngineCreateEvent(this));  | 
|   | 
|             Guid pguidProgramId;  | 
|             if (rgpPrograms[0].GetProgramId(out pguidProgramId) != VSConstants.S_OK)  | 
|                 return VSConstants.E_FAIL;  | 
|   | 
|             program.ProgramId = pguidProgramId;  | 
|   | 
|             DebugEvent.Send(new ProgramCreateEvent(program));  | 
|             DebugEvent.Send(new ThreadCreateEvent(program));  | 
|             DebugEvent.Send(new LoadCompleteEvent(program));  | 
|             DebugEvent.Send(new EntryPointEvent(program));  | 
|   | 
|             program.OutputWriteLine("Connecting to the QML runtime...");  | 
|   | 
|             return VSConstants.S_OK;  | 
|         }  | 
|   | 
|         int IDebugEngineLaunch2.CanTerminateProcess(IDebugProcess2 pProcess)  | 
|         {  | 
|             Guid procId;  | 
|             if (pProcess.GetProcessId(out procId) != VSConstants.S_OK)  | 
|                 return VSConstants.E_FAIL;  | 
|   | 
|             Program program;  | 
|             if (!programs.TryGetValue(procId, out program))  | 
|                 return VSConstants.S_FALSE;  | 
|   | 
|             return VSConstants.S_OK;  | 
|         }  | 
|   | 
|         public bool ProgramIsRunning(Program program)  | 
|         {  | 
|             return programs.ContainsKey(program.ProcessId);  | 
|         }  | 
|   | 
|         int IDebugEngineLaunch2.TerminateProcess(IDebugProcess2 pProcess)  | 
|         {  | 
|             Guid procId;  | 
|             if (pProcess.GetProcessId(out procId) != VSConstants.S_OK)  | 
|                 return VSConstants.E_FAIL;  | 
|   | 
|             Program program;  | 
|             if (!programs.TryGetValue(procId, out program))  | 
|                 return VSConstants.S_FALSE;  | 
|   | 
|             programs.Remove(procId);  | 
|   | 
|             DebugEvent.Send(new ThreadDestroyEvent(program, 0));  | 
|             DebugEvent.Send(new ProgramDestroyEvent(program, 0));  | 
|   | 
|             return VSConstants.S_OK;  | 
|         }  | 
|   | 
|         int IDebugEngine2.ContinueFromSynchronousEvent(IDebugEvent2 pEvent)  | 
|         {  | 
|             var evtProgramDestroy = pEvent as ProgramDestroyEvent;  | 
|             if (evtProgramDestroy != null)  | 
|                 evtProgramDestroy.Program.Dispose();  | 
|   | 
|             return VSConstants.S_OK;  | 
|         }  | 
|   | 
|         int IDebugEngine2.CreatePendingBreakpoint(  | 
|             IDebugBreakpointRequest2 pBPRequest,  | 
|             out IDebugPendingBreakpoint2 ppPendingBP)  | 
|         {  | 
|             ppPendingBP = null;  | 
|   | 
|             var pendingBreakpoint = PendingBreakpoint.Create(this, pBPRequest);  | 
|             if (pendingBreakpoint == null)  | 
|                 return VSConstants.E_FAIL;  | 
|   | 
|             ppPendingBP = pendingBreakpoint;  | 
|             pendingBreakpoints.Add(pendingBreakpoint);  | 
|   | 
|             return VSConstants.S_OK;  | 
|         }  | 
|   | 
|         public void DisposePendingBreakpoint(PendingBreakpoint pendingBreakpoint)  | 
|         {  | 
|             pendingBreakpoints.Remove(pendingBreakpoint);  | 
|             pendingBreakpoint.Dispose();  | 
|         }  | 
|   | 
|         public void OutputWriteLine(string msg)  | 
|         {  | 
|             DebugEvent.Send(new OutputStringEvent(this, msg + "\r\n"));  | 
|         }  | 
|   | 
|         #region //////////////////// Concurrent ///////////////////////////////////////////////////  | 
|   | 
|         LocalConcurrent concurrent = new LocalConcurrent();  | 
|         class LocalConcurrent : Concurrent  | 
|         {  | 
|             public void LocalThreadSafe(Action action)  | 
|             { ThreadSafe(action); }  | 
|   | 
|             public T LocalThreadSafe<T>(Func<T> func)  | 
|             { return ThreadSafe(func); }  | 
|   | 
|             public new bool Atomic(Func<bool> test, Action action, Action actionElse = null)  | 
|             { return base.Atomic(test, action, actionElse); }  | 
|         }  | 
|   | 
|         void ThreadSafe(Action action)  | 
|         {  | 
|             concurrent.LocalThreadSafe(action);  | 
|         }  | 
|   | 
|         T ThreadSafe<T>(Func<T> func)  | 
|         {  | 
|             return concurrent.LocalThreadSafe(func);  | 
|         }  | 
|   | 
|         bool Atomic(Func<bool> test, Action action, Action actionElse = null)  | 
|         {  | 
|             return concurrent.Atomic(test, action, actionElse);  | 
|         }  | 
|   | 
|         #endregion //////////////////// Concurrent ////////////////////////////////////////////////  | 
|     }  | 
|   | 
|     [ComVisible(true)]  | 
|     [Guid(CLSID_PROGRAMPROVIDER)]  | 
|     sealed partial class ProgramProvider :  | 
|   | 
|         IDebugProgramProvider2 // "This registered interface allows the session debug manager (SDM)  | 
|                                //  to obtain information about programs that have been "published"  | 
|                                //  through the IDebugProgramPublisher2 interface."  | 
|     {  | 
|         public const string CLSID_PROGRAMPROVIDER = "f2ff34e2-7fa5-461b-9e59-b5997ee0a637";  | 
|         public static readonly Guid ClassId = new Guid(CLSID_PROGRAMPROVIDER);  | 
|   | 
|         public ProgramProvider()  | 
|         { }  | 
|     }  | 
|   | 
|     public static class NativeEngine  | 
|     {  | 
|         const string ID_NATIVEENGINE = "3b476d35-a401-11d2-aad4-00c04f990171";  | 
|         public static readonly Guid Id = new Guid(ID_NATIVEENGINE);  | 
|   | 
|         const string ID_LANGUAGE_CPP = "3a12d0b7-c26c-11d0-b442-00a0244a1dd2";  | 
|         public static Guid IdLanguageCpp = new Guid(ID_LANGUAGE_CPP);  | 
|     }  | 
|   | 
|     public static class GdbEngine  | 
|     {  | 
|         const string ID_GDBENGINE = "ea6637c6-17df-45b5-a183-0951c54243bc";  | 
|         public static readonly Guid Id = new Guid(ID_GDBENGINE);  | 
|     }  | 
| }  |