Skip to content

Commit

Permalink
Add CimAttachItemsProvider
Browse files Browse the repository at this point in the history
Add CimAttachItemsProvider to retrieve processes with Get-CimInstance
Win32_Process if powershell is found in PATH.

From microsoft/vscode-cpptools#8329
  • Loading branch information
WardenGnaw committed Oct 27, 2021
1 parent a632c23 commit 8db6a96
Show file tree
Hide file tree
Showing 3 changed files with 116 additions and 4 deletions.
22 changes: 22 additions & 0 deletions src/common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -209,3 +209,25 @@ export function isSubfolderOf(subfolder: string, folder: string): boolean {
// Check to see that every sub directory in subfolder exists in folder.
return subfolderArray.length <= folderArray.length && subfolderArray.every((subpath, index) => folderArray[index] === subpath);
}

/**
* Find PowerShell executable from PATH (for Windows only).
*/
export function findPowerShell(): string | undefined {
const dirs: string[] = (process.env.PATH || '').replace(/"+/g, '').split(';').filter(x => x);
const exts: string[] = (process.env.PATHEXT || '').split(';');
const names: string[] = ['pwsh', 'powershell'];
for (const name of names) {
const candidates: string[] = dirs.reduce<string[]>((paths, dir) => [
...paths, ...exts.map(ext => path.join(dir, name + ext))
], []);
for (const candidate of candidates) {
try {
if (fs.statSync(candidate).isFile()) {
return name;
}
} catch (e) {
}
}
}
}
47 changes: 44 additions & 3 deletions src/features/processPicker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import * as path from 'path';
import * as vscode from 'vscode';

import { PlatformInformation } from '../platform';
import { getExtensionPath } from '../common';
import { findPowerShell, getExtensionPath } from '../common';

export interface AttachItem extends vscode.QuickPickItem {
id: string;
Expand Down Expand Up @@ -265,7 +265,7 @@ export class RemoteAttachPicker {
}
}

class Process {
export class Process {
constructor(public name: string, public pid: string, public commandLine: string, public flags: number) { }

public toAttachItem(): AttachItem {
Expand All @@ -282,7 +282,8 @@ class Process {
export class DotNetAttachItemsProviderFactory {
static Get(): AttachItemsProvider {
if (os.platform() === 'win32') {
return new WmicAttachItemsProvider();
const pwsh: string | undefined = findPowerShell();
return pwsh ? new CimAttachItemsProvider(pwsh) : new WmicAttachItemsProvider();
}
else {
return new PsAttachItemsProvider();
Expand Down Expand Up @@ -423,6 +424,46 @@ export class PsOutputParser {
}
}

export class CimAttachItemsProvider extends DotNetAttachItemsProvider {
constructor(private pwsh: string) { super(); }

// Perf numbers on Win10:
// TODO

protected async getInternalProcessEntries(): Promise<Process[]> {
const pwshCommand: string = `${this.pwsh} -NoProfile -Command`;
const cimCommand: string = 'Get-CimInstance Win32_Process | Select-Object Name,ProcessId,CommandLine | ConvertTo-JSON';
const processes: string = await execChildProcess(`${pwshCommand} "${cimCommand}"`, undefined);
return CimProcessParser.ParseProcessFromCim(processes);
}
}

type CimProcessInfo = {
Name: string;
ProcessId: number;
CommandLine: string | null;
};

export class CimProcessParser {
private static get extendedLengthPathPrefix(): string { return '\\\\?\\'; }
private static get ntObjectManagerPathPrefix(): string { return '\\??\\'; }

// Only public for tests.
public static ParseProcessFromCim(processes: string): Process[] {
const processInfos: CimProcessInfo[] = JSON.parse(processes);
return processInfos.map(info => {
let cmdline: string | undefined = info.CommandLine || undefined;
if (cmdline?.startsWith(this.extendedLengthPathPrefix)) {
cmdline = cmdline.slice(this.extendedLengthPathPrefix.length);
}
if (cmdline?.startsWith(this.ntObjectManagerPathPrefix)) {
cmdline = cmdline.slice(this.ntObjectManagerPathPrefix.length);
}
return new Process(info.Name, `${info.ProcessId}`, cmdline, null);
});
}
}

export class WmicAttachItemsProvider extends DotNetAttachItemsProvider {
protected async getInternalProcessEntries(): Promise<Process[]> {
const wmicCommand = 'wmic process get Name,ProcessId,CommandLine /FORMAT:list';
Expand Down
51 changes: 50 additions & 1 deletion test/featureTests/processPicker.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/

import { RemoteAttachPicker } from '../../src/features/processPicker';
import { RemoteAttachPicker, Process, CimProcessParser } from '../../src/features/processPicker';
import { should } from 'chai';

suite("Remote Process Picker: Validate quoting arguments.", () => {
Expand Down Expand Up @@ -157,6 +157,55 @@ suite("Remote Process Picker: Validate quoting arguments.", () => {
pipeTransport.pipeArgs.should.deep.equal([]);

});

test('Parse valid CIM output', () => {
// output from the command used in CimAttachItemsProvider
const cimOutput: string = String.raw`[
{
"Name": "System Idle Process",
"ProcessId": 0,
"CommandLine": null
},
{
"Name": "WindowsTerminal.exe",
"ProcessId": 5112,
"CommandLine": "\"C:\\Program Files\\WindowsApps\\Microsoft.WindowsTerminalPreview_1.12.2931.0_x64__8wekyb3d8bbwe\\WindowsTerminal.exe\""
},
{
"Name": "conhost.exe",
"ProcessId": 34560,
"CommandLine": "\\\\?\\C:\\WINDOWS\\system32\\conhost.exe --headless --width 80 --height 30 --signal 0x8e0 --server 0x824"
},
{
"Name": "conhost.exe",
"ProcessId": 33732,
"CommandLine": "\\??\\C:\\WINDOWS\\system32\\conhost.exe 0x4"
}
]`;

const parsedOutput: Process[] = CimProcessParser.ParseProcessFromCim(cimOutput);

const process1: Process = parsedOutput[0];
const process2: Process = parsedOutput[1];
const process3: Process = parsedOutput[2];
const process4: Process = parsedOutput[3];

process1.commandLine.should.equal(undefined);
process1.name.should.equal('System Idle Process');
process1.pid.should.equal(0);

process2.commandLine.should.equal('"C:\\Program Files\\WindowsApps\\Microsoft.WindowsTerminalPreview_1.12.2931.0_x64__8wekyb3d8bbwe\\WindowsTerminal.exe"');
process2.name.should.equal('WindowsTerminal.exe');
process2.pid.should.equal('5112');

process3.commandLine.should.equal('C:\\WINDOWS\\system32\\conhost.exe --headless --width 80 --height 30 --signal 0x8e0 --server 0x824');
process3.name.should.equal('conhost.exe');
process3.pid.should.equal('34560');

process4.commandLine.should.equal('C:\\WINDOWS\\system32\\conhost.exe 0x4');
process4.name.should.equal('conhost.exe');
process4.pid.should.equal('33732');
});
});

function GetWindowsWSLLaunchJSONWithArrayArgs() {
Expand Down

0 comments on commit 8db6a96

Please sign in to comment.