-
Notifications
You must be signed in to change notification settings - Fork 432
Expand file tree
/
Copy pathwindows.ts
More file actions
226 lines (205 loc) · 5.89 KB
/
windows.ts
File metadata and controls
226 lines (205 loc) · 5.89 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
/*
* windows.ts
*
* Copyright (C) 2020-2022 Posit Software, PBC
*/
import { existsSync, safeRemoveSync } from "../deno_ral/fs.ts";
import { join } from "../deno_ral/path.ts";
import { quartoCacheDir } from "./appdirs.ts";
import { removeIfExists } from "./path.ts";
import { execProcess } from "./process.ts";
import { ProcessResult } from "./process-types.ts";
import {
kHKeyCurrentUser,
kHKeyLocalMachine,
registryReadString,
} from "./registry.ts";
import { isWindows } from "../deno_ral/platform.ts";
export async function readRegistryKey(
registryPath: string,
keyname: string | "(Default)",
) {
const defaultKeyname = keyname === "(Default)";
// Build the the reg command
const args = ["query", registryPath];
if (defaultKeyname) {
args.push("/ve");
} else {
args.push("/v");
args.push(keyname);
}
// Run the command handling quotes on Windows
const safeArgs = requireQuoting(args);
const result = await safeWindowsExec(
"reg.exe",
safeArgs.args,
(cmd: string[]) => {
return execProcess({
cmd: cmd[0],
args: cmd.slice(1),
stdout: "piped",
stderr: "piped",
});
},
);
// Check the result
if (result.success) {
// Parse the output to read the value
const output = result.stdout;
const regexStr = (defaultKeyname ? "" : ` *${keyname}`) +
` *(?:REG_SZ|REG_MULTI_SZ|REG_EXPAND_SZ|REG_DWORD|REG_BINARY|REG_NONE) *(.*)$`;
const match = output?.match(RegExp(regexStr, "m"));
if (match) {
return match[1];
} else {
return undefined;
}
} else {
return undefined;
}
}
const tokenPath = join(quartoCacheDir(), "codepage");
export async function cacheCodePage() {
if (!existsSync(tokenPath)) {
const value = await registryReadString(
[kHKeyLocalMachine, kHKeyCurrentUser],
"SYSTEM\\CurrentControlSet\\Control\\Nls\\CodePage",
"ACP",
);
if (value) {
Deno.writeTextFileSync(tokenPath, value);
}
}
}
export function clearCodePageCache() {
if (existsSync(tokenPath)) {
safeRemoveSync(tokenPath);
}
}
function readCodePageCache() {
const codepage = Deno.readTextFileSync(tokenPath);
if (codepage) {
return codepage;
} else {
return undefined;
}
}
export function readCodePage() {
const readCache = () => {
const codepage = readCodePageCache();
if (codepage) {
return codepage;
}
};
try {
// Read the cache and return it
return readCache();
} catch {
try {
// Retry creating the cache
cacheCodePage();
return readCache();
} catch {
// We couldn't read the cache at all, so just give up
return undefined;
}
}
}
// On Windows, determine and apply double quoting on args that needs it
// Do nothing on other OS.
export function requireQuoting(
args: string[],
) {
// TODO - we probably shouldn't be calling this if we're not on windows
let requireQuoting = false;
if (isWindows) {
// On Windows, we need to check if arguments may need quoting to avoid issue with Deno.Run()
// https://github.com/quarto-dev/quarto-cli/issues/336
const shellCharReg = new RegExp("[ <>()|\\:&;#?*']");
args = args.map((a) => {
if (shellCharReg.test(a)) {
requireQuoting = true;
return `"${a}"`;
} else {
return a;
}
});
}
return {
status: requireQuoting,
args: args,
};
}
// Execute a program on Windows by writing command line
// to a tempfile and execute the file with CMD
export async function safeWindowsExec(
program: string,
args: string[],
fnExec: (cmdExec: string[]) => Promise<ProcessResult>,
) {
const tempFile = Deno.makeTempFileSync(
{ prefix: "quarto-safe-exec", suffix: ".bat" },
);
try {
Deno.writeTextFileSync(
tempFile,
["@echo off", [program, ...args].join(" ")].join("\n"),
);
return await fnExec(["cmd", "/c", tempFile]);
} finally {
removeIfExists(tempFile);
}
}
// Detect Windows ARM hardware using IsWow64Process2 API
// Returns true if running on ARM64 hardware (even from x64 Deno under emulation)
//
// Background: Deno.build.arch reports the architecture Deno was compiled for,
// not the actual hardware architecture. When x64 Deno runs on ARM64 under
// WOW64 emulation, Deno.build.arch still reports "x86_64".
//
// Solution: Use Windows IsWow64Process2 API which returns the native machine
// architecture regardless of emulation. This is a standard Windows API function
// available since Windows 10 (kernel32.dll is always present on Windows).
//
// Reference: Validated approach from https://github.com/cderv/quarto-windows-arm
// See: https://learn.microsoft.com/en-us/windows/win32/api/wow64apiset/nf-wow64apiset-iswow64process2
export function isWindowsArm(): boolean {
if (!isWindows) {
return false;
}
try {
// Load kernel32.dll
const kernel32 = Deno.dlopen("kernel32.dll", {
IsWow64Process2: {
parameters: ["pointer", "pointer", "pointer"],
result: "i32",
},
GetCurrentProcess: {
parameters: [],
result: "pointer",
},
});
// Get current process handle
const hProcess = kernel32.symbols.GetCurrentProcess();
// Prepare output parameters - allocate buffer for USHORT (2 bytes each)
const processMachineBuffer = new Uint16Array(1);
const nativeMachineBuffer = new Uint16Array(1);
// Call IsWow64Process2 with pointers to buffers
const result = kernel32.symbols.IsWow64Process2(
hProcess,
Deno.UnsafePointer.of(processMachineBuffer),
Deno.UnsafePointer.of(nativeMachineBuffer),
);
kernel32.close();
if (result === 0) {
// Function failed
return false;
}
// IMAGE_FILE_MACHINE_ARM64 = 0xAA64 = 43620
const IMAGE_FILE_MACHINE_ARM64 = 0xAA64;
return nativeMachineBuffer[0] === IMAGE_FILE_MACHINE_ARM64;
} catch {
// IsWow64Process2 not available (Windows < 10) or other error
return false;
}
}