-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathForm1.cs
318 lines (270 loc) · 11.3 KB
/
Form1.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Configuration;
using System.Management;
using System.Timers;
using System.Windows.Forms;
using Timer = System.Timers.Timer;
namespace Hyper_V_Manager
{
/// <summary>
/// Possible VM States
/// </summary>
public enum VmState
{
Unknown = 0,
Other = 1, // Corresponds to CIM_EnabledLogicalElement.EnabledState = Other.
Running = 2, // Enabled
Stopped = 3, // Disabled
ShutDown = 4, // Valid in version 1 (V1) of Hyper-V only. The virtual machine is shutting down via the shutdown service. Corresponds to CIM_EnabledLogicalElement.EnabledState = ShuttingDown.
Saved = 6, // Corresponds to CIM_EnabledLogicalElement.EnabledState = Enabled but offline.
Paused = 9, // Corresponds to CIM_EnabledLogicalElement.EnabledState = Quiesce, Enabled but paused.
Starting = 32770,
Saving = 32773,
Stopping = 32774,
Pausing = 32776,
Resuming = 32777
}
/// <inheritdoc />
/// <summary>
/// Main Form
/// </summary>
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private readonly Timer _timer = new Timer();
private readonly Dictionary<string, string> _changingVMs = new Dictionary<string, string>();
/// <summary>
/// Form load event
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void Form1_Load(object sender, EventArgs e)
{
_timer.Elapsed += TimerElapsed;
_timer.Interval = 4500;
BuildContextMenu();
}
/// <summary>
/// Time elapsed event
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void TimerElapsed(object sender, ElapsedEventArgs e)
{
if(!contextMenuStrip1.Visible)
UpdateBalloontip();
}
/// <summary>
/// Update Balloon Tooltip on VM State Change
/// </summary>
private void UpdateBalloontip()
{
var localVMs = GetVMs();
foreach (var vm in localVMs)
{
if (!_changingVMs.ContainsKey(vm["ElementName"].ToString())) continue;
var initvmBalloonState = _changingVMs[vm["ElementName"].ToString()];
var vmState = (VmState) Convert.ToInt32(vm["EnabledState"]);
var currentBalloonState = vmState.ToString();
if (initvmBalloonState != currentBalloonState)
{
notifyIcon1.ShowBalloonTip(4000, "VM State Changed", vm["ElementName"] + " " + currentBalloonState, ToolTipIcon.Info);
_changingVMs[vm["ElementName"].ToString()] = currentBalloonState;
}
else if (vmState == VmState.Running || vmState == VmState.Stopped || vmState == VmState.Paused || vmState == VmState.Saved)
_changingVMs.Remove(vm["ElementName"].ToString());
else if (_changingVMs.Count <= 0)
_timer.Enabled = false;
}
}
/// <summary>
/// Get an enumerable list of Hyper-V VMs using WMI
/// </summary>
/// <returns></returns>
private static IEnumerable<ManagementObject> GetVMs()
{
var vms = new List<ManagementObject>();
var path = ConfigurationManager.AppSettings["root"];
var manScope = new ManagementScope(path);
var queryObj = new ObjectQuery("Select * From Msvm_ComputerSystem");
var vmSearcher = new ManagementObjectSearcher(manScope, queryObj);
var vmCollection = vmSearcher.Get();
foreach (var o in vmCollection)
{
var vm = (ManagementObject) o;
if (vm.Properties["Caption"].Value.ToString() == "Virtual Machine")
{
vms.Add(vm);
}
}
return vms;
}
#region ContextMenuEvents
private void VmItem_Click(object sender, EventArgs e)
{
System.Diagnostics.Process.Start($@"{Environment.GetEnvironmentVariable("SYSTEMROOT")}\System32\vmconnect.exe",
$"localhost \"{((ToolStripMenuItem) sender).Name}\"");
}
private void PauseItem_Click(object sender, EventArgs e)
{
ChangeVmState("Pause", ((ToolStripMenuItem)sender).OwnerItem.Name);
}
private void SaveStateItem_Click(object sender, EventArgs e)
{
ChangeVmState("Save State", ((ToolStripMenuItem)sender).OwnerItem.Name);
}
private void ShutDownItem_Click(object sender, EventArgs e)
{
ChangeVmState("Shut Down", ((ToolStripMenuItem)sender).OwnerItem.Name);
}
private void StopItem_Click(object sender, EventArgs e)
{
ChangeVmState("Stop", ((ToolStripMenuItem)sender).OwnerItem.Name);
}
private void StartItem_Click(object sender, EventArgs e)
{
ChangeVmState("Start", ((ToolStripMenuItem)sender).OwnerItem.Name);
}
private void ExitItem_Click(object sender, EventArgs e)
{
Close();
}
#endregion
/// <summary>
/// Set the VM State
/// </summary>
/// <param name="requestedState">Requested state</param>
/// <param name="vmName">Name of the VM</param>
private void ChangeVmState(string requestedState, string vmName)
{
var localVMs = GetVMs();
_timer.Enabled = true;
// Loop to find a matching VM
foreach (var vm in localVMs)
{
if (vm["ElementName"].ToString() != vmName) continue;
// Set the state to unknown as we request the change
_changingVMs[vm["ElementName"].ToString()] = VmState.Unknown.ToString();
var inParams = vm.GetMethodParameters("RequestStateChange");
switch (requestedState)
{
case "Start":
inParams["RequestedState"] = (ushort) VmState.Running;
break;
case "Stop":
inParams["RequestedState"] = (ushort) VmState.Stopped;
break;
case "Shut Down":
inParams["RequestedState"] = (ushort) VmState.ShutDown;
break;
case "Pause":
inParams["RequestedState"] = (ushort) VmState.Paused;
break;
case "Save State":
inParams["RequestedState"] = (ushort) VmState.Saved;
break;
default:
throw new Exception("Unexpected VM State");
}
// Todo - handle response from request to change
// https://docs.microsoft.com/en-us/windows/desktop/hyperv_v2/requeststatechange-msvm-computersystem
vm.InvokeMethod("RequestStateChange", inParams, null);
}
}
/// <summary>
/// Build context menu
/// </summary>
private void BuildContextMenu()
{
// Get all VMs
var localVMs = GetVMs();
// Clear the context menu
contextMenuStrip1.Items.Clear();
// Add context menu items with current state options
foreach (var vm in localVMs)
{
var startItem = new ToolStripMenuItem("Start");
startItem.Click += StartItem_Click;
startItem.DisplayStyle = ToolStripItemDisplayStyle.Text;
var stopItem = new ToolStripMenuItem("Stop");
stopItem.Click += StopItem_Click;
stopItem.DisplayStyle = ToolStripItemDisplayStyle.Text;
var shutDownItem = new ToolStripMenuItem("Shut Down");
shutDownItem.Click += ShutDownItem_Click;
shutDownItem.DisplayStyle = ToolStripItemDisplayStyle.Text;
var saveStateItem = new ToolStripMenuItem("Save State");
saveStateItem.Click += SaveStateItem_Click;
saveStateItem.DisplayStyle = ToolStripItemDisplayStyle.Text;
var pauseItem = new ToolStripMenuItem("Pause");
pauseItem.Click += PauseItem_Click;
pauseItem.DisplayStyle = ToolStripItemDisplayStyle.Text;
var vmState = (VmState)Convert.ToInt32(vm["EnabledState"]);
// ReSharper disable once SwitchStatementMissingSomeCases
switch (vmState)
{
case VmState.Running:
startItem.Enabled = false;
break;
case VmState.Saved:
case VmState.ShutDown:
case VmState.Stopped:
stopItem.Enabled = false;
saveStateItem.Enabled = false;
pauseItem.Enabled = false;
break;
case VmState.Paused:
pauseItem.Enabled = false;
break;
}
// Create a lable for each VM, show its state if not stopped
var vmStatusText = vm["ElementName"].ToString();
if (vmState != VmState.Stopped) vmStatusText += " [" + vmState + "]";
// Create sub-menu
var vmItem = new ToolStripMenuItem(vmStatusText) {Name = vm["ElementName"].ToString()};
// Add a VM click handler to open remote
vmItem.Click += VmItem_Click;
// Add sub-menu items
if (vmState == VmState.Running || vmState == VmState.Stopped || vmState == VmState.Saved || vmState == VmState.Paused)
{
vmItem.DropDownItems.Add(startItem);
vmItem.DropDownItems.Add(stopItem);
vmItem.DropDownItems.Add(shutDownItem);
vmItem.DropDownItems.Add(saveStateItem);
vmItem.DropDownItems.Add(pauseItem);
}
contextMenuStrip1.Items.Add(vmItem);
}
// Add Exit option
var exitItem = new ToolStripMenuItem("Exit");
exitItem.Click += ExitItem_Click;
contextMenuStrip1.Items.Add(exitItem);
// Redraw the menu
contextMenuStrip1.Refresh();
}
/// <summary>
/// Context menu open event
/// Build a new context menu op open
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void ContextMenuStrip1_Opening(object sender, CancelEventArgs e)
{
BuildContextMenu();
}
/// <summary>
/// Form activated event
/// Hide the form, to only show the system tray
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void Form1_Activated(object sender, EventArgs e)
{
Hide();
}
}
}