Skip to content

Commit 4016be1

Browse files
authored
Deprecate WMIC in favor of Get-CimInstance (#8329)
* use `Get-CimInstance` to retrieve processes * Add unit test * Fix bugs * Parse JSON output
1 parent 6b00343 commit 4016be1

File tree

3 files changed

+114
-2
lines changed

3 files changed

+114
-2
lines changed

Extension/src/Debugger/nativeAttach.ts

Lines changed: 43 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import * as os from 'os';
88
import { AttachItemsProvider } from './attachToProcess';
99
import { AttachItem } from './attachQuickPick';
1010
import * as nls from 'vscode-nls';
11+
import { findPowerShell } from '../common';
1112

1213
nls.config({ messageFormat: nls.MessageFormat.bundle, bundleFormat: nls.BundleFormat.standalone })();
1314
const localize: nls.LocalizeFunc = nls.loadMessageBundle();
@@ -28,7 +29,8 @@ export class Process {
2829
export class NativeAttachItemsProviderFactory {
2930
static Get(): AttachItemsProvider {
3031
if (os.platform() === 'win32') {
31-
return new WmicAttachItemsProvider();
32+
const pwsh: string | undefined = findPowerShell();
33+
return pwsh ? new CimAttachItemsProvider(pwsh) : new WmicAttachItemsProvider();
3234
} else {
3335
return new PsAttachItemsProvider();
3436
}
@@ -254,3 +256,43 @@ export class WmicProcessParser {
254256
}
255257
}
256258
}
259+
260+
export class CimAttachItemsProvider extends NativeAttachItemsProvider {
261+
constructor(private pwsh: string) { super(); }
262+
263+
// Perf numbers on Win10:
264+
// TODO
265+
266+
protected async getInternalProcessEntries(): Promise<Process[]> {
267+
const pwshCommand: string = `${this.pwsh} -NoProfile -Command`;
268+
const cimCommand: string = 'Get-CimInstance Win32_Process | Select-Object Name,ProcessId,CommandLine | ConvertTo-JSON';
269+
const processes: string = await execChildProcess(`${pwshCommand} "${cimCommand}"`, undefined);
270+
return CimProcessParser.ParseProcessFromCim(processes);
271+
}
272+
}
273+
274+
type CimProcessInfo = {
275+
Name: string;
276+
ProcessId: number;
277+
CommandLine: string | null;
278+
};
279+
280+
export class CimProcessParser {
281+
private static get extendedLengthPathPrefix(): string { return '\\\\?\\'; }
282+
private static get ntObjectManagerPathPrefix(): string { return '\\??\\'; }
283+
284+
// Only public for tests.
285+
public static ParseProcessFromCim(processes: string): Process[] {
286+
const processInfos: CimProcessInfo[] = JSON.parse(processes);
287+
return processInfos.map(info => {
288+
let cmdline: string | undefined = info.CommandLine || undefined;
289+
if (cmdline?.startsWith(this.extendedLengthPathPrefix)) {
290+
cmdline = cmdline.slice(this.extendedLengthPathPrefix.length);
291+
}
292+
if (cmdline?.startsWith(this.ntObjectManagerPathPrefix)) {
293+
cmdline = cmdline.slice(this.ntObjectManagerPathPrefix.length);
294+
}
295+
return new Process(info.Name, `${info.ProcessId}`, cmdline);
296+
});
297+
}
298+
}

Extension/src/common.ts

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1327,3 +1327,24 @@ export function normalizeArg(arg: string): string {
13271327
}
13281328
}
13291329

1330+
/**
1331+
* Find PowerShell executable from PATH (for Windows only).
1332+
*/
1333+
export function findPowerShell(): string | undefined {
1334+
const dirs: string[] = (process.env.PATH || '').replace(/"+/g, '').split(';').filter(x => x);
1335+
const exts: string[] = (process.env.PATHEXT || '').split(';');
1336+
const names: string[] = ['pwsh', 'powershell'];
1337+
for (const name of names) {
1338+
const candidates: string[] = dirs.reduce<string[]>((paths, dir) => [
1339+
...paths, ...exts.map(ext => path.join(dir, name + ext))
1340+
], []);
1341+
for (const candidate of candidates) {
1342+
try {
1343+
if (fs.statSync(candidate).isFile()) {
1344+
return name;
1345+
}
1346+
} catch (e) {
1347+
}
1348+
}
1349+
}
1350+
}

Extension/test/unitTests/extension.test.ts

Lines changed: 50 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
import * as assert from 'assert';
77
import * as os from 'os';
88
import { LinuxDistribution } from '../../src/linuxDistribution';
9-
import { Process, WmicProcessParser, PsProcessParser } from '../../src/Debugger/nativeAttach';
9+
import { Process, WmicProcessParser, PsProcessParser, CimProcessParser } from '../../src/Debugger/nativeAttach';
1010

1111
suite("LinuxDistro Tests", () => {
1212
test("Parse valid os-release file", () => {
@@ -125,4 +125,53 @@ suite("Pick Process Tests", () => {
125125
assert.equal(process2.name, 'mdworker');
126126
assert.equal(process2.pid, '15220');
127127
});
128+
129+
test('Parse valid CIM output', () => {
130+
// output from the command used in CimAttachItemsProvider
131+
const cimOutput: string = String.raw`[
132+
{
133+
"Name": "System Idle Process",
134+
"ProcessId": 0,
135+
"CommandLine": null
136+
},
137+
{
138+
"Name": "WindowsTerminal.exe",
139+
"ProcessId": 5112,
140+
"CommandLine": "\"C:\\Program Files\\WindowsApps\\Microsoft.WindowsTerminalPreview_1.12.2931.0_x64__8wekyb3d8bbwe\\WindowsTerminal.exe\""
141+
},
142+
{
143+
"Name": "conhost.exe",
144+
"ProcessId": 34560,
145+
"CommandLine": "\\\\?\\C:\\WINDOWS\\system32\\conhost.exe --headless --width 80 --height 30 --signal 0x8e0 --server 0x824"
146+
},
147+
{
148+
"Name": "conhost.exe",
149+
"ProcessId": 33732,
150+
"CommandLine": "\\??\\C:\\WINDOWS\\system32\\conhost.exe 0x4"
151+
}
152+
]`;
153+
154+
const parsedOutput: Process[] = CimProcessParser.ParseProcessFromCim(cimOutput);
155+
156+
const process1: Process = parsedOutput[0];
157+
const process2: Process = parsedOutput[1];
158+
const process3: Process = parsedOutput[2];
159+
const process4: Process = parsedOutput[3];
160+
161+
assert.equal(process1.commandLine, undefined);
162+
assert.equal(process1.name, 'System Idle Process');
163+
assert.equal(process1.pid, '0');
164+
165+
assert.equal(process2.commandLine, '"C:\\Program Files\\WindowsApps\\Microsoft.WindowsTerminalPreview_1.12.2931.0_x64__8wekyb3d8bbwe\\WindowsTerminal.exe"');
166+
assert.equal(process2.name, 'WindowsTerminal.exe');
167+
assert.equal(process2.pid, '5112');
168+
169+
assert.equal(process3.commandLine, 'C:\\WINDOWS\\system32\\conhost.exe --headless --width 80 --height 30 --signal 0x8e0 --server 0x824');
170+
assert.equal(process3.name, 'conhost.exe');
171+
assert.equal(process3.pid, '34560');
172+
173+
assert.equal(process4.commandLine, 'C:\\WINDOWS\\system32\\conhost.exe 0x4');
174+
assert.equal(process4.name, 'conhost.exe');
175+
assert.equal(process4.pid, '33732');
176+
});
128177
});

0 commit comments

Comments
 (0)