@@ -312,32 +312,17 @@ export class ChromeDebugAdapter extends CoreDebugAdapter {
312312 super . onResumed ( ) ;
313313 }
314314
315- public disconnect ( args : DebugProtocol . DisconnectArguments ) : void {
315+ public async disconnect ( args : DebugProtocol . DisconnectArguments ) : Promise < void > {
316316 const hadTerminated = this . _hasTerminated ;
317317
318318 // Disconnect before killing Chrome, because running "taskkill" when it's paused sometimes doesn't kill it
319319 super . disconnect ( args ) ;
320320
321- if ( ( this . _chromeProc || ( ! this . _chromeProc && this . _chromePID ) ) && ! hadTerminated ) {
321+ if ( ( this . _chromeProc || this . _chromePID ) && ! hadTerminated ) {
322322 // Only kill Chrome if the 'disconnect' originated from vscode. If we previously terminated
323323 // due to Chrome shutting down, or devtools taking over, don't kill Chrome.
324324 if ( coreUtils . getPlatform ( ) === coreUtils . Platform . Windows && this . _chromePID ) {
325- let taskkillCmd = `taskkill /PID ${ this . _chromePID } ` ;
326- logger . log ( `Killing Chrome process by pid: ${ taskkillCmd } ` ) ;
327- try {
328- // Run synchronously because this process may be killed before exec() would run
329- execSync ( taskkillCmd ) ;
330- } catch ( e ) {
331- // Can fail if Chrome was already open, and the process with _chromePID is gone.
332- // Or if it already shut down for some reason.
333- }
334- // execSync above may succeed, but Chrome still might not shut down, for example if the web page promts the user about unsaved changes.
335- // In that case, we need to use /F to force shutdown, but we risk Chrome not shutting down correctly.
336- taskkillCmd = `taskkill /F /PID ${ this . _chromePID } ` ;
337- logger . log ( `Killing Chrome process by pid (using force in case the first attempt failed): ${ taskkillCmd } ` ) ;
338- try {
339- execSync ( taskkillCmd ) ;
340- } catch ( e ) { }
325+ await this . killChromeOnWindows ( this . _chromePID ) ;
341326 } else if ( this . _chromeProc ) {
342327 logger . log ( 'Killing Chrome process' ) ;
343328 this . _chromeProc . kill ( 'SIGINT' ) ;
@@ -347,6 +332,37 @@ export class ChromeDebugAdapter extends CoreDebugAdapter {
347332 this . _chromeProc = null ;
348333 }
349334
335+ private async killChromeOnWindows ( chromePID : number ) : Promise < void > {
336+ let taskkillCmd = `taskkill /PID ${ chromePID } ` ;
337+ logger . log ( `Killing Chrome process by pid: ${ taskkillCmd } ` ) ;
338+ try {
339+ execSync ( taskkillCmd ) ;
340+ } catch ( e ) {
341+ // The command will fail if process was not found. This can be safely ignored.
342+ }
343+
344+ for ( let i = 0 ; i < 10 ; i ++ ) {
345+ // Check to see if the process is still running
346+ let tasklistOutput = execSync ( `tasklist /FI "PID eq ${ chromePID } "` ) . toString ( ) ;
347+ if ( ! tasklistOutput . includes ( chromePID . toString ( ) ) ) {
348+ return ;
349+ }
350+
351+ // Give the process some time to close gracefully
352+ logger . log ( `Chrome process with pid ${ chromePID } is still alive, waiting...` )
353+ await new Promise < void > ( ( resolve ) => {
354+ setTimeout ( resolve , 200 ) ;
355+ } ) ;
356+ }
357+
358+ // At this point we can assume the process won't close on its own, so force kill it
359+ let taskkillForceCmd = `taskkill /F /PID ${ chromePID } ` ;
360+ logger . log ( `Killing Chrome process timed out. Killing again using force: ${ taskkillForceCmd } ` ) ;
361+ try {
362+ execSync ( taskkillForceCmd ) ;
363+ } catch ( e ) { }
364+ }
365+
350366 /**
351367 * Opt-in event called when the 'reload' button in the debug widget is pressed
352368 */
0 commit comments