PageRenderTime 41ms CodeModel.GetById 18ms RepoModel.GetById 1ms app.codeStats 0ms

/Snoop/Shell/EmbeddedShellView.xaml.cs

http://snoopwpf.codeplex.com
C# | 247 lines | 193 code | 37 blank | 17 comment | 13 complexity | 5eb6987295a17a00a0e7f2ba7825f52c MD5 | raw file
Possible License(s): CC-BY-SA-3.0
  1. // (c) Copyright Bailey Ling.
  2. // This source is subject to the Microsoft Public License (Ms-PL).
  3. // Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details.
  4. // All other rights reserved.
  5. using System;
  6. using System.IO;
  7. using System.Management.Automation;
  8. using System.Management.Automation.Runspaces;
  9. using System.Reflection;
  10. using System.Threading;
  11. using System.Windows.Controls;
  12. using System.Windows.Input;
  13. namespace Snoop.Shell
  14. {
  15. /// <summary>
  16. /// Interaction logic for EmbeddedShellView.xaml
  17. /// </summary>
  18. public partial class EmbeddedShellView : UserControl
  19. {
  20. public event Action<VisualTreeItem> ProviderLocationChanged = delegate { };
  21. private readonly Runspace runspace;
  22. private readonly SnoopPSHost host;
  23. private int historyIndex;
  24. public EmbeddedShellView()
  25. {
  26. InitializeComponent();
  27. this.commandTextBox.PreviewKeyDown += OnCommandTextBoxPreviewKeyDown;
  28. // ignore execution-policy
  29. var iis = InitialSessionState.CreateDefault();
  30. iis.AuthorizationManager = new AuthorizationManager(Guid.NewGuid().ToString());
  31. iis.Providers.Add(new SessionStateProviderEntry(ShellConstants.DriveName, typeof(VisualTreeProvider), string.Empty));
  32. this.host = new SnoopPSHost(x => this.outputTextBox.AppendText(x));
  33. this.runspace = RunspaceFactory.CreateRunspace(this.host, iis);
  34. this.runspace.ThreadOptions = PSThreadOptions.UseCurrentThread;
  35. this.runspace.ApartmentState = ApartmentState.STA;
  36. this.runspace.Open();
  37. // default required if you intend to inject scriptblocks into the host application
  38. Runspace.DefaultRunspace = this.runspace;
  39. }
  40. /// <summary>
  41. /// Initiates the startup routine and configures the runspace for use.
  42. /// </summary>
  43. public void Start(SnoopUI ui)
  44. {
  45. Invoke(string.Format("new-psdrive {0} {0} -root /", ShellConstants.DriveName));
  46. // synchronize selected and root tree elements
  47. ui.PropertyChanged += (sender, e) =>
  48. {
  49. switch (e.PropertyName)
  50. {
  51. case "CurrentSelection":
  52. SetVariable(ShellConstants.Selected, ui.CurrentSelection);
  53. break;
  54. case "Root":
  55. SetVariable(ShellConstants.Root, ui.Root);
  56. break;
  57. }
  58. };
  59. // allow scripting of the host controls
  60. SetVariable("snoopui", ui);
  61. SetVariable("ui", this);
  62. // marshall back to the UI thread when the provider notifiers of a location change
  63. var action = new Action<VisualTreeItem>(item => this.Dispatcher.BeginInvoke(new Action(() => this.ProviderLocationChanged(item))));
  64. this.SetVariable(ShellConstants.LocationChangedActionKey, action);
  65. string folder = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), "Scripts");
  66. Invoke(string.Format("import-module \"{0}\"", Path.Combine(folder, ShellConstants.SnoopModule)));
  67. this.outputTextBox.Clear();
  68. Invoke("write-host 'Welcome to the Snoop PowerShell console!'");
  69. Invoke("write-host '----------------------------------------'");
  70. Invoke(string.Format("write-host 'To get started, try using the ${0} and ${1} variables.'", ShellConstants.Root, ShellConstants.Selected));
  71. FindAndLoadProfile(folder);
  72. }
  73. public void SetVariable(string name, object instance)
  74. {
  75. // add to the host so the provider has access to exposed variables
  76. this.host[name] = instance;
  77. // expose to the current runspace
  78. Invoke(string.Format("${0} = $host.PrivateData['{0}']", name));
  79. }
  80. public void NotifySelected(VisualTreeItem item)
  81. {
  82. if (this.autoExpandCheckBox.IsChecked == true)
  83. {
  84. item.IsExpanded = true;
  85. }
  86. this.Invoke(string.Format("cd {0}:\\{1}", ShellConstants.DriveName, item.NodePath()));
  87. }
  88. private void FindAndLoadProfile(string scriptFolder)
  89. {
  90. if (LoadProfile(Path.Combine(Environment.GetEnvironmentVariable("USERPROFILE"), ShellConstants.SnoopProfile)))
  91. {
  92. return;
  93. }
  94. if (LoadProfile(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Personal) + "\\WindowsPowerShell", ShellConstants.SnoopProfile)))
  95. {
  96. return;
  97. }
  98. LoadProfile(Path.Combine(scriptFolder, ShellConstants.SnoopProfile));
  99. }
  100. private bool LoadProfile(string scriptPath)
  101. {
  102. if (File.Exists(scriptPath))
  103. {
  104. Invoke("write-host ''");
  105. Invoke(string.Format("${0} = '{1}'; . ${0}", ShellConstants.Profile, scriptPath));
  106. Invoke(string.Format("write-host \"Loaded `$profile: ${0}\"", ShellConstants.Profile));
  107. return true;
  108. }
  109. return false;
  110. }
  111. private void OnCommandTextBoxPreviewKeyDown(object sender, KeyEventArgs e)
  112. {
  113. switch (e.Key)
  114. {
  115. case Key.Up:
  116. SetCommandTextToHistory(++this.historyIndex);
  117. break;
  118. case Key.Down:
  119. if (this.historyIndex - 1 <= 0)
  120. {
  121. this.commandTextBox.Clear();
  122. }
  123. else
  124. {
  125. SetCommandTextToHistory(--this.historyIndex);
  126. }
  127. break;
  128. case Key.Return:
  129. this.outputTextBox.AppendText(Environment.NewLine);
  130. this.outputTextBox.AppendText(this.commandTextBox.Text);
  131. this.outputTextBox.AppendText(Environment.NewLine);
  132. Invoke(this.commandTextBox.Text, true);
  133. this.commandTextBox.Clear();
  134. break;
  135. }
  136. }
  137. private void Invoke(string script, bool addToHistory = false)
  138. {
  139. this.historyIndex = 0;
  140. try
  141. {
  142. using (var pipe = this.runspace.CreatePipeline(script, addToHistory))
  143. {
  144. var cmd = new Command("Out-String");
  145. cmd.Parameters.Add("Width", Math.Max(2, (int)(this.ActualWidth * 0.7)));
  146. pipe.Commands.Add(cmd);
  147. foreach (var item in pipe.Invoke())
  148. {
  149. this.outputTextBox.AppendText(item.ToString());
  150. }
  151. foreach (PSObject item in pipe.Error.ReadToEnd())
  152. {
  153. var error = (ErrorRecord)item.BaseObject;
  154. this.OutputErrorRecord(error);
  155. }
  156. }
  157. }
  158. catch (RuntimeException ex)
  159. {
  160. this.OutputErrorRecord(ex.ErrorRecord);
  161. }
  162. catch (Exception ex)
  163. {
  164. this.outputTextBox.AppendText(string.Format("Oops! Uncaught exception invoking on the PowerShell runspace: {0}", ex.Message));
  165. }
  166. this.outputTextBox.ScrollToEnd();
  167. }
  168. private void OutputErrorRecord(ErrorRecord error)
  169. {
  170. this.outputTextBox.AppendText(string.Format("{0}{1}", error, error.InvocationInfo != null ? error.InvocationInfo.PositionMessage : string.Empty));
  171. this.outputTextBox.AppendText(string.Format("{1} + CategoryInfo : {0}", error.CategoryInfo, Environment.NewLine));
  172. this.outputTextBox.AppendText(string.Format("{1} + FullyQualifiedErrorId : {0}", error.FullyQualifiedErrorId, Environment.NewLine));
  173. }
  174. private void SetCommandTextToHistory(int history)
  175. {
  176. var cmd = GetHistoryCommand(history);
  177. if (cmd != null)
  178. {
  179. this.commandTextBox.Text = cmd;
  180. this.commandTextBox.SelectionStart = cmd.Length;
  181. }
  182. }
  183. private string GetHistoryCommand(int history)
  184. {
  185. using (var pipe = this.runspace.CreatePipeline("get-history -count " + history, false))
  186. {
  187. var results = pipe.Invoke();
  188. if (results.Count > 0)
  189. {
  190. var item = results[0];
  191. return (string)item.Properties["CommandLine"].Value;
  192. }
  193. return null;
  194. }
  195. }
  196. protected override void OnPreviewKeyDown(KeyEventArgs e)
  197. {
  198. base.OnPreviewKeyDown(e);
  199. switch (e.Key)
  200. {
  201. case Key.F5:
  202. Invoke(string.Format("if (${0}) {{ . ${0} }}", ShellConstants.Profile));
  203. break;
  204. case Key.F12:
  205. this.outputTextBox.Clear();
  206. break;
  207. }
  208. }
  209. }
  210. }