PageRenderTime 34ms CodeModel.GetById 12ms RepoModel.GetById 0ms app.codeStats 0ms

/Python/Product/IronPython/Debugger/IronPythonLauncher.cs

https://gitlab.com/SplatoonModdingHub/PTVS
C# | 332 lines | 292 code | 19 blank | 21 comment | 12 complexity | 60c105ca6fa7497fda282b1c018bb158 MD5 | raw file
  1. // Python Tools for Visual Studio
  2. // Copyright(c) Microsoft Corporation
  3. // All rights reserved.
  4. //
  5. // Licensed under the Apache License, Version 2.0 (the License); you may not use
  6. // this file except in compliance with the License. You may obtain a copy of the
  7. // License at http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS
  10. // OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY
  11. // IMPLIED WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE,
  12. // MERCHANTABLITY OR NON-INFRINGEMENT.
  13. //
  14. // See the Apache Version 2.0 License for specific language governing
  15. // permissions and limitations under the License.
  16. using System;
  17. using System.Diagnostics;
  18. using System.IO;
  19. using System.Linq;
  20. using System.Net.NetworkInformation;
  21. using System.Runtime.InteropServices;
  22. using System.Windows.Forms;
  23. using Microsoft.PythonTools;
  24. using Microsoft.PythonTools.Debugger;
  25. using Microsoft.PythonTools.Interpreter;
  26. using Microsoft.PythonTools.Project;
  27. using Microsoft.VisualStudio;
  28. using Microsoft.VisualStudio.Shell;
  29. using Microsoft.VisualStudio.Shell.Interop;
  30. using Microsoft.VisualStudioTools.Project;
  31. using Microsoft.Win32;
  32. namespace Microsoft.IronPythonTools.Debugger {
  33. class IronPythonLauncher : IProjectLauncher {
  34. private static Process _chironProcess;
  35. private static string _chironDir;
  36. private static int _chironPort;
  37. private static readonly Guid _cpyInterpreterGuid = new Guid("{2AF0F10D-7135-4994-9156-5D01C9C11B7E}");
  38. private static readonly Guid _cpy64InterpreterGuid = new Guid("{9A7A9026-48C1-4688-9D5D-E5699D47D074}");
  39. private readonly IPythonProject _project;
  40. private readonly PythonToolsService _pyService;
  41. private readonly IServiceProvider _serviceProvider;
  42. public IronPythonLauncher(IServiceProvider serviceProvider, PythonToolsService pyService, IPythonProject project) {
  43. _serviceProvider = serviceProvider;
  44. _pyService = pyService;
  45. _project = project;
  46. }
  47. #region IPythonLauncher Members
  48. private static readonly Lazy<string> NoIronPythonHelpPage = new Lazy<string>(() => {
  49. try {
  50. var path = Path.GetDirectoryName(typeof(IronPythonLauncher).Assembly.Location);
  51. return Path.Combine(path, "NoIronPython.mht");
  52. } catch (ArgumentException) {
  53. } catch (NotSupportedException) {
  54. }
  55. return null;
  56. });
  57. public int LaunchProject(bool debug) {
  58. LaunchConfiguration config;
  59. try {
  60. config = _project.GetLaunchConfigurationOrThrow();
  61. } catch (NoInterpretersException) {
  62. throw new NoInterpretersException(null, NoIronPythonHelpPage.Value);
  63. }
  64. return Launch(config, debug);
  65. }
  66. public int LaunchFile(string file, bool debug) {
  67. LaunchConfiguration config;
  68. try {
  69. config = _project.GetLaunchConfigurationOrThrow();
  70. } catch (NoInterpretersException) {
  71. throw new NoInterpretersException(null, NoIronPythonHelpPage.Value);
  72. }
  73. return Launch(config, debug);
  74. }
  75. private int Launch(LaunchConfiguration config, bool debug) {
  76. //if (factory.Id == _cpyInterpreterGuid || factory.Id == _cpy64InterpreterGuid) {
  77. // MessageBox.Show(
  78. // "The project is currently set to use the .NET debugger for IronPython debugging but the project is configured to start with a CPython interpreter.\r\n\r\nTo fix this change the debugger type in project properties->Debug->Launch mode.\r\nIf IronPython is not an available interpreter you may need to download it from http://ironpython.codeplex.com.",
  79. // "Python Tools for Visual Studio");
  80. // return VSConstants.S_OK;
  81. //}
  82. string extension = Path.GetExtension(config.ScriptName);
  83. if (string.Equals(extension, ".html", StringComparison.OrdinalIgnoreCase) ||
  84. string.Equals(extension, ".htm", StringComparison.OrdinalIgnoreCase)) {
  85. try {
  86. StartSilverlightApp(config, debug);
  87. } catch (ChironNotFoundException ex) {
  88. MessageBox.Show(ex.Message, "Python Tools for Visual Studio");
  89. }
  90. return VSConstants.S_OK;
  91. }
  92. try {
  93. if (debug) {
  94. if (string.IsNullOrEmpty(config.InterpreterArguments)) {
  95. config.InterpreterArguments = "-X:Debug";
  96. } else if (config.InterpreterArguments.IndexOf("-X:Debug", StringComparison.InvariantCultureIgnoreCase) < 0) {
  97. config.InterpreterArguments = "-X:Debug " + config.InterpreterArguments;
  98. }
  99. var debugStdLib = _project.GetProperty(IronPythonLauncherOptions.DebugStandardLibrarySetting);
  100. bool debugStdLibResult;
  101. if (!bool.TryParse(debugStdLib, out debugStdLibResult) || !debugStdLibResult) {
  102. string interpDir = config.Interpreter.PrefixPath;
  103. config.InterpreterArguments += " -X:NoDebug \"" + System.Text.RegularExpressions.Regex.Escape(Path.Combine(interpDir, "Lib\\")) + ".*\"";
  104. }
  105. using (var dti = DebugLaunchHelper.CreateDebugTargetInfo(_serviceProvider, config)) {
  106. // Set the CLR debugger
  107. dti.Info.clsidCustom = VSConstants.CLSID_ComPlusOnlyDebugEngine;
  108. dti.Info.grfLaunch = (uint)__VSDBGLAUNCHFLAGS.DBGLAUNCH_StopDebuggingOnEnd;
  109. // Clear the CLSID list while launching, then restore it
  110. // so Dispose() can free it.
  111. var clsidList = dti.Info.pClsidList;
  112. dti.Info.pClsidList = IntPtr.Zero;
  113. try {
  114. dti.Launch();
  115. } finally {
  116. dti.Info.pClsidList = clsidList;
  117. }
  118. }
  119. } else {
  120. var psi = DebugLaunchHelper.CreateProcessStartInfo(_serviceProvider, config);
  121. Process.Start(psi).Dispose();
  122. }
  123. } catch (FileNotFoundException) {
  124. }
  125. return VSConstants.S_OK;
  126. }
  127. #endregion
  128. private static Guid? guidSilverlightDebug = new Guid("{032F4B8C-7045-4B24-ACCF-D08C9DA108FE}");
  129. public void StartSilverlightApp(LaunchConfiguration config, bool debug) {
  130. var root = Path.GetFullPath(config.WorkingDirectory).TrimEnd('\\');
  131. var file = Path.Combine(root, config.ScriptName);
  132. int port = EnsureChiron(root);
  133. var url = string.Format(
  134. "http://localhost:{0}/{1}",
  135. port,
  136. (file.StartsWith(root + "\\") ? file.Substring(root.Length + 1) : file.TrimStart('\\')).Replace('\\', '/')
  137. );
  138. StartInBrowser(url, debug ? guidSilverlightDebug : null);
  139. }
  140. public void StartInBrowser(string url, Guid? debugEngine) {
  141. if (debugEngine.HasValue) {
  142. // launch via VS debugger, it'll take care of figuring out the browsers
  143. VsDebugTargetInfo dbgInfo = new VsDebugTargetInfo();
  144. dbgInfo.dlo = (DEBUG_LAUNCH_OPERATION)_DEBUG_LAUNCH_OPERATION3.DLO_LaunchBrowser;
  145. dbgInfo.bstrExe = url;
  146. dbgInfo.clsidCustom = debugEngine.Value;
  147. dbgInfo.grfLaunch = (uint)__VSDBGLAUNCHFLAGS.DBGLAUNCH_StopDebuggingOnEnd | (uint)__VSDBGLAUNCHFLAGS4.DBGLAUNCH_UseDefaultBrowser;
  148. dbgInfo.cbSize = (uint)Marshal.SizeOf(dbgInfo);
  149. VsShellUtilities.LaunchDebugger(_serviceProvider, dbgInfo);
  150. } else {
  151. // run the users default browser
  152. var handler = GetBrowserHandlerProgId();
  153. var browserCmd = (string)Registry.ClassesRoot.OpenSubKey(handler).OpenSubKey("shell").OpenSubKey("open").OpenSubKey("command").GetValue("");
  154. if (browserCmd.IndexOf("%1") != -1) {
  155. browserCmd = browserCmd.Replace("%1", url);
  156. } else {
  157. browserCmd = browserCmd + " " + url;
  158. }
  159. bool inQuote = false;
  160. string cmdLine = null;
  161. for (int i = 0; i < browserCmd.Length; i++) {
  162. if (browserCmd[i] == '"') {
  163. inQuote = !inQuote;
  164. }
  165. if (browserCmd[i] == ' ' && !inQuote) {
  166. cmdLine = browserCmd.Substring(0, i);
  167. break;
  168. }
  169. }
  170. if (cmdLine == null) {
  171. cmdLine = browserCmd;
  172. }
  173. Process.Start(cmdLine, browserCmd.Substring(cmdLine.Length));
  174. }
  175. }
  176. private static string GetBrowserHandlerProgId() {
  177. try {
  178. return (string)Registry.CurrentUser.OpenSubKey("Software").OpenSubKey("Microsoft").OpenSubKey("Windows").OpenSubKey("CurrentVersion").OpenSubKey("Explorer").OpenSubKey("FileExts").OpenSubKey(".html").OpenSubKey("UserChoice").GetValue("Progid");
  179. } catch {
  180. return (string)Registry.ClassesRoot.OpenSubKey(".html").GetValue("");
  181. }
  182. }
  183. private int EnsureChiron(string/*!*/ webSiteRoot) {
  184. Debug.Assert(!webSiteRoot.EndsWith("\\"));
  185. if (_chironDir != webSiteRoot && _chironProcess != null && !_chironProcess.HasExited) {
  186. try {
  187. _chironProcess.Kill();
  188. } catch {
  189. // process already exited
  190. }
  191. _chironProcess = null;
  192. }
  193. if (_chironProcess == null || _chironProcess.HasExited) {
  194. // start Chiron
  195. var chironPath = ChironPath;
  196. // Get a free port
  197. _chironPort = GetFreePort();
  198. // TODO: race condition - the port might be taked by the time Chiron attempts to open it
  199. // TODO: we should wait for Chiron before launching the browser
  200. string commandLine = "/w:" + _chironPort + " /notification /d:";
  201. if (webSiteRoot.IndexOf(' ') != -1) {
  202. commandLine += "\"" + webSiteRoot + "\"";
  203. } else {
  204. commandLine += webSiteRoot;
  205. }
  206. ProcessStartInfo startInfo = new ProcessStartInfo(chironPath, commandLine);
  207. startInfo.CreateNoWindow = true;
  208. startInfo.UseShellExecute = false;
  209. _chironDir = webSiteRoot;
  210. _chironProcess = Process.Start(startInfo);
  211. }
  212. return _chironPort;
  213. }
  214. private static int GetFreePort() {
  215. return Enumerable.Range(new Random().Next(1200, 2000), 60000).Except(
  216. from connection in IPGlobalProperties.GetIPGlobalProperties().GetActiveTcpConnections()
  217. select connection.LocalEndPoint.Port
  218. ).First();
  219. }
  220. public string ChironPath {
  221. get {
  222. string result = GetPythonInstallDir();
  223. if (result != null) {
  224. result = Path.Combine(result, @"Silverlight\bin\Chiron.exe");
  225. if (File.Exists(result)) {
  226. return result;
  227. }
  228. }
  229. result = Path.Combine(Path.GetDirectoryName(typeof(IronPythonLauncher).Assembly.Location), "Chiron.exe");
  230. if (File.Exists(result)) {
  231. return result;
  232. }
  233. throw new ChironNotFoundException();
  234. }
  235. }
  236. internal static string GetPythonInstallDir() {
  237. using (var ipy = Registry.LocalMachine.OpenSubKey("SOFTWARE\\IronPython")) {
  238. if (ipy != null) {
  239. using (var twoSeven = ipy.OpenSubKey("2.7")) {
  240. if (twoSeven != null) {
  241. using (var installPath = twoSeven.OpenSubKey("InstallPath")) {
  242. var path = installPath.GetValue("") as string;
  243. if (path != null) {
  244. return path;
  245. }
  246. }
  247. }
  248. }
  249. }
  250. }
  251. var paths = Environment.GetEnvironmentVariable("PATH");
  252. if (paths != null) {
  253. foreach (string dir in paths.Split(Path.PathSeparator)) {
  254. try {
  255. if (IronPythonExistsIn(dir)) {
  256. return dir;
  257. }
  258. } catch {
  259. // ignore
  260. }
  261. }
  262. }
  263. return null;
  264. }
  265. private static bool IronPythonExistsIn(string/*!*/ dir) {
  266. return File.Exists(Path.Combine(dir, "ipy.exe"));
  267. }
  268. [Serializable]
  269. class ChironNotFoundException : Exception {
  270. public ChironNotFoundException()
  271. : this("Chiron.exe was not found. Ensure the Silverlight Tools component of IronPython has been installed.") {
  272. }
  273. public ChironNotFoundException(string message) : base(message) { }
  274. public ChironNotFoundException(string message, Exception inner) : base(message, inner) { }
  275. protected ChironNotFoundException(
  276. System.Runtime.Serialization.SerializationInfo info,
  277. System.Runtime.Serialization.StreamingContext context)
  278. : base(info, context) { }
  279. }
  280. }
  281. }