Skip to content

Commit d173c53

Browse files
committed
Fix issues with resolving correct PowerShell paths in 64-bit VS Code
This change introduces a new module that contains all the logic for determining the current OS platform, process architecture and OS architecture for use in determining available Windows PowerShell EXE paths. This module helps resolve problems that were introduced by the new 64-bit distribution of Visual Studio Code. Fixes #1008 Fixes #1007 Fixes #1006 Fixes #993
1 parent 9aab76e commit d173c53

File tree

2 files changed

+217
-81
lines changed

2 files changed

+217
-81
lines changed

src/platform.ts

+163
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,163 @@
1+
/*---------------------------------------------------------
2+
* Copyright (C) Microsoft Corporation. All rights reserved.
3+
*--------------------------------------------------------*/
4+
5+
import fs = require('fs');
6+
import os = require('os');
7+
import path = require('path');
8+
import vscode = require('vscode');
9+
import process = require('process');
10+
import Settings = require('./settings');
11+
12+
export enum OperatingSystem {
13+
Unknown,
14+
Windows,
15+
MacOS,
16+
Linux
17+
}
18+
19+
export interface PlatformDetails {
20+
operatingSystem: OperatingSystem
21+
isOS64Bit: boolean
22+
isProcess64Bit: boolean
23+
}
24+
25+
interface PowerShellExeDetails {
26+
versionName: string;
27+
exePath: string;
28+
}
29+
30+
export function getPlatformDetails(): PlatformDetails {
31+
var operatingSystem = OperatingSystem.Unknown;
32+
33+
if (process.platform === "win32") {
34+
operatingSystem = OperatingSystem.Windows;
35+
}
36+
else if (process.platform === "darwin") {
37+
operatingSystem = OperatingSystem.MacOS;
38+
}
39+
else if (process.platform === "linux") {
40+
operatingSystem = OperatingSystem.Linux;
41+
}
42+
43+
let isProcess64Bit = process.arch === "x64";
44+
45+
return {
46+
operatingSystem: operatingSystem,
47+
isOS64Bit: isProcess64Bit || process.env.hasOwnProperty('PROCESSOR_ARCHITEW6432'),
48+
isProcess64Bit: isProcess64Bit
49+
}
50+
}
51+
52+
export function getDefaultPowerShellPath(
53+
platformDetails: PlatformDetails,
54+
use32Bit: boolean = false): string | null {
55+
56+
var powerShellExePath = undefined;
57+
58+
// Find the path to powershell.exe based on the current platform
59+
// and the user's desire to run the x86 version of PowerShell
60+
if (platformDetails.operatingSystem == OperatingSystem.Windows) {
61+
powerShellExePath =
62+
use32Bit || !process.env.hasOwnProperty('PROCESSOR_ARCHITEW6432')
63+
? System32PowerShellPath
64+
: SysnativePowerShellPath
65+
}
66+
else if (platformDetails.operatingSystem == OperatingSystem.MacOS) {
67+
powerShellExePath = "/usr/local/bin/powershell";
68+
}
69+
else if (platformDetails.operatingSystem == OperatingSystem.Linux) {
70+
powerShellExePath = "/usr/bin/powershell";
71+
}
72+
73+
return powerShellExePath;
74+
}
75+
76+
export function getWindowsSystemPowerShellPath(systemFolderName: string) {
77+
return `${process.env.windir}\\${systemFolderName}\\WindowsPowerShell\\v1.0\\powershell.exe`
78+
}
79+
80+
export const System32PowerShellPath = getWindowsSystemPowerShellPath('System32');
81+
export const SysnativePowerShellPath = getWindowsSystemPowerShellPath('Sysnative');
82+
export const SysWow64PowerShellPath = getWindowsSystemPowerShellPath('SysWow64');
83+
84+
const powerShell64BitPathOn32Bit = SysnativePowerShellPath.toLocaleLowerCase();
85+
const powerShell32BitPathOn64Bit = SysWow64PowerShellPath.toLocaleLowerCase();
86+
87+
export function fixWindowsPowerShellPath(powerShellExePath: string, platformDetails: PlatformDetails): string {
88+
let lowerCasedPath = powerShellExePath.toLocaleLowerCase();
89+
90+
if ((platformDetails.isProcess64Bit && (lowerCasedPath === powerShell64BitPathOn32Bit)) ||
91+
(!platformDetails.isProcess64Bit && (lowerCasedPath === powerShell32BitPathOn64Bit))) {
92+
return System32PowerShellPath;
93+
}
94+
95+
// If the path doesn't need to be fixed, return the original
96+
return powerShellExePath;
97+
}
98+
99+
export function getPowerShellExeItems(platformDetails: PlatformDetails): PowerShellExeDetails[] {
100+
101+
var paths: PowerShellExeDetails[] = [];
102+
103+
const windowsPowerShell64BitLabel = "Windows PowerShell (x64)";
104+
const windowsPowerShell32BitLabel = "Windows PowerShell (x86)";
105+
106+
if (platformDetails.operatingSystem === OperatingSystem.Windows) {
107+
const psCoreInstallPath =
108+
(!platformDetails.isProcess64Bit ? process.env.ProgramW6432 : process.env.ProgramFiles) + '\\PowerShell';
109+
110+
if (platformDetails.isProcess64Bit) {
111+
paths.push({
112+
versionName: windowsPowerShell64BitLabel,
113+
exePath: System32PowerShellPath
114+
})
115+
116+
paths.push({
117+
versionName: windowsPowerShell32BitLabel,
118+
exePath: SysWow64PowerShellPath
119+
})
120+
}
121+
else {
122+
if (platformDetails.isOS64Bit) {
123+
paths.push({
124+
versionName: windowsPowerShell64BitLabel,
125+
exePath: SysnativePowerShellPath
126+
})
127+
}
128+
129+
paths.push({
130+
versionName: windowsPowerShell32BitLabel,
131+
exePath: System32PowerShellPath
132+
})
133+
}
134+
135+
if (fs.existsSync(psCoreInstallPath)) {
136+
var psCorePaths =
137+
fs.readdirSync(psCoreInstallPath)
138+
.map(item => path.join(psCoreInstallPath, item))
139+
.filter(item => fs.lstatSync(item).isDirectory())
140+
.map(item => {
141+
return {
142+
versionName: `PowerShell Core ${path.parse(item).base}`,
143+
exePath: path.join(item, "powershell.exe")
144+
};
145+
});
146+
147+
if (psCorePaths) {
148+
paths = paths.concat(psCorePaths);
149+
}
150+
}
151+
}
152+
else {
153+
paths.push({
154+
versionName: "PowerShell Core",
155+
exePath:
156+
os.platform() === "darwin"
157+
? "/usr/local/bin/powershell"
158+
: "/usr/bin/powershell"
159+
});
160+
}
161+
162+
return paths;
163+
}

src/session.ts

+54-81
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,18 @@ import { IFeature } from './feature';
1616
import { Message } from 'vscode-jsonrpc';
1717
import { PowerShellProcess } from './process';
1818
import { StringDecoder } from 'string_decoder';
19+
1920
import {
2021
LanguageClient, LanguageClientOptions, Executable,
2122
RequestType, RequestType0, NotificationType,
2223
StreamInfo, ErrorAction, CloseAction, RevealOutputChannelOn,
2324
Middleware, ResolveCodeLensSignature } from 'vscode-languageclient';
2425

26+
import {
27+
OperatingSystem, PlatformDetails, getDefaultPowerShellPath,
28+
getPlatformDetails, fixWindowsPowerShellPath,
29+
getPowerShellExeItems } from './platform';
30+
2531
export enum SessionStatus {
2632
NotStarted,
2733
Initializing,
@@ -35,12 +41,12 @@ export class SessionManager implements Middleware {
3541
private ShowSessionMenuCommandName = "PowerShell.ShowSessionMenu";
3642

3743
private hostVersion: string;
38-
private isWindowsOS: boolean;
3944
private editorServicesArgs: string;
4045
private powerShellExePath: string = "";
4146
private sessionStatus: SessionStatus;
4247
private suppressRestartPrompt: boolean;
4348
private focusConsoleOnExecute: boolean;
49+
private platformDetails: PlatformDetails;
4450
private extensionFeatures: IFeature[] = [];
4551
private statusBarItem: vscode.StatusBarItem;
4652
private languageServerProcess: PowerShellProcess;
@@ -61,7 +67,7 @@ export class SessionManager implements Middleware {
6167
private requiredEditorServicesVersion: string,
6268
private log: Logger) {
6369

64-
this.isWindowsOS = os.platform() == "win32";
70+
this.platformDetails = getPlatformDetails();
6571

6672
// Get the current version of this extension
6773
this.hostVersion =
@@ -542,9 +548,53 @@ export class SessionManager implements Middleware {
542548
this.sessionSettings.developer.powerShellExePath ||
543549
"").trim();
544550

551+
if (this.platformDetails.operatingSystem === OperatingSystem.Windows &&
552+
powerShellExePath.length > 0) {
553+
554+
// Check the path bitness
555+
let fixedPath =
556+
fixWindowsPowerShellPath(
557+
powerShellExePath,
558+
this.platformDetails);
559+
560+
if (fixedPath !== powerShellExePath) {
561+
let bitness = this.platformDetails.isOS64Bit ? 64 : 32;
562+
// Show deprecation message with fix action.
563+
// We don't need to wait on this to complete
564+
// because we can finish gathering the configured
565+
// PowerShell path without the fix
566+
vscode
567+
.window
568+
.showWarningMessage(
569+
`The specified PowerShell path is incorrect for ${bitness}-bit VS Code, using '${fixedPath}' instead.`,
570+
"Fix Setting Automatically")
571+
.then(choice => {
572+
if (choice) {
573+
this.suppressRestartPrompt = true;
574+
Settings
575+
.change(
576+
"powerShellExePath",
577+
this.sessionSettings.developer.powerShellExePath,
578+
true)
579+
.then(() => {
580+
return Settings.change(
581+
"developer.powerShellExePath",
582+
undefined,
583+
true)
584+
})
585+
.then(() => {
586+
this.suppressRestartPrompt = false;
587+
});
588+
}
589+
});
590+
591+
powerShellExePath = fixedPath;
592+
}
593+
}
594+
545595
return powerShellExePath.length > 0
546596
? this.resolvePowerShellPath(powerShellExePath)
547-
: this.getDefaultPowerShellPath(this.sessionSettings.useX86Host);
597+
: getDefaultPowerShellPath(this.platformDetails, this.sessionSettings.useX86Host);
548598
}
549599

550600
private changePowerShellExePath(exePath: string) {
@@ -554,78 +604,6 @@ export class SessionManager implements Middleware {
554604
.then(() => this.restartSession());
555605
}
556606

557-
private getPowerShellExeItems(): PowerShellExeDetails[] {
558-
559-
var paths: PowerShellExeDetails[] = [];
560-
561-
if (this.isWindowsOS) {
562-
const is64Bit = process.env.hasOwnProperty('PROCESSOR_ARCHITEW6432');
563-
const rootInstallPath = (is64Bit ? process.env.ProgramW6432 : process.env.ProgramFiles) + '\\PowerShell';
564-
565-
if (fs.existsSync(rootInstallPath)) {
566-
var psCorePaths =
567-
fs.readdirSync(rootInstallPath)
568-
.map(item => path.join(rootInstallPath, item))
569-
.filter(item => fs.lstatSync(item).isDirectory())
570-
.map(item => {
571-
return {
572-
versionName: `PowerShell Core ${path.parse(item).base}`,
573-
exePath: path.join(item, "powershell.exe")
574-
};
575-
});
576-
577-
if (psCorePaths) {
578-
paths = paths.concat(psCorePaths);
579-
}
580-
}
581-
582-
if (is64Bit) {
583-
paths.push({
584-
versionName: "Windows PowerShell (x64)",
585-
exePath: process.env.windir + '\\Sysnative\\WindowsPowerShell\\v1.0\\powershell.exe'
586-
})
587-
}
588-
589-
paths.push({
590-
versionName: "Windows PowerShell (x86)",
591-
exePath: process.env.windir + '\\System32\\WindowsPowerShell\\v1.0\\powershell.exe'
592-
})
593-
}
594-
else {
595-
paths.push({
596-
versionName: "PowerShell Core",
597-
exePath:
598-
os.platform() === "darwin"
599-
? "/usr/local/bin/powershell"
600-
: "/usr/bin/powershell"
601-
});
602-
}
603-
604-
return paths;
605-
}
606-
607-
private getDefaultPowerShellPath(use32Bit: boolean): string | null {
608-
609-
// Find the path to powershell.exe based on the current platform
610-
// and the user's desire to run the x86 version of PowerShell
611-
var powerShellExePath = undefined;
612-
613-
if (this.isWindowsOS) {
614-
powerShellExePath =
615-
use32Bit || !process.env.hasOwnProperty('PROCESSOR_ARCHITEW6432')
616-
? process.env.windir + '\\System32\\WindowsPowerShell\\v1.0\\powershell.exe'
617-
: process.env.windir + '\\Sysnative\\WindowsPowerShell\\v1.0\\powershell.exe';
618-
}
619-
else if (os.platform() == "darwin") {
620-
powerShellExePath = "/usr/local/bin/powershell";
621-
}
622-
else {
623-
powerShellExePath = "/usr/bin/powershell";
624-
}
625-
626-
return this.resolvePowerShellPath(powerShellExePath);
627-
}
628-
629607
private resolvePowerShellPath(powerShellExePath: string): string {
630608
var resolvedPath = path.resolve(__dirname, powerShellExePath);
631609

@@ -679,7 +657,7 @@ export class SessionManager implements Middleware {
679657

680658
var currentExePath = this.powerShellExePath.toLowerCase();
681659
var powerShellItems =
682-
this.getPowerShellExeItems()
660+
getPowerShellExeItems(this.platformDetails)
683661
.filter(item => item.exePath.toLowerCase() !== currentExePath)
684662
.map(item => {
685663
return new SessionMenuItem(
@@ -748,11 +726,6 @@ export class SessionManager implements Middleware {
748726
}
749727
}
750728

751-
interface PowerShellExeDetails {
752-
versionName: string;
753-
exePath: string;
754-
}
755-
756729
class SessionMenuItem implements vscode.QuickPickItem {
757730
public description: string;
758731

0 commit comments

Comments
 (0)