Skip to content

Commit a4d1667

Browse files
authored
Add API to get data from registry (#14297)
* Add API to get data from registry * Address Comments * more tweaks
1 parent 8b8fbbf commit a4d1667

File tree

2 files changed

+125
-0
lines changed

2 files changed

+125
-0
lines changed

src/client/pythonEnvironments/common/windowsUtils.ts

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,14 @@
22
// Licensed under the MIT License.
33

44
import * as path from 'path';
5+
import {
6+
Options, REG_SZ, Registry, RegistryItem,
7+
} from 'winreg';
8+
import { traceVerbose } from '../../common/logger';
9+
import { createDeferred } from '../../common/utils/async';
10+
11+
// tslint:disable-next-line: no-single-line-block-comment
12+
/* eslint-disable global-require */
513

614
/**
715
* Checks if a given path ends with python*.exe
@@ -20,3 +28,99 @@ export function isWindowsPythonExe(interpreterPath:string): boolean {
2028

2129
return windowsPythonExes.test(path.basename(interpreterPath));
2230
}
31+
32+
export async function readRegistryValues(options: Options): Promise<RegistryItem[]> {
33+
// tslint:disable-next-line:no-require-imports
34+
const WinReg = require('winreg');
35+
const regKey = new WinReg(options);
36+
const deferred = createDeferred<RegistryItem[]>();
37+
regKey.values((err:Error, res:RegistryItem[]) => {
38+
if (err) {
39+
deferred.reject(err);
40+
}
41+
deferred.resolve(res);
42+
});
43+
return deferred.promise;
44+
}
45+
46+
export async function readRegistryKeys(options: Options): Promise<Registry[]> {
47+
// tslint:disable-next-line:no-require-imports
48+
const WinReg = require('winreg');
49+
const regKey = new WinReg(options);
50+
const deferred = createDeferred<Registry[]>();
51+
regKey.keys((err:Error, res:Registry[]) => {
52+
if (err) {
53+
deferred.reject(err);
54+
}
55+
deferred.resolve(res);
56+
});
57+
return deferred.promise;
58+
}
59+
60+
export interface IRegistryInterpreterData{
61+
interpreterPath: string;
62+
versionStr?: string;
63+
sysVersionStr?:string;
64+
bitnessStr?: string;
65+
displayName?: string;
66+
distroOrgName?: string;
67+
}
68+
69+
async function getInterpreterDataFromKey(
70+
{ arch, hive, key }:Registry,
71+
distroOrgName:string,
72+
): Promise<IRegistryInterpreterData | undefined> {
73+
const result:IRegistryInterpreterData = {
74+
interpreterPath: '',
75+
distroOrgName,
76+
};
77+
78+
const values:RegistryItem[] = await readRegistryValues({ arch, hive, key });
79+
for (const value of values) {
80+
switch (value.name) {
81+
case 'SysArchitecture':
82+
result.bitnessStr = value.value;
83+
break;
84+
case 'SysVersion':
85+
result.sysVersionStr = value.value;
86+
break;
87+
case 'Version':
88+
result.versionStr = value.value;
89+
break;
90+
case 'DisplayName':
91+
result.displayName = value.value;
92+
break;
93+
default:
94+
break;
95+
}
96+
}
97+
98+
const subKeys:Registry[] = await readRegistryKeys({ arch, hive, key });
99+
const subKey = subKeys.map((s) => s.key).find((s) => s.endsWith('InstallPath'));
100+
if (subKey) {
101+
const subKeyValues:RegistryItem[] = await readRegistryValues({ arch, hive, key: subKey });
102+
const value = subKeyValues.find((v) => v.name === 'ExecutablePath');
103+
if (value) {
104+
result.interpreterPath = value.value;
105+
if (value.type !== REG_SZ) {
106+
traceVerbose(`Registry interpreter path type [${value.type}]: ${value.value}`);
107+
}
108+
}
109+
}
110+
111+
if (result.interpreterPath.length > 0) {
112+
return result;
113+
}
114+
return undefined;
115+
}
116+
117+
export async function getInterpreterDataFromRegistry(
118+
arch:string,
119+
hive:string,
120+
key:string,
121+
): Promise<IRegistryInterpreterData[]> {
122+
const subKeys = await readRegistryKeys({ arch, hive, key });
123+
const distroOrgName = key.substr(key.lastIndexOf('\\') + 1);
124+
const allData = await Promise.all(subKeys.map((subKey) => getInterpreterDataFromKey(subKey, distroOrgName)));
125+
return (allData.filter((data) => data !== undefined) || []) as IRegistryInterpreterData[];
126+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
// Copyright (c) Microsoft Corporation. All rights reserved.
2+
// Licensed under the MIT License.
3+
4+
import { uniqBy } from 'lodash';
5+
import { HKCU, HKLM } from 'winreg';
6+
import { getInterpreterDataFromRegistry, IRegistryInterpreterData, readRegistryKeys } from '../../../common/windowsUtils';
7+
8+
export async function getRegistryInterpreters() : Promise<IRegistryInterpreterData[]> {
9+
let registryData:IRegistryInterpreterData[] = [];
10+
11+
for (const arch of ['x64', 'x86']) {
12+
for (const hive of [HKLM, HKCU]) {
13+
const keys = (await readRegistryKeys({ arch, hive, key: '\\SOFTWARE\\Python' })).map((k) => k.key);
14+
for (const key of keys) {
15+
registryData = registryData.concat(await getInterpreterDataFromRegistry(arch, hive, key));
16+
}
17+
}
18+
}
19+
20+
return uniqBy(registryData, (r:IRegistryInterpreterData) => r.interpreterPath);
21+
}

0 commit comments

Comments
 (0)