| OLD | NEW |
| (Empty) |
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | |
| 2 // Use of this source code is governed by a BSD-style license that can be | |
| 3 // found in the LICENSE file. | |
| 4 | |
| 5 namespace NativeClientVSAddIn | |
| 6 { | |
| 7 using System; | |
| 8 using System.Collections.Generic; | |
| 9 using System.IO; | |
| 10 using System.Linq; | |
| 11 using System.Text; | |
| 12 using System.Windows.Forms; | |
| 13 | |
| 14 using EnvDTE; | |
| 15 using EnvDTE80; | |
| 16 using Microsoft.VisualStudio.VCProjectEngine; | |
| 17 | |
| 18 /// <summary> | |
| 19 /// This class contains functions and utilities which are run when the user | |
| 20 /// presses F5 or otherwise starts debugging from within Visual Studio. | |
| 21 /// </summary> | |
| 22 public class PluginDebuggerHelper | |
| 23 { | |
| 24 /// <summary> | |
| 25 /// This is the initial number of milliseconds to wait between | |
| 26 /// checking for plug-in processes to attach the debugger to. | |
| 27 /// </summary> | |
| 28 private const int InitialPluginCheckFrequency = 1000; | |
| 29 | |
| 30 /// <summary> | |
| 31 /// After a plug-in has been found, we slow the frequency of checking | |
| 32 /// for new ones. This value is in milliseconds. | |
| 33 /// </summary> | |
| 34 private const int RelaxedPluginCheckFrequency = 5000; | |
| 35 | |
| 36 /// <summary> | |
| 37 /// The web server port to default to if the user does not specify one. | |
| 38 /// </summary> | |
| 39 private const int DefaultWebServerPort = 5103; | |
| 40 | |
| 41 /// <summary> | |
| 42 /// The main visual studio object through which all Visual Studio functions
are executed. | |
| 43 /// </summary> | |
| 44 private DTE2 dte_; | |
| 45 | |
| 46 /// <summary> | |
| 47 /// Indicates the PluginDebuggerHelper is configured properly to run. | |
| 48 /// </summary> | |
| 49 private bool isProperlyInitialized_ = false; | |
| 50 | |
| 51 /// <summary> | |
| 52 /// Directory of the plug-in project we are debugging. | |
| 53 /// </summary> | |
| 54 private string pluginProjectDirectory_; | |
| 55 | |
| 56 /// <summary> | |
| 57 /// Directory where the plug-in assembly is placed. | |
| 58 /// </summary> | |
| 59 private string pluginOutputDirectory_; | |
| 60 | |
| 61 /// <summary> | |
| 62 /// Path to the actual plug-in assembly. | |
| 63 /// </summary> | |
| 64 private string pluginAssembly_; | |
| 65 | |
| 66 /// <summary> | |
| 67 /// Path to the NaCl IRT. | |
| 68 /// </summary> | |
| 69 private string irtPath_; | |
| 70 | |
| 71 /// <summary> | |
| 72 /// Path to the project's nmf file. | |
| 73 /// </summary> | |
| 74 private string manifestPath_; | |
| 75 | |
| 76 /// <summary> | |
| 77 /// Root directory of the installed NaCl SDK. | |
| 78 /// </summary> | |
| 79 private string sdkRootDirectory_; | |
| 80 | |
| 81 /// <summary> | |
| 82 /// If debugging a .nexe this is the nacl-gdb process object. | |
| 83 /// </summary> | |
| 84 private System.Diagnostics.Process gdbProcess_; | |
| 85 | |
| 86 /// <summary> | |
| 87 /// Path to NaCl-GDB executable. | |
| 88 /// </summary> | |
| 89 private string gdbPath_; | |
| 90 | |
| 91 /// <summary> | |
| 92 /// Path to the gdb initialization file that we auto-generate from the VS pr
oject. | |
| 93 /// </summary> | |
| 94 private string gdbInitFileName_; | |
| 95 | |
| 96 /// <summary> | |
| 97 /// The platform that the start-up project is currently configured with (NaC
l or PPAPI). | |
| 98 /// </summary> | |
| 99 private ProjectPlatformType projectPlatformType_; | |
| 100 | |
| 101 /// <summary> | |
| 102 /// When debugging is started this is the web server process object. | |
| 103 /// </summary> | |
| 104 private System.Diagnostics.Process webServer_; | |
| 105 | |
| 106 /// <summary> | |
| 107 /// Visual Studio output window pane that captures output from the web serve
r. | |
| 108 /// </summary> | |
| 109 private OutputWindowPane webServerOutputPane_; | |
| 110 | |
| 111 /// <summary> | |
| 112 /// Path to the web server executable. | |
| 113 /// </summary> | |
| 114 private string webServerExecutable_; | |
| 115 | |
| 116 /// <summary> | |
| 117 /// Arguments to be passed to the web server executable to start it. | |
| 118 /// </summary> | |
| 119 private string webServerArguments_; | |
| 120 | |
| 121 /// <summary> | |
| 122 /// Timer object that periodically calls a function to look for the plug-in
process to debug. | |
| 123 /// </summary> | |
| 124 private Timer pluginFinderTimer_; | |
| 125 | |
| 126 /// <summary> | |
| 127 /// List of process IDs which we should not attempt to attach the debugger t
o. Mainly this | |
| 128 /// list contains process IDs of processes we have already attached to. | |
| 129 /// </summary> | |
| 130 private List<uint> pluginFinderForbiddenPids_; | |
| 131 | |
| 132 /// <summary> | |
| 133 /// Process searcher class which allows us to query the system for running p
rocesses. | |
| 134 /// </summary> | |
| 135 private ProcessSearcher processSearcher_; | |
| 136 | |
| 137 /// <summary> | |
| 138 /// The main process of chrome that was started by Visual Studio during debu
gging. | |
| 139 /// </summary> | |
| 140 private System.Diagnostics.Process debuggedChromeMainProcess_; | |
| 141 | |
| 142 /// <summary> | |
| 143 /// Constructs the PluginDebuggerHelper. | |
| 144 /// Object is not usable until LoadProjectSettings() is called. | |
| 145 /// </summary> | |
| 146 /// <param name="dte">Automation object from Visual Studio.</param> | |
| 147 public PluginDebuggerHelper(DTE2 dte) | |
| 148 { | |
| 149 if (dte == null) | |
| 150 { | |
| 151 throw new ArgumentNullException("dte"); | |
| 152 } | |
| 153 | |
| 154 dte_ = dte; | |
| 155 | |
| 156 // Every second, check for a new instance of the plug-in to attach to. | |
| 157 // Note that although the timer itself runs on a separate thread, the even
t | |
| 158 // is fired from the main UI thread during message processing, thus we do
not | |
| 159 // need to worry about threading issues. | |
| 160 pluginFinderTimer_ = new Timer(); | |
| 161 pluginFinderTimer_.Tick += new EventHandler(FindAndAttachToPlugin); | |
| 162 pluginFinderForbiddenPids_ = new List<uint>(); | |
| 163 processSearcher_ = new ProcessSearcher(); | |
| 164 } | |
| 165 | |
| 166 /// <summary> | |
| 167 /// An event indicating a target plug-in was found on the system. | |
| 168 /// </summary> | |
| 169 public event EventHandler<PluginFoundEventArgs> PluginFoundEvent; | |
| 170 | |
| 171 /// <summary> | |
| 172 /// Specifies the type of plug-in being run in this debug session. | |
| 173 /// </summary> | |
| 174 private enum ProjectPlatformType | |
| 175 { | |
| 176 /// <summary> | |
| 177 /// Represents all non-pepper/non-nacl platform types. | |
| 178 /// </summary> | |
| 179 Other, | |
| 180 | |
| 181 /// <summary> | |
| 182 /// Indicates project platform is a trusted plug-in (nexe). | |
| 183 /// </summary> | |
| 184 NaCl, | |
| 185 | |
| 186 /// <summary> | |
| 187 /// Indicates project platform is an untrusted plug-in. | |
| 188 /// </summary> | |
| 189 Pepper | |
| 190 } | |
| 191 | |
| 192 /// <summary> | |
| 193 /// Initializes the PluginDebuggerHelper with the current project settings | |
| 194 /// If project settings are unsupported for NaCl/Pepper debugging then | |
| 195 /// the object is not initialized and we return false. | |
| 196 /// </summary> | |
| 197 /// <returns>True if the object is successfully initialized, false otherwise
.</returns> | |
| 198 public bool LoadProjectSettings() | |
| 199 { | |
| 200 isProperlyInitialized_ = false; | |
| 201 | |
| 202 string platformToolset; | |
| 203 | |
| 204 // We require that there is only a single start-up project. | |
| 205 // If multiple start-up projects are specified then we use the first and | |
| 206 // leave a warning message in the Web Server output pane. | |
| 207 Array startupProjects = dte_.Solution.SolutionBuild.StartupProjects as Arr
ay; | |
| 208 if (startupProjects == null || startupProjects.Length == 0) | |
| 209 { | |
| 210 throw new ArgumentOutOfRangeException("startupProjects.Length"); | |
| 211 } | |
| 212 else if (startupProjects.Length > 1) | |
| 213 { | |
| 214 WebServerWriteLine(Strings.WebServerMultiStartProjectWarning); | |
| 215 } | |
| 216 | |
| 217 // Get the first start-up project object. | |
| 218 List<Project> projList = dte_.Solution.Projects.OfType<Project>().ToList()
; | |
| 219 string startProjectName = startupProjects.GetValue(0) as string; | |
| 220 Project startProject = projList.Find(proj => proj.UniqueName == startProje
ctName); | |
| 221 | |
| 222 // Get the current platform type. If not nacl/pepper then fail. | |
| 223 string activePlatform = startProject.ConfigurationManager.ActiveConfigurat
ion.PlatformName; | |
| 224 if (string.Compare(activePlatform, Strings.PepperPlatformName, true) == 0) | |
| 225 { | |
| 226 projectPlatformType_ = ProjectPlatformType.Pepper; | |
| 227 PluginFoundEvent += new EventHandler<PluginFoundEventArgs>(AttachVSDebug
ger); | |
| 228 } | |
| 229 else if (string.Compare(activePlatform, Strings.NaClPlatformName, true) ==
0) | |
| 230 { | |
| 231 projectPlatformType_ = ProjectPlatformType.NaCl; | |
| 232 PluginFoundEvent += new EventHandler<PluginFoundEventArgs>(AttachNaClGDB
); | |
| 233 } | |
| 234 else | |
| 235 { | |
| 236 projectPlatformType_ = ProjectPlatformType.Other; | |
| 237 return false; | |
| 238 } | |
| 239 | |
| 240 // We only support certain project types (e.g. C/C++ projects). Otherwise
we fail. | |
| 241 if (!Utility.IsVisualCProject(startProject)) | |
| 242 { | |
| 243 return false; | |
| 244 } | |
| 245 | |
| 246 // Extract necessary information from specific project type. | |
| 247 VCConfiguration config = Utility.GetActiveVCConfiguration(startProject); | |
| 248 IVCRulePropertyStorage general = config.Rules.Item("ConfigurationGeneral")
; | |
| 249 VCLinkerTool linker = config.Tools.Item("VCLinkerTool"); | |
| 250 VCProject vcproj = (VCProject)startProject.Object; | |
| 251 | |
| 252 sdkRootDirectory_ = general.GetEvaluatedPropertyValue("VSNaClSDKRoot"); | |
| 253 platformToolset = general.GetEvaluatedPropertyValue("PlatformToolset"); | |
| 254 pluginOutputDirectory_ = config.Evaluate(config.OutputDirectory); | |
| 255 pluginAssembly_ = config.Evaluate(linker.OutputFile); | |
| 256 pluginProjectDirectory_ = vcproj.ProjectDirectory; // Macros not allowed
here. | |
| 257 | |
| 258 if (projectPlatformType_ == ProjectPlatformType.NaCl) | |
| 259 { | |
| 260 irtPath_ = general.GetEvaluatedPropertyValue("NaClIrtPath"); | |
| 261 manifestPath_ = general.GetEvaluatedPropertyValue("NaClManifestPath"); | |
| 262 } | |
| 263 | |
| 264 if (string.IsNullOrEmpty(sdkRootDirectory_)) | |
| 265 { | |
| 266 MessageBox.Show(Strings.SDKPathNotSetError); | |
| 267 return false; | |
| 268 } | |
| 269 | |
| 270 sdkRootDirectory_ = sdkRootDirectory_.TrimEnd("/\\".ToArray<char>()); | |
| 271 | |
| 272 // TODO(tysand): Move this code getting port to where the web server is st
arted. | |
| 273 int webServerPort; | |
| 274 if (!int.TryParse(general.GetEvaluatedPropertyValue("NaClWebServerPort"),
out webServerPort)) | |
| 275 { | |
| 276 webServerPort = DefaultWebServerPort; | |
| 277 } | |
| 278 | |
| 279 webServerExecutable_ = "python.exe"; | |
| 280 webServerArguments_ = string.Format( | |
| 281 "{0}\\examples\\httpd.py --no_dir_check {1}", sdkRootDirectory_, webSe
rverPort); | |
| 282 | |
| 283 gdbPath_ = Path.Combine( | |
| 284 sdkRootDirectory_, "toolchain", platformToolset, @"bin\x86_64-nacl-gdb
.exe"); | |
| 285 | |
| 286 debuggedChromeMainProcess_ = null; | |
| 287 | |
| 288 isProperlyInitialized_ = true; | |
| 289 return true; | |
| 290 } | |
| 291 | |
| 292 /// <summary> | |
| 293 /// This function should be called to start the PluginDebuggerHelper functio
nality. | |
| 294 /// </summary> | |
| 295 public void StartDebugging() | |
| 296 { | |
| 297 if (!isProperlyInitialized_) | |
| 298 { | |
| 299 throw new Exception(Strings.NotInitializedMessage); | |
| 300 } | |
| 301 | |
| 302 StartWebServer(); | |
| 303 pluginFinderTimer_.Interval = InitialPluginCheckFrequency; | |
| 304 pluginFinderTimer_.Start(); | |
| 305 } | |
| 306 | |
| 307 /// <summary> | |
| 308 /// This function should be called to stop the PluginDebuggerHelper function
ality. | |
| 309 /// </summary> | |
| 310 public void StopDebugging() | |
| 311 { | |
| 312 isProperlyInitialized_ = false; | |
| 313 pluginFinderTimer_.Stop(); | |
| 314 pluginFinderForbiddenPids_.Clear(); | |
| 315 | |
| 316 // Remove all event handlers from the plug-in found event. | |
| 317 if (PluginFoundEvent != null) | |
| 318 { | |
| 319 foreach (Delegate del in PluginFoundEvent.GetInvocationList()) | |
| 320 { | |
| 321 PluginFoundEvent -= (EventHandler<PluginFoundEventArgs>)del; | |
| 322 } | |
| 323 } | |
| 324 | |
| 325 Utility.EnsureProcessKill(ref webServer_); | |
| 326 WebServerWriteLine(Strings.WebServerStopMessage); | |
| 327 CleanUpGDBProcess(); | |
| 328 } | |
| 329 | |
| 330 /// <summary> | |
| 331 /// This function cleans up the started GDB process. | |
| 332 /// </summary> | |
| 333 private void CleanUpGDBProcess() | |
| 334 { | |
| 335 Utility.EnsureProcessKill(ref gdbProcess_); | |
| 336 if (!string.IsNullOrEmpty(gdbInitFileName_) && File.Exists(gdbInitFileName
_)) | |
| 337 { | |
| 338 File.Delete(gdbInitFileName_); | |
| 339 gdbInitFileName_ = null; | |
| 340 } | |
| 341 } | |
| 342 | |
| 343 /// <summary> | |
| 344 /// This is called periodically by the Visual Studio UI thread to look for o
ur plug-in process | |
| 345 /// and attach the debugger to it. The call is triggered by the pluginFinde
rTimer_ object. | |
| 346 /// </summary> | |
| 347 /// <param name="unused">The parameter is not used.</param> | |
| 348 /// <param name="unused1">The parameter is not used.</param> | |
| 349 private void FindAndAttachToPlugin(object unused, EventArgs unused1) | |
| 350 { | |
| 351 StringComparison ignoreCase = StringComparison.InvariantCultureIgnoreCase; | |
| 352 | |
| 353 // Set the main chrome process that was started by visual studio. If it's
not chrome | |
| 354 // or not found then we have no business attaching to any plug-ins so retu
rn. | |
| 355 if (debuggedChromeMainProcess_ == null) | |
| 356 { | |
| 357 foreach (Process proc in dte_.Debugger.DebuggedProcesses) | |
| 358 { | |
| 359 if (proc.Name.EndsWith(Strings.ChromeProcessName, ignoreCase)) | |
| 360 { | |
| 361 debuggedChromeMainProcess_ = System.Diagnostics.Process.GetProcessBy
Id(proc.ProcessID); | |
| 362 break; | |
| 363 } | |
| 364 } | |
| 365 | |
| 366 return; | |
| 367 } | |
| 368 | |
| 369 // Get the list of all descendants of the main chrome process. | |
| 370 uint mainChromeProcId = (uint)debuggedChromeMainProcess_.Id; | |
| 371 List<ProcessInfo> chromeDescendants = processSearcher_.GetDescendants(main
ChromeProcId); | |
| 372 | |
| 373 // If we didn't start with debug flags then we should not attach. | |
| 374 string mainChromeFlags = chromeDescendants.Find(p => p.ID == mainChromePro
cId).CommandLine; | |
| 375 if (projectPlatformType_ == ProjectPlatformType.NaCl && | |
| 376 !mainChromeFlags.Contains(Strings.NaClDebugFlag)) | |
| 377 { | |
| 378 return; | |
| 379 } | |
| 380 | |
| 381 // From the list of descendants, find the plug-in by it's command line arg
uments and | |
| 382 // process name as well as not being attached to already. | |
| 383 List<ProcessInfo> plugins; | |
| 384 switch (projectPlatformType_) | |
| 385 { | |
| 386 case ProjectPlatformType.Pepper: | |
| 387 string identifierFlagTarget = | |
| 388 string.Format(Strings.PepperProcessPluginFlagFormat, pluginAssembl
y_); | |
| 389 plugins = chromeDescendants.FindAll(p => | |
| 390 p.Name.Equals(Strings.ChromeProcessName, ignoreCase) && | |
| 391 p.CommandLine.Contains(Strings.ChromeRendererFlag, ignoreCase) && | |
| 392 p.CommandLine.Contains(identifierFlagTarget, ignoreCase) && | |
| 393 !pluginFinderForbiddenPids_.Contains(p.ID)); | |
| 394 break; | |
| 395 case ProjectPlatformType.NaCl: | |
| 396 plugins = chromeDescendants.FindAll(p => | |
| 397 p.Name.Equals(Strings.NaClProcessName, ignoreCase) && | |
| 398 p.CommandLine.Contains(Strings.NaClLoaderFlag, ignoreCase) && | |
| 399 !pluginFinderForbiddenPids_.Contains(p.ID)); | |
| 400 break; | |
| 401 default: | |
| 402 return; | |
| 403 } | |
| 404 | |
| 405 // Attach to all plug-ins that we found. | |
| 406 foreach (ProcessInfo process in plugins) | |
| 407 { | |
| 408 // If we are attaching to a plug-in, add it to the forbidden list to ens
ure we | |
| 409 // don't try to attach again later. | |
| 410 pluginFinderForbiddenPids_.Add(process.ID); | |
| 411 PluginFoundEvent.Invoke(this, new PluginFoundEventArgs(process.ID)); | |
| 412 | |
| 413 // Slow down the frequency of checks for new plugins. | |
| 414 pluginFinderTimer_.Interval = RelaxedPluginCheckFrequency; | |
| 415 } | |
| 416 } | |
| 417 | |
| 418 /// <summary> | |
| 419 /// Attaches the visual studio debugger to a given process ID. | |
| 420 /// </summary> | |
| 421 /// <param name="src">The parameter is not used.</param> | |
| 422 /// <param name="args">Contains the process ID to attach to.</param> | |
| 423 private void AttachVSDebugger(object src, PluginFoundEventArgs args) | |
| 424 { | |
| 425 foreach (EnvDTE.Process proc in dte_.Debugger.LocalProcesses) | |
| 426 { | |
| 427 if (proc.ProcessID == args.ProcessID) | |
| 428 { | |
| 429 proc.Attach(); | |
| 430 break; | |
| 431 } | |
| 432 } | |
| 433 } | |
| 434 | |
| 435 /// <summary> | |
| 436 /// Attaches the NaCl GDB debugger to the NaCl plug-in process. Handles loa
ding symbols | |
| 437 /// and breakpoints from Visual Studio. | |
| 438 /// </summary> | |
| 439 /// <param name="src">The parameter is not used.</param> | |
| 440 /// <param name="args"> | |
| 441 /// Contains the process ID to attach to, unused since debug stub is already
attached. | |
| 442 /// </param> | |
| 443 private void AttachNaClGDB(object src, PluginFoundEventArgs args) | |
| 444 { | |
| 445 // Clean up any pre-existing GDB process (can happen if user reloads page)
. | |
| 446 CleanUpGDBProcess(); | |
| 447 | |
| 448 gdbInitFileName_ = Path.GetTempFileName(); | |
| 449 string pluginAssemblyEscaped = pluginAssembly_.Replace("\\", "\\\\"); | |
| 450 string irtPathEscaped = irtPath_.Replace("\\", "\\\\"); | |
| 451 | |
| 452 // Create the initialization file to read in on GDB start. | |
| 453 StringBuilder contents = new StringBuilder(); | |
| 454 | |
| 455 if (!string.IsNullOrEmpty(manifestPath_)) | |
| 456 { | |
| 457 string manifestEscaped = manifestPath_.Replace("\\", "\\\\"); | |
| 458 contents.AppendFormat("nacl-manifest {0}\n", manifestEscaped); | |
| 459 } | |
| 460 else | |
| 461 { | |
| 462 contents.AppendFormat("file \"{0}\"\n", pluginAssemblyEscaped); | |
| 463 } | |
| 464 | |
| 465 contents.AppendFormat("nacl-irt {0}\n", irtPathEscaped); | |
| 466 contents.AppendFormat("target remote localhost:{0}\n", 4014); | |
| 467 | |
| 468 // Insert breakpoints from Visual Studio project. | |
| 469 foreach (Breakpoint bp in dte_.Debugger.Breakpoints) | |
| 470 { | |
| 471 if (!bp.Enabled) | |
| 472 { | |
| 473 continue; | |
| 474 } | |
| 475 | |
| 476 if (bp.LocationType == dbgBreakpointLocationType.dbgBreakpointLocationTy
peFile) | |
| 477 { | |
| 478 contents.AppendFormat("b {0}:{1}\n", Path.GetFileName(bp.File), bp.Fil
eLine); | |
| 479 } | |
| 480 else if (bp.LocationType == dbgBreakpointLocationType.dbgBreakpointLocat
ionTypeFunction) | |
| 481 { | |
| 482 contents.AppendFormat("b {0}\n", bp.FunctionName); | |
| 483 } | |
| 484 else | |
| 485 { | |
| 486 WebServerWriteLine( | |
| 487 string.Format(Strings.UnsupportedBreakpointTypeFormat, bp.LocationTy
pe.ToString())); | |
| 488 } | |
| 489 } | |
| 490 | |
| 491 contents.AppendLine("continue"); | |
| 492 File.WriteAllText(gdbInitFileName_, contents.ToString()); | |
| 493 | |
| 494 // Start NaCl-GDB. | |
| 495 try | |
| 496 { | |
| 497 gdbProcess_ = new System.Diagnostics.Process(); | |
| 498 gdbProcess_.StartInfo.UseShellExecute = true; | |
| 499 gdbProcess_.StartInfo.FileName = gdbPath_; | |
| 500 gdbProcess_.StartInfo.Arguments = string.Format("-x {0}", gdbInitFileNam
e_); | |
| 501 gdbProcess_.StartInfo.WorkingDirectory = pluginProjectDirectory_; | |
| 502 gdbProcess_.Start(); | |
| 503 } | |
| 504 catch (Exception e) | |
| 505 { | |
| 506 MessageBox.Show( | |
| 507 string.Format("NaCl-GDB Start Failed. {0}. Path: {1}", e.Message, gd
bPath_)); | |
| 508 } | |
| 509 } | |
| 510 | |
| 511 /// <summary> | |
| 512 /// Spins up the web server process to host our plug-in. | |
| 513 /// </summary> | |
| 514 private void StartWebServer() | |
| 515 { | |
| 516 // Add a panel to the output window which is used to capture output | |
| 517 // from the web server hosting the plugin. | |
| 518 if (webServerOutputPane_ == null) | |
| 519 { | |
| 520 webServerOutputPane_ = dte_.ToolWindows.OutputWindow.OutputWindowPanes.A
dd( | |
| 521 Strings.WebServerOutputWindowTitle); | |
| 522 } | |
| 523 | |
| 524 webServerOutputPane_.Clear(); | |
| 525 WebServerWriteLine(Strings.WebServerStartMessage); | |
| 526 | |
| 527 try | |
| 528 { | |
| 529 webServer_ = new System.Diagnostics.Process(); | |
| 530 webServer_.StartInfo.CreateNoWindow = true; | |
| 531 webServer_.StartInfo.UseShellExecute = false; | |
| 532 webServer_.StartInfo.RedirectStandardOutput = true; | |
| 533 webServer_.StartInfo.RedirectStandardError = true; | |
| 534 webServer_.StartInfo.FileName = webServerExecutable_; | |
| 535 webServer_.StartInfo.Arguments = webServerArguments_; | |
| 536 webServer_.StartInfo.WorkingDirectory = pluginProjectDirectory_; | |
| 537 webServer_.OutputDataReceived += WebServerMessageReceive; | |
| 538 webServer_.ErrorDataReceived += WebServerMessageReceive; | |
| 539 webServer_.Start(); | |
| 540 webServer_.BeginOutputReadLine(); | |
| 541 webServer_.BeginErrorReadLine(); | |
| 542 } | |
| 543 catch (Exception e) | |
| 544 { | |
| 545 WebServerWriteLine(Strings.WebServerStartFail); | |
| 546 WebServerWriteLine("Exception: " + e.Message); | |
| 547 } | |
| 548 } | |
| 549 | |
| 550 /// <summary> | |
| 551 /// Receives output from the web server process to display in the Visual Stu
dio UI. | |
| 552 /// </summary> | |
| 553 /// <param name="sender">The parameter is not used.</param> | |
| 554 /// <param name="e">Contains the data to display.</param> | |
| 555 private void WebServerMessageReceive(object sender, System.Diagnostics.DataR
eceivedEventArgs e) | |
| 556 { | |
| 557 WebServerWriteLine(e.Data); | |
| 558 } | |
| 559 | |
| 560 /// <summary> | |
| 561 /// Helper function to write data to the Web Server Output Pane. | |
| 562 /// </summary> | |
| 563 /// <param name="message">Message to write.</param> | |
| 564 private void WebServerWriteLine(string message) | |
| 565 { | |
| 566 if (webServerOutputPane_ != null) | |
| 567 { | |
| 568 webServerOutputPane_.OutputString(message + "\n"); | |
| 569 } | |
| 570 } | |
| 571 | |
| 572 /// <summary> | |
| 573 /// The event arguments when a plug-in is found. | |
| 574 /// </summary> | |
| 575 public class PluginFoundEventArgs : EventArgs | |
| 576 { | |
| 577 /// <summary> | |
| 578 /// Construct the PluginFoundEventArgs. | |
| 579 /// </summary> | |
| 580 /// <param name="pid">Process ID of the found plug-in.</param> | |
| 581 public PluginFoundEventArgs(uint pid) | |
| 582 { | |
| 583 this.ProcessID = pid; | |
| 584 } | |
| 585 | |
| 586 /// <summary> | |
| 587 /// Gets or sets process ID of the found plug-in. | |
| 588 /// </summary> | |
| 589 public uint ProcessID { get; set; } | |
| 590 } | |
| 591 } | |
| 592 } | |
| OLD | NEW |