private string ExtractCab(string file) { m_generatedPath = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString()); while (Directory.Exists(m_generatedPath)) m_generatedPath = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString()); Directory.CreateDirectory(m_generatedPath); CommandOptions options = new CommandOptions(); options.NoThrow = true; options.NoWindow = true; Command cmd = Command.Run(string.Format("expand -F:*dmp {0} {1}", file, m_generatedPath), options); bool error = false; if (cmd.ExitCode != 0) { error = true; } else { file = null; foreach (var item in Directory.GetFiles(m_generatedPath)) { string ext = Path.GetExtension(item).ToLower(); if (ext == ".dll" || ext == ".pdb" || ext == ".exe") continue; file = item; break; } error |= file == null; } if (error) { Dispose(); throw new IOException("Failed to extract a crash dump from " + file); } return file; }
/// <summary> /// Run 'commandLine', sending the output to the console, and wait for the command to complete. /// This simulates what batch filedo when executing their commands. It is a bit more verbose /// by default, however /// </summary> /// <param variable="commandLine">The command lineNumber to run as a subprocess</param> /// <param variable="options">Additional qualifiers that control how the process is run</param> /// <returns>A Command structure that can be queried to determine ExitCode, Output, etc.</returns> public static Command RunToConsole(string commandLine, CommandOptions options) { return(Run(commandLine, options.Clone().AddOutputStream(Console.Out))); }
/// <summary> /// Launch a new command and returns the Command object that can be used to monitor /// the restult. It does not wait for the command to complete, however you /// can call 'Wait' to do that, or use the 'Run' or 'RunToConsole' methods. */ /// </summary> /// <param variable="commandLine">The command lineNumber to run as a subprocess</param> /// <param variable="options">Additional qualifiers that control how the process is run</param> /// <returns>A Command structure that can be queried to determine ExitCode, Output, etc.</returns> public Command(string commandLine, CommandOptions options) { _options = options; _commandLine = commandLine; // See if the command is quoted and match it in that case Match m = Regex.Match(commandLine, "^\\s*\"(.*?)\"\\s*(.*)"); if (!m.Success) { m = Regex.Match(commandLine, @"\s*(\S*)\s*(.*)"); // thing before first space is command } ProcessStartInfo startInfo = new ProcessStartInfo(m.Groups[1].Value, m.Groups[2].Value) { UseShellExecute = false, RedirectStandardError = true, RedirectStandardOutput = true, ErrorDialog = false, CreateNoWindow = true, RedirectStandardInput = options.Input != null }; _process = new Process() { StartInfo = startInfo }; _process.StartInfo = startInfo; _output = new StringBuilder(); if (options.elevate) { options.useShellExecute = true; startInfo.Verb = "runas"; if (options.currentDirectory == null) { options.currentDirectory = Environment.CurrentDirectory; } } _process.OutputDataReceived += new DataReceivedEventHandler(OnProcessOutput); _process.ErrorDataReceived += new DataReceivedEventHandler(OnProcessOutput); if (options.environmentVariables != null) { // copy over the environment variables to the process startInfo options. foreach (string key in options.environmentVariables.Keys) { // look for %VAR% strings in the value and subtitute the appropriate environment variable. string value = options.environmentVariables[key]; if (value != null) { int startAt = 0; for (; ;) { m = new Regex(@"%(\w+)%").Match(value, startAt); if (!m.Success) { break; } string varName = m.Groups[1].Value; string varValue; if (startInfo.EnvironmentVariables.ContainsKey(varName)) { varValue = startInfo.EnvironmentVariables[varName]; } else { varValue = Environment.GetEnvironmentVariable(varName); if (varValue == null) { varValue = ""; } } // replace this instance of the variable with its definition. int varStart = m.Groups[1].Index - 1; // -1 becasue % chars are not in the group int varEnd = varStart + m.Groups[1].Length + 2; // +2 because % chars are not in the group value = value.Substring(0, varStart) + varValue + value.Substring(varEnd, value.Length - varEnd); startAt = varStart + varValue.Length; } } startInfo.EnvironmentVariables[key] = value; } } startInfo.WorkingDirectory = options.currentDirectory; _outputStream = options.outputStream; if (options.outputFile != null) { _outputStream = File.CreateText(options.outputFile); } #if false if (options.showCommand && outputStream != null) { // TODO why only for output streams? outputStream.WriteLine("RUN CMD: " + commandLine); } #endif #if false // Make sure we kill this task when Ctrl C or appdomain unloads if (!options.noWait) { AddCommandToCleanupList(this); } #endif try { _process.Start(); } catch (Exception e) { string msg = "Failure starting Process\r\n" + " Exception: " + e.Message + "\r\n" + " Cmd: " + commandLine + "\r\n"; if (Regex.IsMatch(startInfo.FileName, @"^(copy|dir|del|color|set|cd|cdir|md|mkdir|prompt|pushd|popd|start|assoc|ftype)", RegexOptions.IgnoreCase)) { msg += " Cmd " + startInfo.FileName + " implemented by Cmd.exe, fix by prefixing with 'cmd /c'."; } throw new Exception(msg, e); } if (!startInfo.UseShellExecute) { // startInfo asyncronously collecting output _process.BeginOutputReadLine(); _process.BeginErrorReadLine(); } // Send any input to the command if (options.input != null) { _process.StandardInput.Write(options.input); _process.StandardInput.Dispose(); } }
/// <summary> /// Launch a new command and returns the Command object that can be used to monitor /// the restult. It does not wait for the command to complete, however you /// can call 'Wait' to do that, or use the 'Run' or 'RunToConsole' methods. */ /// </summary> /// <param variable="commandLine">The command lineNumber to run as a subprocess</param> /// <param variable="options">Additional qualifiers that control how the process is run</param> /// <returns>A Command structure that can be queried to determine ExitCode, Output, etc.</returns> public Command(string commandLine, CommandOptions options) { Options = options; _commandLine = commandLine; // See if the command is quoted and match it in that case Match m = Regex.Match(commandLine, "^\\s*\"(.*?)\"\\s*(.*)"); if (!m.Success) { m = Regex.Match(commandLine, @"\s*(\S*)\s*(.*)"); // thing before first space is command } ProcessStartInfo startInfo = new ProcessStartInfo(m.Groups[1].Value, m.Groups[2].Value) { UseShellExecute = false, RedirectStandardError = true, RedirectStandardOutput = true, ErrorDialog = false, CreateNoWindow = true, RedirectStandardInput = options.Input != null }; Process = new Process { StartInfo = startInfo }; Process.StartInfo = startInfo; _output = new StringBuilder(); if (options.elevate) { options.useShellExecute = true; startInfo.Verb = "runas"; options.currentDirectory ??= Environment.CurrentDirectory; } Process.OutputDataReceived += OnProcessOutput; Process.ErrorDataReceived += OnProcessOutput; if (options.environmentVariables != null) { // copy over the environment variables to the process startInfo options. foreach (KeyValuePair <string, string> pair in options.environmentVariables) { // look for %VAR% strings in the value and subtitute the appropriate environment variable. string value = pair.Value; if (value != null) { value = Environment.ExpandEnvironmentVariables(value); } startInfo.EnvironmentVariables[pair.Key] = value; } } startInfo.WorkingDirectory = options.currentDirectory; _outputStream = options.outputStream; if (options.outputFile != null) { _outputStream = File.CreateText(options.outputFile); } try { Process.Start(); } catch (Exception e) { string msg = "Failure starting Process\r\n" + " Exception: " + e.Message + "\r\n" + " Cmd: " + commandLine + "\r\n"; if (Regex.IsMatch(startInfo.FileName, @"^(copy|dir|del|color|set|cd|cdir|md|mkdir|prompt|pushd|popd|start|assoc|ftype)", RegexOptions.IgnoreCase)) { msg += " Cmd " + startInfo.FileName + " implemented by Cmd.exe, fix by prefixing with 'cmd /c'."; } throw new Exception(msg, e); } if (!startInfo.UseShellExecute) { // startInfo asyncronously collecting output Process.BeginOutputReadLine(); Process.BeginErrorReadLine(); } // Send any input to the command if (options.input != null) { Process.StandardInput.Write(options.input); Process.StandardInput.Dispose(); } }
/// <summary> /// Launch a new command and returns the Command object that can be used to monitor /// the restult. It does not wait for the command to complete, however you /// can call 'Wait' to do that, or use the 'Run' or 'RunToConsole' methods. */ /// </summary> /// <param variable="commandLine">The command lineNumber to run as a subprocess</param> /// <param variable="options">Additional qualifiers that control how the process is run</param> /// <returns>A Command structure that can be queried to determine ExitCode, Output, etc.</returns> public Command(string commandLine, CommandOptions options) { _options = options; _commandLine = commandLine; // See if the command is quoted and match it in that case Match m = Regex.Match(commandLine, "^\\s*\"(.*?)\"\\s*(.*)"); if (!m.Success) m = Regex.Match(commandLine, @"\s*(\S*)\s*(.*)"); // thing before first space is command ProcessStartInfo startInfo = new ProcessStartInfo(m.Groups[1].Value, m.Groups[2].Value); _process = new Process(); _process.StartInfo = startInfo; _output = new StringBuilder(); if (options.elevate) { options.useShellExecute = true; startInfo.Verb = "runas"; if (options.currentDirectory == null) options.currentDirectory = Environment.CurrentDirectory; } startInfo.CreateNoWindow = options.noWindow; if (options.useShellExecute) { startInfo.UseShellExecute = true; if (options.noWindow) startInfo.WindowStyle = ProcessWindowStyle.Hidden; } else { if (options.input != null) startInfo.RedirectStandardInput = true; startInfo.UseShellExecute = false; startInfo.RedirectStandardError = true; startInfo.RedirectStandardOutput = true; startInfo.ErrorDialog = false; startInfo.WindowStyle = ProcessWindowStyle.Hidden; startInfo.CreateNoWindow = true; _process.OutputDataReceived += new DataReceivedEventHandler(OnProcessOutput); _process.ErrorDataReceived += new DataReceivedEventHandler(OnProcessOutput); } if (options.environmentVariables != null) { // copy over the environment variables to the process startInfo options. foreach (string key in options.environmentVariables.Keys) { // look for %VAR% strings in the value and subtitute the appropriate environment variable. string value = options.environmentVariables[key]; if (value != null) { int startAt = 0; for (;;) { m = new Regex(@"%(\w+)%").Match(value, startAt); if (!m.Success) break; string varName = m.Groups[1].Value; string varValue; if (startInfo.EnvironmentVariables.ContainsKey(varName)) varValue = startInfo.EnvironmentVariables[varName]; else { varValue = Environment.GetEnvironmentVariable(varName); if (varValue == null) varValue = ""; } // replace this instance of the variable with its definition. int varStart = m.Groups[1].Index - 1; // -1 becasue % chars are not in the group int varEnd = varStart + m.Groups[1].Length + 2; // +2 because % chars are not in the group value = value.Substring(0, varStart) + varValue + value.Substring(varEnd, value.Length - varEnd); startAt = varStart + varValue.Length; } } startInfo.EnvironmentVariables[key] = value; } } startInfo.WorkingDirectory = options.currentDirectory; _outputStream = options.outputStream; if (options.outputFile != null) { _outputStream = File.CreateText(options.outputFile); } #if false if (options.showCommand && outputStream != null) { // TODO why only for output streams? outputStream.WriteLine("RUN CMD: " + commandLine); } #endif #if false // Make sure we kill this task when Ctrl C or appdomain unloads if (!options.noWait) AddCommandToCleanupList(this); #endif try { _process.Start(); } catch (Exception e) { string msg = "Failure starting Process\r\n" + " Exception: " + e.Message + "\r\n" + " Cmd: " + commandLine + "\r\n"; if (Regex.IsMatch(startInfo.FileName, @"^(copy|dir|del|color|set|cd|cdir|md|mkdir|prompt|pushd|popd|start|assoc|ftype)", RegexOptions.IgnoreCase)) msg += " Cmd " + startInfo.FileName + " implemented by Cmd.exe, fix by prefixing with 'cmd /c'."; throw new ApplicationException(msg, e); } if (!startInfo.UseShellExecute) { // startInfo asyncronously collecting output _process.BeginOutputReadLine(); _process.BeginErrorReadLine(); } // Send any input to the command if (options.input != null) { _process.StandardInput.Write(options.input); _process.StandardInput.Close(); } }
/// <summary> /// Run 'commandLine' as a subprocess and waits for the command to complete. /// Output is captured and placed in the 'Output' property of the returned Command /// structure. /// </summary> /// <param variable="commandLine">The command lineNumber to run as a subprocess</param> /// <param variable="options">Additional qualifiers that control how the process is run</param> /// <returns>A Command structure that can be queried to determine ExitCode, Output, etc.</returns> public static Command Run(string commandLine, CommandOptions options) { Command run = new Command(commandLine, options); run.Wait(); return run; }
/// <summary> /// Run 'commandLine', sending the output to the console, and wait for the command to complete. /// This simulates what batch filedo when executing their commands. It is a bit more verbose /// by default, however /// </summary> /// <param variable="commandLine">The command lineNumber to run as a subprocess</param> /// <param variable="options">Additional qualifiers that control how the process is run</param> /// <returns>A Command structure that can be queried to determine ExitCode, Output, etc.</returns> public static Command RunToConsole(string commandLine, CommandOptions options) { return Run(commandLine, options.Clone().AddOutputStream(Console.Out)); }
/// <summary> /// Given a full filename path to an NGEN image, insure that there is an NGEN image for it /// in the symbol cache. If one already exists, this method simply returns that. If not /// it is generated and placed in the symbol cache. When generating the PDB this routine /// attempt to resolve line numbers, which DOES require looking up the PDB for the IL image. /// Thus routine may do network accesses (to download IL PDBs). /// /// Note that FindSymbolFilePathForModule calls this, so normally you don't need to call /// this method directly. /// </summary> public string GenerateNGenPdbForModule(string ngenImageFullPath) { SymbolReader symReader = this; var log = symReader.m_log; if (!File.Exists(ngenImageFullPath)) { log.WriteLine("Warning, NGEN image does not exist: {0}", ngenImageFullPath); return null; } // When V4.5 shipped, NGEN CreatePdb did not support looking up the IL pdb using symbol servers. // We work around by explicitly fetching the IL PDB and pointing NGEN CreatePdb at that. string ilPdbName = null; Guid ilPdbGuid = Guid.Empty; int ilPdbAge = 0; string pdbName; Guid pdbGuid; int pdbAge; using (var peFile = new PEFile(ngenImageFullPath)) { if (!peFile.GetPdbSignature(out pdbName, out pdbGuid, out pdbAge, true)) { log.WriteLine("Could not get PDB signature for {0}", ngenImageFullPath); return null; } // Also get the IL pdb information peFile.GetPdbSignature(out ilPdbName, out ilPdbGuid, out ilPdbAge, false); } // Fast path, the file already exists. pdbName = Path.GetFileName(pdbName); var relPath = pdbName + "\\" + pdbGuid.ToString("N") + pdbAge.ToString() + "\\" + pdbName; var pdbPath = Path.Combine(symReader.SymbolCacheDirectory, relPath); if (File.Exists(pdbPath)) return pdbPath; var clrDir = GetClrDirectoryForNGenImage(ngenImageFullPath, log); if (clrDir == null) return null; // See if this is a V4.5 CLR, if so we can do line numbers too.l var lineNumberArg = ""; var ngenexe = Path.Combine(clrDir, "ngen.exe"); log.WriteLine("Checking for V4.5 for NGEN image {0}", ngenexe); if (!File.Exists(ngenexe)) return null; var isV4_5Runtime = false; Match m; using (var peFile = new PEFile(ngenexe)) { var fileVersionInfo = peFile.GetFileVersionInfo(); if (fileVersionInfo != null) { var clrFileVersion = fileVersionInfo.FileVersion; m = Regex.Match(clrFileVersion, @"^[\d.]+\.(\d+) "); // Fetch the build number (last number) if (m.Success) { // Is this a V4.5 runtime? var buildNumber = int.Parse(m.Groups[1].Value); log.WriteLine("Got NGEN.exe Build number: {0}", buildNumber); if (buildNumber > 16000) { if (ilPdbName != null) { var ilPdbPath = symReader.FindSymbolFilePath(ilPdbName, ilPdbGuid, ilPdbAge); if (ilPdbPath != null) lineNumberArg = "/lines " + Command.Quote(Path.GetDirectoryName(ilPdbPath)); else log.WriteLine("Could not find IL PDB {0} Guid {1} Age {2}.", ilPdbName, ilPdbGuid, ilPdbAge); } else log.WriteLine("NGEN image did not have IL PDB information, giving up on line number info."); isV4_5Runtime = true; } } } } var options = new CommandOptions(); options.AddEnvironmentVariable("COMPLUS_NGenEnableCreatePdb", "1"); // NGenLocalWorker is needed for V4.0 runtims but interferes on V4.5 runtimes. if (!isV4_5Runtime) options.AddEnvironmentVariable("COMPLUS_NGenLocalWorker", "1"); options.AddEnvironmentVariable("_NT_SYMBOL_PATH", symReader.SymbolPath.ToString()); var newPath = "%PATH%;" + clrDir; options.AddEnvironmentVariable("PATH", newPath); options.AddOutputStream(log); options.AddNoThrow(); // For Metro Auto-NGEN images we need to use a location where the app can write the PDB file var outputDirectory = symReader.SymbolCacheDirectory; var outputPdbPath = pdbPath; // Find the tempDir where we can write. string tempDir = null; m = Regex.Match(ngenImageFullPath, @"(.*)\\Microsoft\\CLR_v(\d+)\.\d+(_(\d\d))?\\NativeImages", RegexOptions.IgnoreCase); if (m.Success) { tempDir = Path.Combine(m.Groups[1].Value, @"Temp\NGenPdb"); DirectoryUtilities.Clean(tempDir); Directory.CreateDirectory(tempDir); outputDirectory = tempDir; outputPdbPath = Path.Combine(tempDir, relPath); log.WriteLine("Updating NGEN createPdb output file to {0}", outputPdbPath); // TODO FIX NOW REMOVE (for debugging) } try { for (; ; ) // Loop for retrying without /lines { // TODO FIX NOW: there is a and ugly problem with persistance of suboptimial PDB files // This is made pretty bad because the not finding the IL pdbs is enough to make it fail. // TODO we need to figure out a convention show we know that we have fallen back to no-lines // and we should regenerate it if we ultimately get the PDB information var cmdLine = string.Format(@"{0}\ngen.exe createpdb {1} {2} {3}", clrDir, Command.Quote(ngenImageFullPath), Command.Quote(outputDirectory), lineNumberArg); // TODO FIX NOW REMOVE after V4.5 is out a while log.WriteLine("set COMPLUS_NGenEnableCreatePdb=1"); if (!isV4_5Runtime) log.WriteLine("set COMPLUS_NGenLocalWorker=1"); log.WriteLine("set PATH=" + newPath); log.WriteLine("set _NT_SYMBOL_PATH={0}", symReader.SymbolPath); log.WriteLine("*** NGEN CREATEPDB cmdline: {0}\r\n", cmdLine); options.AddOutputStream(log); var cmd = Command.Run(cmdLine, options); log.WriteLine("*** NGEN CREATEPDB returns: {0}", cmd.ExitCode); if (cmd.ExitCode != 0) { // ngen might make a bad PDB, so if it returns failure delete it. if (File.Exists(outputPdbPath)) File.Delete(outputPdbPath); // We may have failed because we could not get the PDB. if (lineNumberArg.Length != 0) { log.WriteLine("Ngen failed to generate pdb for {0}, trying again without /lines", ngenImageFullPath); lineNumberArg = ""; continue; } } if (cmd.ExitCode != 0 || !File.Exists(outputPdbPath)) { log.WriteLine("ngen failed to generate pdb for {0} at expected location {1}", ngenImageFullPath, outputPdbPath); return null; } // Copy the file to where we want the PDB to live. if (outputPdbPath != pdbPath) { Directory.CreateDirectory(Path.GetDirectoryName(pdbPath)); // Make sure the destination directory exists. File.Copy(outputPdbPath, pdbPath); } return pdbPath; } } finally { // Insure we have cleaned up any temporary files. if (tempDir != null) DirectoryUtilities.Clean(tempDir); } }