Skip to content

Commit 902c972

Browse files
authored
Merge branch 'master' into DarCampbell-defaultBrowserEdge
2 parents 1300a75 + 3869431 commit 902c972

File tree

3 files changed

+115
-4
lines changed

3 files changed

+115
-4
lines changed

src/common.ts

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -209,3 +209,24 @@ export function isSubfolderOf(subfolder: string, folder: string): boolean {
209209
// Check to see that every sub directory in subfolder exists in folder.
210210
return subfolderArray.length <= folderArray.length && subfolderArray.every((subpath, index) => folderArray[index] === subpath);
211211
}
212+
213+
/**
214+
* Find PowerShell executable from PATH (for Windows only).
215+
*/
216+
export function findPowerShell(): string | undefined {
217+
const dirs: string[] = (process.env.PATH || '').replace(/"+/g, '').split(';').filter(x => x);
218+
const names: string[] = ['pwsh.exe', 'powershell.exe'];
219+
for (const name of names) {
220+
const candidates: string[] = dirs.reduce<string[]>((paths, dir) => [
221+
...paths, path.join(dir, name)
222+
], []);
223+
for (const candidate of candidates) {
224+
try {
225+
if (fs.statSync(candidate).isFile()) {
226+
return name;
227+
}
228+
} catch (e) {
229+
}
230+
}
231+
}
232+
}

src/features/processPicker.ts

Lines changed: 41 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import * as path from 'path';
1010
import * as vscode from 'vscode';
1111

1212
import { PlatformInformation } from '../platform';
13-
import { getExtensionPath } from '../common';
13+
import { findPowerShell, getExtensionPath } from '../common';
1414

1515
export interface AttachItem extends vscode.QuickPickItem {
1616
id: string;
@@ -265,7 +265,7 @@ export class RemoteAttachPicker {
265265
}
266266
}
267267

268-
class Process {
268+
export class Process {
269269
constructor(public name: string, public pid: string, public commandLine: string, public flags: number) { }
270270

271271
public toAttachItem(): AttachItem {
@@ -282,7 +282,8 @@ class Process {
282282
export class DotNetAttachItemsProviderFactory {
283283
static Get(): AttachItemsProvider {
284284
if (os.platform() === 'win32') {
285-
return new WmicAttachItemsProvider();
285+
const pwsh: string | undefined = findPowerShell();
286+
return pwsh ? new CimAttachItemsProvider(pwsh) : new WmicAttachItemsProvider();
286287
}
287288
else {
288289
return new PsAttachItemsProvider();
@@ -423,6 +424,43 @@ export class PsOutputParser {
423424
}
424425
}
425426

427+
export class CimAttachItemsProvider extends DotNetAttachItemsProvider {
428+
constructor(private pwsh: string) { super(); }
429+
430+
protected async getInternalProcessEntries(): Promise<Process[]> {
431+
const pwshCommand: string = `${this.pwsh} -NoProfile -Command`;
432+
const cimCommand: string = 'Get-CimInstance Win32_Process | Select-Object Name,ProcessId,CommandLine | ConvertTo-JSON';
433+
const processes: string = await execChildProcess(`${pwshCommand} "${cimCommand}"`, undefined);
434+
return CimProcessParser.ParseProcessFromCim(processes);
435+
}
436+
}
437+
438+
type CimProcessInfo = {
439+
Name: string;
440+
ProcessId: number;
441+
CommandLine: string | null;
442+
};
443+
444+
export class CimProcessParser {
445+
private static get extendedLengthPathPrefix(): string { return '\\\\?\\'; }
446+
private static get ntObjectManagerPathPrefix(): string { return '\\??\\'; }
447+
448+
// Only public for tests.
449+
public static ParseProcessFromCim(processes: string): Process[] {
450+
const processInfos: CimProcessInfo[] = JSON.parse(processes);
451+
return processInfos.map(info => {
452+
let cmdline: string | undefined = info.CommandLine || undefined;
453+
if (cmdline?.startsWith(this.extendedLengthPathPrefix)) {
454+
cmdline = cmdline.slice(this.extendedLengthPathPrefix.length);
455+
}
456+
if (cmdline?.startsWith(this.ntObjectManagerPathPrefix)) {
457+
cmdline = cmdline.slice(this.ntObjectManagerPathPrefix.length);
458+
}
459+
return new Process(info.Name, `${info.ProcessId}`, cmdline, null);
460+
});
461+
}
462+
}
463+
426464
export class WmicAttachItemsProvider extends DotNetAttachItemsProvider {
427465
protected async getInternalProcessEntries(): Promise<Process[]> {
428466
const wmicCommand = 'wmic process get Name,ProcessId,CommandLine /FORMAT:list';

test/featureTests/processPicker.test.ts

Lines changed: 53 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
* Licensed under the MIT License. See License.txt in the project root for license information.
44
*--------------------------------------------------------------------------------------------*/
55

6-
import { RemoteAttachPicker } from '../../src/features/processPicker';
6+
import { RemoteAttachPicker, Process, CimProcessParser } from '../../src/features/processPicker';
77
import { should } from 'chai';
88

99
suite("Remote Process Picker: Validate quoting arguments.", () => {
@@ -157,6 +157,58 @@ suite("Remote Process Picker: Validate quoting arguments.", () => {
157157
pipeTransport.pipeArgs.should.deep.equal([]);
158158

159159
});
160+
161+
test('Parse valid CIM output', () => {
162+
// output from the command used in CimAttachItemsProvider
163+
const cimOutput: string = String.raw`[
164+
{
165+
"Name": "System Idle Process",
166+
"ProcessId": 0,
167+
"CommandLine": null
168+
},
169+
{
170+
"Name": "WindowsTerminal.exe",
171+
"ProcessId": 5112,
172+
"CommandLine": "\"C:\\Program Files\\WindowsApps\\Microsoft.WindowsTerminalPreview_1.12.2931.0_x64__8wekyb3d8bbwe\\WindowsTerminal.exe\""
173+
},
174+
{
175+
"Name": "conhost.exe",
176+
"ProcessId": 34560,
177+
"CommandLine": "\\\\?\\C:\\WINDOWS\\system32\\conhost.exe --headless --width 80 --height 30 --signal 0x8e0 --server 0x824"
178+
},
179+
{
180+
"Name": "conhost.exe",
181+
"ProcessId": 33732,
182+
"CommandLine": "\\??\\C:\\WINDOWS\\system32\\conhost.exe 0x4"
183+
}
184+
]`;
185+
186+
const parsedOutput: Process[] = CimProcessParser.ParseProcessFromCim(cimOutput);
187+
188+
parsedOutput.length.should.equal(4);
189+
190+
const process1: Process = parsedOutput[0];
191+
const process2: Process = parsedOutput[1];
192+
const process3: Process = parsedOutput[2];
193+
const process4: Process = parsedOutput[3];
194+
195+
var should = require('chai').should();
196+
should.not.exist(process1.commandLine);
197+
process1.name.should.equal('System Idle Process');
198+
process1.pid.should.equal('0');
199+
200+
process2.commandLine.should.equal('"C:\\Program Files\\WindowsApps\\Microsoft.WindowsTerminalPreview_1.12.2931.0_x64__8wekyb3d8bbwe\\WindowsTerminal.exe"');
201+
process2.name.should.equal('WindowsTerminal.exe');
202+
process2.pid.should.equal('5112');
203+
204+
process3.commandLine.should.equal('C:\\WINDOWS\\system32\\conhost.exe --headless --width 80 --height 30 --signal 0x8e0 --server 0x824');
205+
process3.name.should.equal('conhost.exe');
206+
process3.pid.should.equal('34560');
207+
208+
process4.commandLine.should.equal('C:\\WINDOWS\\system32\\conhost.exe 0x4');
209+
process4.name.should.equal('conhost.exe');
210+
process4.pid.should.equal('33732');
211+
});
160212
});
161213

162214
function GetWindowsWSLLaunchJSONWithArrayArgs() {

0 commit comments

Comments
 (0)