77
88//using Vestris.VMWareLib;
99using Cosmos . Build . Common ;
10+ using System . Linq ;
1011
11- namespace Cosmos . Debug . Hosts {
12- public class VMware : Host {
12+ namespace Cosmos . Debug . Hosts
13+ {
14+ public class VMware : Host
15+ {
1316 protected VMwareEdition mEdition ;
1417 protected string mDir ;
1518 protected string mVmxPath ;
1619 protected Process mProcess ;
20+ protected Process [ ] mProcessesBefore , mProcessesAfter , mProcesses ;
1721 protected string mWorkstationPath ;
1822 protected string mPlayerPath ;
1923 protected string mHarddisk ;
2024
21- public VMware ( Dictionary < string , string > aParams , bool aUseGDB , string harddisk = "Filesystem.vmdk" ) : base ( aParams , aUseGDB ) {
25+ public VMware ( Dictionary < string , string > aParams , bool aUseGDB , string harddisk = "Filesystem.vmdk" ) : base ( aParams , aUseGDB )
26+ {
2227 mHarddisk = harddisk ;
2328 mDir = Path . Combine ( CosmosPaths . Build , @"VMWare\Workstation\" ) ;
2429 mVmxPath = Path . Combine ( mDir , @"Debug.vmx" ) ;
2530
2631 mWorkstationPath = GetPathname ( "VMware Workstation" , "vmware.exe" ) ;
2732 mPlayerPath = GetPathname ( "VMware Player" , "vmplayer.exe" ) ;
28- if ( mWorkstationPath == null && mPlayerPath == null ) {
33+ if ( mWorkstationPath == null && mPlayerPath == null )
34+ {
2935 throw new Exception ( "VMware not found." ) ;
3036 }
3137
3238 string xFlavor = aParams [ BuildPropertyNames . VMwareEditionString ] . ToUpper ( ) ;
3339 mEdition = VMwareEdition . Player ;
34- if ( xFlavor == "WORKSTATION" ) {
40+ if ( xFlavor == "WORKSTATION" )
41+ {
3542 mEdition = VMwareEdition . Workstation ;
3643 }
3744
3845 // Try alternate if selected one is not installed
39- if ( mEdition == VMwareEdition . Player && mPlayerPath == null && mWorkstationPath != null ) {
46+ if ( mEdition == VMwareEdition . Player && mPlayerPath == null && mWorkstationPath != null )
47+ {
4048 mEdition = VMwareEdition . Workstation ;
41- } else if ( mEdition == VMwareEdition . Workstation && mWorkstationPath == null ) {
49+ }
50+ else if ( mEdition == VMwareEdition . Workstation && mWorkstationPath == null )
51+ {
4252 mEdition = VMwareEdition . Player ;
4353 }
4454 }
@@ -51,36 +61,47 @@ public VMware(Dictionary<string, string> aParams, bool aUseGDB,string harddisk =
5161 // }
5262 //}
5363
54- protected string GetPathname ( string aKey , string aEXE ) {
55- using ( var xRegKey = Registry . LocalMachine . OpenSubKey ( @"Software\WOW6432Node\VMware, Inc.\" + aKey , false ) ) {
56- if ( xRegKey != null ) {
64+ protected string GetPathname ( string aKey , string aEXE )
65+ {
66+ using ( var xRegKey = Registry . LocalMachine . OpenSubKey ( @"Software\WOW6432Node\VMware, Inc.\" + aKey , false ) )
67+ {
68+ if ( xRegKey != null )
69+ {
5770 string xResult = Path . Combine ( ( ( string ) xRegKey . GetValue ( "InstallPath" ) ) , aEXE ) ;
58- if ( File . Exists ( xResult ) ) {
71+ if ( File . Exists ( xResult ) )
72+ {
5973 return xResult ;
6074 }
6175 }
6276 return null ;
6377 }
6478 }
6579
66- public override void Start ( ) {
80+ public override void Start ( )
81+ {
6782 Cleanup ( ) ;
6883 CreateDebugVmx ( ) ;
6984
7085 // Target exe or file
7186 mProcess = new Process ( ) ;
7287 var xPSI = mProcess . StartInfo ;
73- if ( mEdition == VMwareEdition . Player ) {
88+ if ( mEdition == VMwareEdition . Player )
89+ {
7490 xPSI . FileName = mPlayerPath ;
75- } else {
91+ }
92+ else
93+ {
7694 xPSI . FileName = mWorkstationPath ;
7795 }
7896 var xArgSB = new StringBuilder ( ) ;
7997
8098 string xVmxPath = "\" " + mVmxPath + "\" " ;
81- if ( mEdition == VMwareEdition . Player ) {
99+ if ( mEdition == VMwareEdition . Player )
100+ {
82101 xPSI . Arguments = xVmxPath ;
83- } else {
102+ }
103+ else
104+ {
84105 // -x: Auto power on VM. Must be small x, big X means something else.
85106 // -q: Close VMWare when VM is powered off.
86107 // Options must come beore the vmx, and cannot use shellexecute
@@ -89,7 +110,27 @@ public override void Start() {
89110 xPSI . UseShellExecute = false ; //must be true to allow elevate the process, sometimes needed if vmware only runs with admin rights
90111 mProcess . EnableRaisingEvents = true ;
91112 mProcess . Exited += ExitCallback ;
113+
114+ mProcessesBefore = Process . GetProcessesByName ( "vmware-vmx" ) ;
92115 mProcess . Start ( ) ;
116+
117+ Stopwatch watch = Stopwatch . StartNew ( ) ;
118+
119+ // Wait for the process to spawn
120+ mProcessesAfter = Process . GetProcessesByName ( "vmware-vmx" ) ;
121+ while ( mProcessesAfter . Length == mProcessesBefore . Length )
122+ {
123+ if ( watch . Elapsed . Seconds == 15 )
124+ {
125+ watch . Stop ( ) ;
126+ throw new TimeoutException ( "VMware Workstation took too long to launch!" ) ;
127+ }
128+
129+ mProcessesAfter = Process . GetProcessesByName ( "vmware-vmx" ) ;
130+ }
131+
132+ // Get the new processes
133+ mProcesses = mProcessesBefore . Concat ( mProcessesAfter ) . Distinct ( ) . ToArray ( ) ;
93134 }
94135
95136 private void ExitCallback ( object sender , EventArgs e )
@@ -106,18 +147,19 @@ private void ExitCallback(object sender, EventArgs e)
106147 }
107148 }
108149
109- public override void Stop ( ) {
150+ public override void Stop ( )
151+ {
110152 if ( null != mProcess )
111153 {
112154 try
113155 {
114156 //TODO: Close VMWare properly
115157
116- //Force Kill VMWare
158+ // Kill the VMware GUI
117159 mProcess . Kill ( ) ;
118160
119- //kil vmware-vmx.exe
120- foreach ( var process in Process . GetProcessesByName ( "vmware-vmx" ) )
161+ // Kill the actual VM instance
162+ foreach ( var process in mProcesses )
121163 {
122164 process . Kill ( ) ;
123165 }
@@ -129,15 +171,19 @@ public override void Stop() {
129171 Cleanup ( ) ;
130172 }
131173
132- protected void DeleteFiles ( string aPath , string aPattern ) {
174+ protected void DeleteFiles ( string aPath , string aPattern )
175+ {
133176 var xFiles = Directory . GetFiles ( aPath , aPattern ) ;
134- foreach ( var xFile in xFiles ) {
177+ foreach ( var xFile in xFiles )
178+ {
135179 File . Delete ( xFile ) ;
136180 }
137181 }
138182
139- protected void Cleanup ( ) {
140- try {
183+ protected void Cleanup ( )
184+ {
185+ try
186+ {
141187 string xPath = Path . GetDirectoryName ( mVmxPath ) ;
142188 // Delete old Debug.vmx and other files that might be left over from previous run.
143189 // Especially important with newer versions of VMWare player which defaults to suspend
@@ -155,59 +201,78 @@ protected void Cleanup() {
155201 File . Delete ( Path . Combine ( xPath , "vmware-0.log" ) ) ;
156202 File . Delete ( Path . Combine ( xPath , "vmware-1.log" ) ) ;
157203 File . Delete ( Path . Combine ( xPath , "vmware-2.log" ) ) ;
158- } catch ( Exception ) {
204+ }
205+ catch ( Exception )
206+ {
159207 // Ignore errors, users can stop VS while VMware is still running and files will be locked.
160208 }
161209 }
162210
163- protected void CreateDebugVmx ( ) {
211+ protected void CreateDebugVmx ( )
212+ {
164213 // VMWare doesn't like to boot a read only VMX.
165214 // We also need to make changes based on project / debug settings.
166215 // Finally we do not want to create VCS checkins based on local user changes.
167216 // Because of this we use Cosmos.vmx as a template and output a Debug.vmx on every run.
168- using ( var xSrc = new StreamReader ( File . Open ( Path . Combine ( mDir , "Cosmos.vmx" ) , FileMode . OpenOrCreate ) ) ) {
169- try {
217+ using ( var xSrc = new StreamReader ( File . Open ( Path . Combine ( mDir , "Cosmos.vmx" ) , FileMode . OpenOrCreate ) ) )
218+ {
219+ try
220+ {
170221 // Write out Debug.vmx
171- using ( var xDest = new StreamWriter ( File . Open ( mVmxPath , FileMode . Create ) ) ) {
222+ using ( var xDest = new StreamWriter ( File . Open ( mVmxPath , FileMode . Create ) ) )
223+ {
172224 string xLine ;
173- while ( ( xLine = xSrc . ReadLine ( ) ) != null ) {
225+ while ( ( xLine = xSrc . ReadLine ( ) ) != null )
226+ {
174227 var xParts = xLine . Split ( '=' ) ;
175- if ( xParts . Length == 2 ) {
228+ if ( xParts . Length == 2 )
229+ {
176230 string xName = xParts [ 0 ] . Trim ( ) ;
177231 string xValue = xParts [ 1 ] . Trim ( ) ;
178232
179- if ( ( xName == "uuid.location" ) || ( xName == "uuid.bios" ) ) {
233+ if ( ( xName == "uuid.location" ) || ( xName == "uuid.bios" ) )
234+ {
180235 // We delete uuid entries so VMWare doesnt ask the user "Did you move or copy" the file
181236 xValue = null ;
182237
183- } else if ( xName == "ide1:0.fileName" )
238+ }
239+ else if ( xName == "ide1:0.fileName" )
184240 {
185241 // Set the ISO file for booting
186242 xValue = "\" " + mParams [ "ISOFile" ] + "\" " ;
187- } else if ( xName == "ide0:0.fileName" ) {
243+ }
244+ else if ( xName == "ide0:0.fileName" )
245+ {
188246 xValue = "\" " + mHarddisk + "\" " ;
189- } else if ( xName == "nvram" ) {
247+ }
248+ else if ( xName == "nvram" )
249+ {
190250 // Point it to an initially non-existent nvram.
191251 // This has the effect of disabling PXE so the boot is faster.
192252 xValue = "\" Debug.nvram\" " ;
193253 }
194254
195- if ( xValue != null ) {
255+ if ( xValue != null )
256+ {
196257 xDest . WriteLine ( xName + " = " + xValue ) ;
197258 }
198259 }
199260 }
200261
201- if ( mUseGDB ) {
262+ if ( mUseGDB )
263+ {
202264 xDest . WriteLine ( ) ;
203265 xDest . WriteLine ( "debugStub.listen.guest32 = \" TRUE\" " ) ;
204266 xDest . WriteLine ( "debugStub.hideBreakpoints = \" TRUE\" " ) ;
205267 xDest . WriteLine ( "monitor.debugOnStartGuest32 = \" TRUE\" " ) ;
206268 xDest . WriteLine ( "debugStub.listen.guest32.remote = \" TRUE\" " ) ;
207269 }
208270 }
209- } catch ( IOException ex ) {
210- if ( ex . Message . Contains ( Path . GetFileName ( mDir ) ) ) {
271+ }
272+ catch ( IOException ex )
273+ {
274+ if ( ex . Message . Contains ( Path . GetFileName ( mDir ) ) )
275+ {
211276 throw new Exception ( "The VMware image " + mDir + " is still in use. Please exit current Vmware session with Cosmos and try again." , ex ) ;
212277 }
213278 throw ;
0 commit comments