Skip to content

Commit 746fda3

Browse files
authored
add wslFeatureInstalled context key (microsoft#160424)
* add wslFeatureInstalled context key * only register the contribution when on windows * add doc * wait for LifecyclePhase#Eventually
1 parent 0dce868 commit 746fda3

File tree

5 files changed

+125
-2
lines changed

5 files changed

+125
-2
lines changed

src/vs/platform/native/common/native.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,8 @@ export interface ICommonNativeHostService {
121121

122122
getOSColorScheme(): Promise<IColorScheme>;
123123

124+
hasWSLFeatureInstalled(): Promise<boolean>;
125+
124126
// Process
125127
killProcess(pid: number, code: string): Promise<void>;
126128

src/vs/platform/native/electron-main/nativeHostMainService.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ import { IWindowsMainService, OpenContext } from 'vs/platform/windows/electron-m
4040
import { isWorkspaceIdentifier, toWorkspaceIdentifier } from 'vs/platform/workspace/common/workspace';
4141
import { IWorkspacesManagementMainService } from 'vs/platform/workspaces/electron-main/workspacesManagementMainService';
4242
import { VSBuffer } from 'vs/base/common/buffer';
43+
import { hasWSLFeatureInstalled } from 'vs/platform/remote/node/wsl';
4344

4445
export interface INativeHostMainService extends AddFirstParameterToFunctions<ICommonNativeHostService, Promise<unknown> /* only methods, not events */, number | undefined /* window ID */> { }
4546

@@ -568,6 +569,12 @@ export class NativeHostMainService extends Disposable implements INativeHostMain
568569
}
569570

570571

572+
// WSL
573+
public async hasWSLFeatureInstalled(): Promise<boolean> {
574+
return isWindows && hasWSLFeatureInstalled();
575+
}
576+
577+
571578
//#endregion
572579

573580

src/vs/platform/remote/node/wsl.ts

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
/*---------------------------------------------------------------------------------------------
2+
* Copyright (c) Microsoft Corporation. All rights reserved.
3+
* Licensed under the MIT License. See License.txt in the project root for license information.
4+
*--------------------------------------------------------------------------------------------*/
5+
6+
import * as os from 'os';
7+
import * as cp from 'child_process';
8+
import { promises as fs } from 'fs';
9+
import path = require('path');
10+
11+
let hasWSLFeaturePromise: Promise<boolean> | undefined;
12+
13+
export async function hasWSLFeatureInstalled(refresh = false): Promise<boolean> {
14+
if (hasWSLFeaturePromise === undefined || refresh) {
15+
hasWSLFeaturePromise = testWSLFeatureInstalled();
16+
}
17+
return hasWSLFeaturePromise;
18+
}
19+
20+
async function testWSLFeatureInstalled(): Promise<boolean> {
21+
const windowsBuildNumber = getWindowsBuildNumber();
22+
if (windowsBuildNumber === undefined) {
23+
return false;
24+
}
25+
if (windowsBuildNumber >= 22000) {
26+
const wslExePath = getWSLExecutablePath();
27+
if (wslExePath) {
28+
return new Promise<boolean>(s => {
29+
cp.execFile(wslExePath, ['--status'], err => s(!err));
30+
});
31+
}
32+
} else {
33+
const dllPath = getLxssManagerDllPath();
34+
if (dllPath) {
35+
try {
36+
if ((await fs.stat(dllPath)).isFile()) {
37+
return true;
38+
}
39+
} catch (e) {
40+
}
41+
}
42+
}
43+
return false;
44+
}
45+
46+
function getWindowsBuildNumber(): number | undefined {
47+
const osVersion = (/(\d+)\.(\d+)\.(\d+)/g).exec(os.release());
48+
if (osVersion) {
49+
return parseInt(osVersion[3]);
50+
}
51+
return undefined;
52+
}
53+
54+
function getSystem32Path(subPath: string): string | undefined {
55+
const systemRoot = process.env['SystemRoot'];
56+
if (systemRoot) {
57+
const is32ProcessOn64Windows = process.env.hasOwnProperty('PROCESSOR_ARCHITEW6432');
58+
return path.join(systemRoot, is32ProcessOn64Windows ? 'Sysnative' : 'System32', subPath);
59+
}
60+
return undefined;
61+
}
62+
63+
function getWSLExecutablePath(): string | undefined {
64+
return getSystem32Path('wsl.exe');
65+
}
66+
67+
/**
68+
* In builds < 22000 this dll inidcates that WSL is installed
69+
*/
70+
function getLxssManagerDllPath(): string | undefined {
71+
return getSystem32Path('lxss\\LxssManager.dll');
72+
}

src/vs/workbench/contrib/remote/electron-sandbox/remote.contribution.ts

Lines changed: 43 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,11 @@ import * as nls from 'vs/nls';
77
import { Registry } from 'vs/platform/registry/common/platform';
88
import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService';
99
import { Disposable } from 'vs/base/common/lifecycle';
10-
import { isMacintosh } from 'vs/base/common/platform';
10+
import { isMacintosh, isWindows } from 'vs/base/common/platform';
1111
import { KeyMod, KeyChord, KeyCode } from 'vs/base/common/keyCodes';
1212
import { KeybindingsRegistry, KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry';
1313
import { IWorkbenchContribution, IWorkbenchContributionsRegistry, Extensions as WorkbenchContributionsExtensions } from 'vs/workbench/common/contributions';
14-
import { LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle';
14+
import { ILifecycleService, LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle';
1515
import { ILabelService } from 'vs/platform/label/common/label';
1616
import { ICommandService } from 'vs/platform/commands/common/commands';
1717
import { Schemas } from 'vs/base/common/network';
@@ -27,6 +27,9 @@ import { OpenLocalFileFolderCommand, OpenLocalFileCommand, OpenLocalFolderComman
2727
import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace';
2828
import { TELEMETRY_SETTING_ID } from 'vs/platform/telemetry/common/telemetry';
2929
import { getTelemetryLevel } from 'vs/platform/telemetry/common/telemetryUtils';
30+
import { IContextKeyService, RawContextKey } from 'vs/platform/contextkey/common/contextkey';
31+
import { INativeHostService } from 'vs/platform/native/electron-sandbox/native';
32+
import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage';
3033

3134
class RemoteAgentDiagnosticListener implements IWorkbenchContribution {
3235
constructor(
@@ -131,11 +134,49 @@ class RemoteEmptyWorkbenchPresentation extends Disposable implements IWorkbenchC
131134
}
132135
}
133136

137+
/**
138+
* Sets the 'wslFeatureInstalled' context key if the WSL feature is or was installed on this machine.
139+
*/
140+
class WSLContextKeyInitializer extends Disposable implements IWorkbenchContribution {
141+
142+
constructor(
143+
@IContextKeyService contextKeyService: IContextKeyService,
144+
@INativeHostService nativeHostService: INativeHostService,
145+
@IStorageService storageService: IStorageService,
146+
@ILifecycleService lifecycleService: ILifecycleService
147+
) {
148+
super();
149+
150+
const contextKeyId = 'wslFeatureInstalled';
151+
const storageKey = 'remote.wslFeatureInstalled';
152+
153+
const defaultValue = storageService.getBoolean(storageKey, StorageScope.APPLICATION, undefined);
154+
155+
const hasWSLFeatureContext = new RawContextKey<boolean>(contextKeyId, !!defaultValue, nls.localize('wslFeatureInstalled', "Whether the platform has the WSL feature installed"));
156+
const contextKey = hasWSLFeatureContext.bindTo(contextKeyService);
157+
158+
if (defaultValue === undefined) {
159+
lifecycleService.when(LifecyclePhase.Eventually).then(async () => {
160+
nativeHostService.hasWSLFeatureInstalled().then(res => {
161+
if (res) {
162+
contextKey.set(true);
163+
// once detected, set to true
164+
storageService.store(storageKey, true, StorageScope.APPLICATION, StorageTarget.MACHINE);
165+
}
166+
});
167+
});
168+
}
169+
}
170+
}
171+
134172
const workbenchContributionsRegistry = Registry.as<IWorkbenchContributionsRegistry>(WorkbenchContributionsExtensions.Workbench);
135173
workbenchContributionsRegistry.registerWorkbenchContribution(RemoteAgentDiagnosticListener, 'RemoteAgentDiagnosticListener', LifecyclePhase.Eventually);
136174
workbenchContributionsRegistry.registerWorkbenchContribution(RemoteExtensionHostEnvironmentUpdater, 'RemoteExtensionHostEnvironmentUpdater', LifecyclePhase.Eventually);
137175
workbenchContributionsRegistry.registerWorkbenchContribution(RemoteTelemetryEnablementUpdater, 'RemoteTelemetryEnablementUpdater', LifecyclePhase.Ready);
138176
workbenchContributionsRegistry.registerWorkbenchContribution(RemoteEmptyWorkbenchPresentation, 'RemoteEmptyWorkbenchPresentation', LifecyclePhase.Ready);
177+
if (isWindows) {
178+
workbenchContributionsRegistry.registerWorkbenchContribution(WSLContextKeyInitializer, 'WSLContextKeyInitializer', LifecyclePhase.Ready);
179+
}
139180

140181
Registry.as<IConfigurationRegistry>(ConfigurationExtensions.Configuration)
141182
.registerConfiguration({

src/vs/workbench/test/electron-browser/workbenchTestServices.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -240,6 +240,7 @@ export class TestNativeHostService implements INativeHostService {
240240
async getOSStatistics(): Promise<IOSStatistics> { return Object.create(null); }
241241
async getOSVirtualMachineHint(): Promise<number> { return 0; }
242242
async getOSColorScheme(): Promise<IColorScheme> { return { dark: true, highContrast: false }; }
243+
async hasWSLFeatureInstalled(): Promise<boolean> { return false; }
243244
async killProcess(): Promise<void> { }
244245
async setDocumentEdited(edited: boolean): Promise<void> { }
245246
async openExternal(url: string): Promise<boolean> { return false; }

0 commit comments

Comments
 (0)