Cosmos/source/Cosmos.Debug.VSDebugEngine/Host/VMware.cs
Matthijs ter Woord 3fb0e6622d Progress on fat.
2015-10-04 11:53:03 +02:00

217 lines
8.5 KiB
C#

using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Text;
using Microsoft.Win32;
using Vestris.VMWareLib;
using Cosmos.Build.Common;
using Cosmos.Debug.Common;
namespace Cosmos.Debug.VSDebugEngine.Host {
public class VMware : Base {
protected VMwareEdition mEdition;
protected string mDir;
protected string mVmxPath;
protected Process mProcess;
protected string mWorkstationPath;
protected string mPlayerPath;
protected string mHarddisk;
public VMware(NameValueCollection aParams, bool aUseGDB,string harddisk = "Filesystem.vmdk") : base(aParams, aUseGDB) {
mHarddisk = harddisk;
mDir = Path.Combine(CosmosPaths.Build, @"VMWare\Workstation\");
mVmxPath = Path.Combine(mDir, @"Debug.vmx");
mWorkstationPath = GetPathname("VMware Workstation", "vmware.exe");
mPlayerPath = GetPathname("VMware Player", "vmplayer.exe");
if (mWorkstationPath == null && mPlayerPath == null) {
throw new Exception("VMware not found.");
}
CheckIfHyperVServiceIsRunning();
string xFlavor = aParams[BuildPropertyNames.VMwareEditionString].ToUpper();
mEdition = VMwareEdition.Player;
if (xFlavor == "WORKSTATION") {
mEdition = VMwareEdition.Workstation;
}
// Try alternate if selected one is not installed
if (mEdition == VMwareEdition.Player && mPlayerPath == null && mWorkstationPath != null) {
mEdition = VMwareEdition.Workstation;
} else if (mEdition == VMwareEdition.Workstation && mWorkstationPath == null) {
mEdition = VMwareEdition.Player;
}
}
private static void CheckIfHyperVServiceIsRunning()
{
using (System.ServiceProcess.ServiceController sc = new System.ServiceProcess.ServiceController("vmms")) {
try {
if (sc.Status == System.ServiceProcess.ServiceControllerStatus.Running) {
if (System.Windows.Forms.DialogResult.Yes == global::System.Windows.Forms.MessageBox.Show("Do you want to stop the Hyper-V Virtual Machine Management Service? This is needed to allow to run VMware. If you press \"No\" the debug will stop.", "Question", System.Windows.Forms.MessageBoxButtons.YesNo, System.Windows.Forms.MessageBoxIcon.Question))
sc.Stop();
else
throw new NotSupportedException("VMware start useless, because of running Hyper-V Virtual Machine Management!");
}
}
catch (InvalidOperationException) {
// service not present
}
}
}
protected void ConnectToVMWare(VMWareVirtualHost aHost) {
if (mEdition != VMwareEdition.Player) {
aHost.ConnectToVMWareWorkstation();
} else {
aHost.ConnectToVMWarePlayer();
}
}
protected string GetPathname(string aKey, string aEXE) {
using (var xRegKey = Registry.LocalMachine.OpenSubKey(@"Software\VMware, Inc.\" + aKey, false)) {
if (xRegKey != null) {
string xResult = Path.Combine(((string)xRegKey.GetValue("InstallPath")), aEXE);
if (File.Exists(xResult)) {
return xResult;
}
}
return null;
}
}
public override void Start() {
Cleanup();
CreateDebugVmx();
// Target exe or file
mProcess = new Process();
var xPSI = mProcess.StartInfo;
if (mEdition == VMwareEdition.Player) {
xPSI.FileName = mPlayerPath;
} else {
xPSI.FileName = mWorkstationPath;
}
var xArgSB = new StringBuilder();
string xVmxPath = "\"" + mVmxPath + "\"";
if (mEdition == VMwareEdition.Player) {
xPSI.Arguments = xVmxPath;
} else {
// -x: Auto power on VM. Must be small x, big X means something else.
// -q: Close VMWare when VM is powered off.
// Options must come beore the vmx, and cannot use shellexecute
xPSI.Arguments = "-x -q " + xVmxPath;
}
xPSI.UseShellExecute = true; //must be true to allow elevate the process, sometimes needed if vmware only runs with admin rights
mProcess.EnableRaisingEvents = true;
mProcess.Exited += delegate(Object aSender, EventArgs e) {
if (OnShutDown != null) {
OnShutDown(aSender, e);
}
};
mProcess.Start();
}
public override void Stop() {
using (var xHost = new VMWareVirtualHost()) {
ConnectToVMWare(xHost);
using (var xMachine = xHost.Open(mVmxPath)) {
xMachine.PowerOff();
}
xHost.Close();
}
Cleanup();
}
protected void DeleteFiles(string aPath, string aPattern) {
var xFiles = Directory.GetFiles(aPath, aPattern);
foreach (var xFile in xFiles) {
File.Delete(xFile);
}
}
protected void Cleanup() {
try {
string xPath = Path.GetDirectoryName(mVmxPath);
// Delete old Debug.vmx and other files that might be left over from previous run.
// Especially important with newer versions of VMWare player which defaults to suspend
// when the close button is used.
File.Delete(mVmxPath);
File.Delete(Path.ChangeExtension(mVmxPath, ".nvram"));
// Delete the auto snapshots that latest vmware players create as default.
// It creates them with suffixes though, so we need to wild card find them.
DeleteFiles(xPath, "*.vmxf");
DeleteFiles(xPath, "*.vmss");
DeleteFiles(xPath, "*.vmsd");
DeleteFiles(xPath, "*.vmem");
// Delete log files so that logged data is only from last boot
File.Delete(Path.Combine(xPath, "vmware.log"));
File.Delete(Path.Combine(xPath, "vmware-0.log"));
File.Delete(Path.Combine(xPath, "vmware-1.log"));
File.Delete(Path.Combine(xPath, "vmware-2.log"));
} catch (Exception) {
// Ignore errors, users can stop VS while VMware is still running and files will be locked.
}
}
protected void CreateDebugVmx() {
// VMWare doesn't like to boot a read only VMX.
// We also need to make changes based on project / debug settings.
// Finally we do not want to create VCS checkins based on local user changes.
// Because of this we use Cosmos.vmx as a template and output a Debug.vmx on every run.
using (var xSrc = new StreamReader(Path.Combine(mDir, "Cosmos.vmx"))) {
try {
// Write out Debug.vmx
using (var xDest = new StreamWriter(mVmxPath)) {
string xLine;
while ((xLine = xSrc.ReadLine()) != null) {
var xParts = xLine.Split('=');
if (xParts.Length == 2) {
string xName = xParts[0].Trim();
string xValue = xParts[1].Trim();
if ((xName == "uuid.location") || (xName == "uuid.bios")) {
// We delete uuid entries so VMWare doesnt ask the user "Did you move or copy" the file
xValue = null;
} else if (xName == "ide1:0.fileName")
{
// Set the ISO file for booting
xValue = "\"" + mParams["ISOFile"] + "\"";
} else if (xName == "ide0:0.fileName") {
xValue = "\"" + mHarddisk + "\"";
} else if (xName == "nvram") {
// Point it to an initially non-existent nvram.
// This has the effect of disabling PXE so the boot is faster.
xValue = "\"Debug.nvram\"";
}
if (xValue != null) {
xDest.WriteLine(xName + " = " + xValue);
}
}
}
if (mUseGDB) {
xDest.WriteLine();
xDest.WriteLine("debugStub.listen.guest32 = \"TRUE\"");
xDest.WriteLine("debugStub.hideBreakpoints = \"TRUE\"");
xDest.WriteLine("monitor.debugOnStartGuest32 = \"TRUE\"");
xDest.WriteLine("debugStub.listen.guest32.remote = \"TRUE\"");
}
}
} catch (IOException ex) {
if (ex.Message.Contains(Path.GetFileName(mDir))) {
throw new Exception("The VMware image " + mDir + " is still in use. Please exit current Vmware session with Cosmos and try again.", ex);
}
throw;
}
}
}
}
}