SynapseCTRL 0.2.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- synapsectrl-0.2.1/.gitignore +16 -0
- synapsectrl-0.2.1/Install-SynapseInspectHook.ps1 +698 -0
- synapsectrl-0.2.1/LICENSE +201 -0
- synapsectrl-0.2.1/NOTICE +5 -0
- synapsectrl-0.2.1/PKG-INFO +327 -0
- synapsectrl-0.2.1/README.md +100 -0
- synapsectrl-0.2.1/codex-stuff/ASTRA_SPEC.md +360 -0
- synapsectrl-0.2.1/codex-stuff/KNOWN_FINDINGS.json +72 -0
- synapsectrl-0.2.1/codex-stuff/PRODUCT_FINDINGS.md +59 -0
- synapsectrl-0.2.1/codex-stuff/PROJECT_CLEANUP.md +44 -0
- synapsectrl-0.2.1/codex-stuff/README.md +32 -0
- synapsectrl-0.2.1/codex-stuff/README_FOR_ASTRA.md +18 -0
- synapsectrl-0.2.1/codex-stuff/tools/README.md +62 -0
- synapsectrl-0.2.1/codex-stuff/tools/_synapse.py +656 -0
- synapsectrl-0.2.1/codex-stuff/tools/capture_stdio.ps1 +43 -0
- synapsectrl-0.2.1/codex-stuff/tools/devices.py +38 -0
- synapsectrl-0.2.1/codex-stuff/tools/diagnostic_bundle.py +73 -0
- synapsectrl-0.2.1/codex-stuff/tools/eval.py +41 -0
- synapsectrl-0.2.1/codex-stuff/tools/hook_status.ps1 +36 -0
- synapsectrl-0.2.1/codex-stuff/tools/probe.py +23 -0
- synapsectrl-0.2.1/codex-stuff/tools/renderers.py +32 -0
- synapsectrl-0.2.1/codex-stuff/tools/source_search.py +54 -0
- synapsectrl-0.2.1/codex-stuff/tools/storage.py +58 -0
- synapsectrl-0.2.1/codex-stuff/tools/switch_profile.py +73 -0
- synapsectrl-0.2.1/docs/API.md +193 -0
- synapsectrl-0.2.1/docs/Architecture.md +81 -0
- synapsectrl-0.2.1/docs/Attribution.md +49 -0
- synapsectrl-0.2.1/docs/CLI.md +176 -0
- synapsectrl-0.2.1/docs/Development.md +83 -0
- synapsectrl-0.2.1/docs/Getting-Started.md +165 -0
- synapsectrl-0.2.1/docs/Publishing.md +104 -0
- synapsectrl-0.2.1/docs/Python-SDK.md +88 -0
- synapsectrl-0.2.1/docs/README.md +38 -0
- synapsectrl-0.2.1/docs/Setup.md +128 -0
- synapsectrl-0.2.1/docs/Troubleshooting.md +104 -0
- synapsectrl-0.2.1/examples/switch_profile.py +33 -0
- synapsectrl-0.2.1/pyproject.toml +63 -0
- synapsectrl-0.2.1/src/synapsectrl/__init__.py +14 -0
- synapsectrl-0.2.1/src/synapsectrl/__main__.py +7 -0
- synapsectrl-0.2.1/src/synapsectrl/_inspector.py +267 -0
- synapsectrl-0.2.1/src/synapsectrl/bootstrap.py +194 -0
- synapsectrl-0.2.1/src/synapsectrl/cli.py +346 -0
- synapsectrl-0.2.1/src/synapsectrl/client.py +254 -0
- synapsectrl-0.2.1/src/synapsectrl/discovery.py +418 -0
- synapsectrl-0.2.1/src/synapsectrl/errors.py +15 -0
- synapsectrl-0.2.1/src/synapsectrl/hook.py +90 -0
- synapsectrl-0.2.1/src/synapsectrl/models.py +85 -0
- synapsectrl-0.2.1/src/synapsectrl/py.typed +0 -0
- synapsectrl-0.2.1/synapse_ctrl.py +827 -0
- synapsectrl-0.2.1/tests/live_synapse.py +102 -0
- synapsectrl-0.2.1/tests/test_bootstrap.py +157 -0
- synapsectrl-0.2.1/tests/test_cli.py +226 -0
- synapsectrl-0.2.1/tests/test_client.py +215 -0
- synapsectrl-0.2.1/tests/test_discovery.py +282 -0
- synapsectrl-0.2.1/tests/test_hook_cli.py +57 -0
- synapsectrl-0.2.1/tests/test_inspector.py +135 -0
- synapsectrl-0.2.1/tests/test_installer.ps1 +216 -0
- synapsectrl-0.2.1/tests/test_integration_review.py +147 -0
|
@@ -0,0 +1,698 @@
|
|
|
1
|
+
param(
|
|
2
|
+
[switch]$Uninstall,
|
|
3
|
+
[switch]$NoPause
|
|
4
|
+
)
|
|
5
|
+
|
|
6
|
+
$ErrorActionPreference = "Stop"
|
|
7
|
+
|
|
8
|
+
function Test-IsAdministrator {
|
|
9
|
+
$identity = [Security.Principal.WindowsIdentity]::GetCurrent()
|
|
10
|
+
$principal = [Security.Principal.WindowsPrincipal]::new($identity)
|
|
11
|
+
|
|
12
|
+
return $principal.IsInRole(
|
|
13
|
+
[Security.Principal.WindowsBuiltInRole]::Administrator
|
|
14
|
+
)
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
if (-not (Test-IsAdministrator)) {
|
|
18
|
+
$elevatedArgs = @(
|
|
19
|
+
"-NoProfile",
|
|
20
|
+
"-ExecutionPolicy", "Bypass",
|
|
21
|
+
"-File", "`"$PSCommandPath`"",
|
|
22
|
+
# The parent handles the optional pause; the hidden child must never wait.
|
|
23
|
+
"-NoPause"
|
|
24
|
+
)
|
|
25
|
+
|
|
26
|
+
if ($Uninstall) {
|
|
27
|
+
$elevatedArgs += "-Uninstall"
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
try {
|
|
31
|
+
$powerShellExe = Join-Path $env:SystemRoot "System32\WindowsPowerShell\v1.0\powershell.exe"
|
|
32
|
+
if ([Environment]::Is64BitOperatingSystem -and -not [Environment]::Is64BitProcess) {
|
|
33
|
+
$powerShellExe = Join-Path $env:SystemRoot "Sysnative\WindowsPowerShell\v1.0\powershell.exe"
|
|
34
|
+
}
|
|
35
|
+
$child = Start-Process -FilePath $powerShellExe -Verb RunAs `
|
|
36
|
+
-WindowStyle Hidden -ArgumentList $elevatedArgs -Wait -PassThru
|
|
37
|
+
$resultCode = $child.ExitCode
|
|
38
|
+
if ($resultCode -ne 0) {
|
|
39
|
+
Write-Error "Elevated SynapseCTRL installer failed (exit $resultCode). Run this script from an Administrator PowerShell window to see the detailed error." -ErrorAction Continue
|
|
40
|
+
} else {
|
|
41
|
+
Write-Host "SynapseCTRL hook operation completed. Fully exit and reopen Synapse to apply changes."
|
|
42
|
+
}
|
|
43
|
+
} catch {
|
|
44
|
+
Write-Error "Could not complete elevated installer: $_" -ErrorAction Continue
|
|
45
|
+
$resultCode = 1
|
|
46
|
+
}
|
|
47
|
+
if (-not $NoPause) { Read-Host "Press Enter to continue..." | Out-Null }
|
|
48
|
+
exit $resultCode
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function Invoke-SynapseHook {
|
|
52
|
+
|
|
53
|
+
$StableExe = Join-Path `
|
|
54
|
+
$env:ProgramFiles `
|
|
55
|
+
"Razer\RazerAppEngine\RazerAppEngine.exe"
|
|
56
|
+
|
|
57
|
+
$InstallDir = Join-Path `
|
|
58
|
+
$env:ProgramFiles `
|
|
59
|
+
"SynapseCTRL"
|
|
60
|
+
|
|
61
|
+
$ShimExe = Join-Path `
|
|
62
|
+
$InstallDir `
|
|
63
|
+
"RazerInspectShim.exe"
|
|
64
|
+
|
|
65
|
+
$OldShimPs1 = Join-Path `
|
|
66
|
+
$InstallDir `
|
|
67
|
+
"RazerInspectShim.ps1"
|
|
68
|
+
|
|
69
|
+
$IFEOBase = (
|
|
70
|
+
"HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\" +
|
|
71
|
+
"Image File Execution Options\RazerAppEngine.exe"
|
|
72
|
+
)
|
|
73
|
+
|
|
74
|
+
$FilterKey = Join-Path `
|
|
75
|
+
$IFEOBase `
|
|
76
|
+
"SynapseCTRL"
|
|
77
|
+
|
|
78
|
+
$debuggerCommand = "`"$ShimExe`""
|
|
79
|
+
|
|
80
|
+
# Check ownership and conflicts before compiling, replacing files, or changing IFEO.
|
|
81
|
+
$baseProperties = if (Test-Path -LiteralPath $IFEOBase) {
|
|
82
|
+
Get-ItemProperty -LiteralPath $IFEOBase
|
|
83
|
+
} else { $null }
|
|
84
|
+
$filterProperties = if (Test-Path -LiteralPath $FilterKey) {
|
|
85
|
+
Get-ItemProperty -LiteralPath $FilterKey
|
|
86
|
+
} else { $null }
|
|
87
|
+
if ($filterProperties -and (
|
|
88
|
+
$filterProperties.FilterFullPath -ine $StableExe -or
|
|
89
|
+
$filterProperties.Debugger -ine $debuggerCommand
|
|
90
|
+
)) {
|
|
91
|
+
throw "The SynapseCTRL IFEO filter has unexpected values. No changes were made; inspect $FilterKey before repairing it."
|
|
92
|
+
}
|
|
93
|
+
if (-not $Uninstall) {
|
|
94
|
+
if (-not (Test-Path -LiteralPath $StableExe -PathType Leaf)) {
|
|
95
|
+
throw "Razer launcher not found: $StableExe"
|
|
96
|
+
}
|
|
97
|
+
if ($baseProperties.Debugger) {
|
|
98
|
+
throw "A non-filtered IFEO Debugger already exists for RazerAppEngine.exe. No changes were made: $($baseProperties.Debugger)"
|
|
99
|
+
}
|
|
100
|
+
if (Test-Path -LiteralPath $IFEOBase) {
|
|
101
|
+
foreach ($otherFilter in Get-ChildItem -LiteralPath $IFEOBase) {
|
|
102
|
+
if ($otherFilter.PSChildName -ieq 'SynapseCTRL') { continue }
|
|
103
|
+
$otherProperties = Get-ItemProperty -LiteralPath $otherFilter.PSPath
|
|
104
|
+
if ($otherProperties.FilterFullPath -ieq $StableExe) {
|
|
105
|
+
throw "Another IFEO filter already targets the stable Razer launcher: $($otherFilter.PSChildName). No changes were made."
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
# Every removable file is a fixed immediate child of this exact install directory.
|
|
112
|
+
# Refuse redirected paths even when this script is invoked with elevation.
|
|
113
|
+
$expectedInstallDir = [IO.Path]::GetFullPath((Join-Path $env:ProgramFiles 'SynapseCTRL'))
|
|
114
|
+
if ([IO.Path]::GetFullPath($InstallDir) -ine $expectedInstallDir) {
|
|
115
|
+
throw "Unexpected installation directory: $InstallDir"
|
|
116
|
+
}
|
|
117
|
+
foreach ($ownedPath in @($InstallDir, $ShimExe, $OldShimPs1)) {
|
|
118
|
+
if (Test-Path -LiteralPath $ownedPath) {
|
|
119
|
+
if ((Get-Item -LiteralPath $ownedPath -Force).Attributes -band [IO.FileAttributes]::ReparsePoint) {
|
|
120
|
+
throw "Refusing redirected installation path: $ownedPath"
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
if ($Uninstall) {
|
|
126
|
+
if ($filterProperties) {
|
|
127
|
+
# Preserve any unrelated values or nested filters in the same key.
|
|
128
|
+
foreach ($valueName in @('Debugger', 'FilterFullPath', 'HookVersion', 'PreviousUseFilter', 'UseFilterWasPresent')) {
|
|
129
|
+
Remove-ItemProperty -LiteralPath $FilterKey -Name $valueName -ErrorAction SilentlyContinue
|
|
130
|
+
}
|
|
131
|
+
$remainingKey = Get-Item -LiteralPath $FilterKey
|
|
132
|
+
if ($remainingKey.ValueCount -eq 0 -and $remainingKey.SubKeyCount -eq 0) {
|
|
133
|
+
Remove-Item -LiteralPath $FilterKey -Force
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
if (Test-Path $ShimExe) {
|
|
138
|
+
Remove-Item `
|
|
139
|
+
-LiteralPath $ShimExe `
|
|
140
|
+
-Force
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
if (Test-Path $OldShimPs1) {
|
|
144
|
+
Remove-Item `
|
|
145
|
+
-LiteralPath $OldShimPs1 `
|
|
146
|
+
-Force
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
if (Test-Path $IFEOBase) {
|
|
150
|
+
$remainingFilters = @(
|
|
151
|
+
Get-ChildItem `
|
|
152
|
+
-LiteralPath $IFEOBase
|
|
153
|
+
)
|
|
154
|
+
|
|
155
|
+
if ($remainingFilters.Count -eq 0 -and $filterProperties -and
|
|
156
|
+
$baseProperties.UseFilter -eq 1 -and -not $baseProperties.Debugger) {
|
|
157
|
+
if ($filterProperties.UseFilterWasPresent -eq 1) {
|
|
158
|
+
Set-ItemProperty -LiteralPath $IFEOBase -Name UseFilter -Value $filterProperties.PreviousUseFilter
|
|
159
|
+
} elseif ($baseProperties.UseFilter -eq 1) {
|
|
160
|
+
Remove-ItemProperty -LiteralPath $IFEOBase -Name UseFilter -ErrorAction SilentlyContinue
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
if (
|
|
166
|
+
(Test-Path $InstallDir) -and
|
|
167
|
+
@(
|
|
168
|
+
Get-ChildItem `
|
|
169
|
+
-LiteralPath $InstallDir `
|
|
170
|
+
-Force `
|
|
171
|
+
-ErrorAction SilentlyContinue
|
|
172
|
+
).Count -eq 0
|
|
173
|
+
) {
|
|
174
|
+
Remove-Item `
|
|
175
|
+
-LiteralPath $InstallDir `
|
|
176
|
+
-Force
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
Write-Host ""
|
|
180
|
+
Write-Host "SynapseCTRL inspector hook removed."
|
|
181
|
+
return
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
New-Item `
|
|
185
|
+
-ItemType Directory `
|
|
186
|
+
-Path $InstallDir `
|
|
187
|
+
-Force | Out-Null
|
|
188
|
+
|
|
189
|
+
# Build a GUI-subsystem executable.
|
|
190
|
+
# Unlike powershell.exe, this shim has NO console of its own.
|
|
191
|
+
$source = @'
|
|
192
|
+
using System;
|
|
193
|
+
using System.Collections.Generic;
|
|
194
|
+
using System.ComponentModel;
|
|
195
|
+
using System.IO;
|
|
196
|
+
using System.Runtime.InteropServices;
|
|
197
|
+
using System.Text;
|
|
198
|
+
|
|
199
|
+
internal static class RazerInspectShim
|
|
200
|
+
{
|
|
201
|
+
private const uint CREATE_NO_WINDOW = 0x08000000;
|
|
202
|
+
|
|
203
|
+
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
|
|
204
|
+
private struct STARTUPINFO
|
|
205
|
+
{
|
|
206
|
+
public int cb;
|
|
207
|
+
public string lpReserved;
|
|
208
|
+
public string lpDesktop;
|
|
209
|
+
public string lpTitle;
|
|
210
|
+
public int dwX;
|
|
211
|
+
public int dwY;
|
|
212
|
+
public int dwXSize;
|
|
213
|
+
public int dwYSize;
|
|
214
|
+
public int dwXCountChars;
|
|
215
|
+
public int dwYCountChars;
|
|
216
|
+
public int dwFillAttribute;
|
|
217
|
+
public int dwFlags;
|
|
218
|
+
public short wShowWindow;
|
|
219
|
+
public short cbReserved2;
|
|
220
|
+
public IntPtr lpReserved2;
|
|
221
|
+
public IntPtr hStdInput;
|
|
222
|
+
public IntPtr hStdOutput;
|
|
223
|
+
public IntPtr hStdError;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
[StructLayout(LayoutKind.Sequential)]
|
|
227
|
+
private struct PROCESS_INFORMATION
|
|
228
|
+
{
|
|
229
|
+
public IntPtr hProcess;
|
|
230
|
+
public IntPtr hThread;
|
|
231
|
+
public int dwProcessId;
|
|
232
|
+
public int dwThreadId;
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
[DllImport(
|
|
236
|
+
"kernel32.dll",
|
|
237
|
+
CharSet = CharSet.Unicode,
|
|
238
|
+
SetLastError = true
|
|
239
|
+
)]
|
|
240
|
+
[return: MarshalAs(UnmanagedType.Bool)]
|
|
241
|
+
private static extern bool CreateProcessW(
|
|
242
|
+
string lpApplicationName,
|
|
243
|
+
StringBuilder lpCommandLine,
|
|
244
|
+
IntPtr lpProcessAttributes,
|
|
245
|
+
IntPtr lpThreadAttributes,
|
|
246
|
+
bool bInheritHandles,
|
|
247
|
+
uint dwCreationFlags,
|
|
248
|
+
IntPtr lpEnvironment,
|
|
249
|
+
string lpCurrentDirectory,
|
|
250
|
+
ref STARTUPINFO lpStartupInfo,
|
|
251
|
+
out PROCESS_INFORMATION lpProcessInformation
|
|
252
|
+
);
|
|
253
|
+
|
|
254
|
+
[DllImport("kernel32.dll", SetLastError = true)]
|
|
255
|
+
[return: MarshalAs(UnmanagedType.Bool)]
|
|
256
|
+
private static extern bool CloseHandle(IntPtr hObject);
|
|
257
|
+
|
|
258
|
+
[STAThread]
|
|
259
|
+
private static int Main(string[] args)
|
|
260
|
+
{
|
|
261
|
+
try
|
|
262
|
+
{
|
|
263
|
+
string programFiles =
|
|
264
|
+
Environment.GetFolderPath(
|
|
265
|
+
Environment.SpecialFolder.ProgramFiles
|
|
266
|
+
);
|
|
267
|
+
|
|
268
|
+
string root = Path.Combine(
|
|
269
|
+
programFiles,
|
|
270
|
+
"Razer",
|
|
271
|
+
"RazerAppEngine"
|
|
272
|
+
);
|
|
273
|
+
|
|
274
|
+
string versionedExe = FindNewestVersionedExe(root);
|
|
275
|
+
|
|
276
|
+
if (String.IsNullOrEmpty(versionedExe))
|
|
277
|
+
{
|
|
278
|
+
LogError(
|
|
279
|
+
"Could not find a versioned RazerAppEngine.exe."
|
|
280
|
+
);
|
|
281
|
+
|
|
282
|
+
return 193;
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
List<string> forwarded = BuildArguments(args);
|
|
286
|
+
|
|
287
|
+
StringBuilder commandLine = new StringBuilder();
|
|
288
|
+
|
|
289
|
+
commandLine.Append(
|
|
290
|
+
QuoteWindowsArgument(versionedExe)
|
|
291
|
+
);
|
|
292
|
+
|
|
293
|
+
for (int i = 0; i < forwarded.Count; i++)
|
|
294
|
+
{
|
|
295
|
+
commandLine.Append(' ');
|
|
296
|
+
commandLine.Append(
|
|
297
|
+
QuoteWindowsArgument(forwarded[i])
|
|
298
|
+
);
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
STARTUPINFO startup = new STARTUPINFO();
|
|
302
|
+
startup.cb = Marshal.SizeOf(typeof(STARTUPINFO));
|
|
303
|
+
|
|
304
|
+
PROCESS_INFORMATION processInfo;
|
|
305
|
+
|
|
306
|
+
bool started = CreateProcessW(
|
|
307
|
+
versionedExe,
|
|
308
|
+
commandLine,
|
|
309
|
+
IntPtr.Zero,
|
|
310
|
+
IntPtr.Zero,
|
|
311
|
+
|
|
312
|
+
// Do not let the Razer process inherit console/stdout
|
|
313
|
+
// handles from anything that happened to launch us.
|
|
314
|
+
false,
|
|
315
|
+
|
|
316
|
+
// If RazerAppEngine is a console-subsystem executable,
|
|
317
|
+
// explicitly create it without a console.
|
|
318
|
+
CREATE_NO_WINDOW,
|
|
319
|
+
|
|
320
|
+
IntPtr.Zero,
|
|
321
|
+
Path.GetDirectoryName(versionedExe),
|
|
322
|
+
ref startup,
|
|
323
|
+
out processInfo
|
|
324
|
+
);
|
|
325
|
+
|
|
326
|
+
if (!started)
|
|
327
|
+
{
|
|
328
|
+
int error = Marshal.GetLastWin32Error();
|
|
329
|
+
|
|
330
|
+
LogError(
|
|
331
|
+
"CreateProcessW failed: " +
|
|
332
|
+
error + " (" +
|
|
333
|
+
new Win32Exception(error).Message +
|
|
334
|
+
")"
|
|
335
|
+
);
|
|
336
|
+
|
|
337
|
+
return error != 0 ? error : 194;
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
// We deliberately do not wait for Synapse.
|
|
341
|
+
// Windows does not tie a normal child process lifetime
|
|
342
|
+
// to its parent, so this shim exits immediately.
|
|
343
|
+
if (processInfo.hThread != IntPtr.Zero)
|
|
344
|
+
{
|
|
345
|
+
CloseHandle(processInfo.hThread);
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
if (processInfo.hProcess != IntPtr.Zero)
|
|
349
|
+
{
|
|
350
|
+
CloseHandle(processInfo.hProcess);
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
return 0;
|
|
354
|
+
}
|
|
355
|
+
catch (Exception ex)
|
|
356
|
+
{
|
|
357
|
+
LogError(ex.ToString());
|
|
358
|
+
return 195;
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
private static List<string> BuildArguments(string[] args)
|
|
363
|
+
{
|
|
364
|
+
List<string> forwarded = new List<string>();
|
|
365
|
+
forwarded.Add("--inspect=127.0.0.1:9229");
|
|
366
|
+
|
|
367
|
+
for (int i = 0; i < args.Length; i++)
|
|
368
|
+
{
|
|
369
|
+
string arg = args[i] ?? String.Empty;
|
|
370
|
+
// IFEO supplies the intercepted executable first; never launch it
|
|
371
|
+
// again, because it is the filtered stable path and would recurse.
|
|
372
|
+
if (i == 0 && Path.IsPathRooted(arg) && String.Equals(
|
|
373
|
+
Path.GetFileName(arg), "RazerAppEngine.exe",
|
|
374
|
+
StringComparison.OrdinalIgnoreCase))
|
|
375
|
+
{
|
|
376
|
+
continue;
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
// Node also accepts underscores in option names. Never preserve an
|
|
380
|
+
// incoming host/port, break-on-start, or inspector-disabling flag.
|
|
381
|
+
string option = arg.Split('=')[0].Replace('_', '-');
|
|
382
|
+
if (option.StartsWith("--inspect", StringComparison.OrdinalIgnoreCase) ||
|
|
383
|
+
option.StartsWith("--no-inspect", StringComparison.OrdinalIgnoreCase) ||
|
|
384
|
+
option.StartsWith("--debug", StringComparison.OrdinalIgnoreCase) ||
|
|
385
|
+
option.StartsWith("--no-debug", StringComparison.OrdinalIgnoreCase))
|
|
386
|
+
{
|
|
387
|
+
if (arg.IndexOf('=') < 0 && i + 1 < args.Length)
|
|
388
|
+
{
|
|
389
|
+
string next = args[i + 1] ?? String.Empty;
|
|
390
|
+
int port;
|
|
391
|
+
// Consume a separate --inspect-port value, or an endpoint
|
|
392
|
+
// after --inspect. Preserve ordinary application arguments.
|
|
393
|
+
if ((option.EndsWith("-port", StringComparison.OrdinalIgnoreCase) &&
|
|
394
|
+
!next.StartsWith("-", StringComparison.Ordinal)) ||
|
|
395
|
+
Int32.TryParse(next, out port) ||
|
|
396
|
+
(next.IndexOf(':') >= 0 && !next.StartsWith("-", StringComparison.Ordinal)))
|
|
397
|
+
{
|
|
398
|
+
i++;
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
continue;
|
|
402
|
+
}
|
|
403
|
+
forwarded.Add(arg);
|
|
404
|
+
}
|
|
405
|
+
return forwarded;
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
private static string FindNewestVersionedExe(string root)
|
|
409
|
+
{
|
|
410
|
+
if (!Directory.Exists(root))
|
|
411
|
+
{
|
|
412
|
+
return null;
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
string[] directories = Directory.GetDirectories(
|
|
416
|
+
root,
|
|
417
|
+
"app-*",
|
|
418
|
+
SearchOption.TopDirectoryOnly
|
|
419
|
+
);
|
|
420
|
+
|
|
421
|
+
Version newestVersion = null;
|
|
422
|
+
string newestExe = null;
|
|
423
|
+
|
|
424
|
+
for (int i = 0; i < directories.Length; i++)
|
|
425
|
+
{
|
|
426
|
+
string directory = directories[i];
|
|
427
|
+
string name = Path.GetFileName(directory);
|
|
428
|
+
|
|
429
|
+
if (
|
|
430
|
+
String.IsNullOrEmpty(name) ||
|
|
431
|
+
!name.StartsWith(
|
|
432
|
+
"app-",
|
|
433
|
+
StringComparison.OrdinalIgnoreCase
|
|
434
|
+
)
|
|
435
|
+
)
|
|
436
|
+
{
|
|
437
|
+
continue;
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
Version version;
|
|
441
|
+
|
|
442
|
+
if (!Version.TryParse(name.Substring(4), out version))
|
|
443
|
+
{
|
|
444
|
+
continue;
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
string exe = Path.Combine(
|
|
448
|
+
directory,
|
|
449
|
+
"RazerAppEngine.exe"
|
|
450
|
+
);
|
|
451
|
+
|
|
452
|
+
if (!File.Exists(exe))
|
|
453
|
+
{
|
|
454
|
+
continue;
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
if (
|
|
458
|
+
newestVersion == null ||
|
|
459
|
+
version > newestVersion
|
|
460
|
+
)
|
|
461
|
+
{
|
|
462
|
+
newestVersion = version;
|
|
463
|
+
newestExe = exe;
|
|
464
|
+
}
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
return newestExe;
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
private static string QuoteWindowsArgument(string value)
|
|
471
|
+
{
|
|
472
|
+
if (value == null)
|
|
473
|
+
{
|
|
474
|
+
return "\"\"";
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
if (value.Length == 0)
|
|
478
|
+
{
|
|
479
|
+
return "\"\"";
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
bool needsQuotes = false;
|
|
483
|
+
|
|
484
|
+
for (int i = 0; i < value.Length; i++)
|
|
485
|
+
{
|
|
486
|
+
char c = value[i];
|
|
487
|
+
|
|
488
|
+
if (
|
|
489
|
+
Char.IsWhiteSpace(c) ||
|
|
490
|
+
c == '"'
|
|
491
|
+
)
|
|
492
|
+
{
|
|
493
|
+
needsQuotes = true;
|
|
494
|
+
break;
|
|
495
|
+
}
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
if (!needsQuotes)
|
|
499
|
+
{
|
|
500
|
+
return value;
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
StringBuilder result = new StringBuilder();
|
|
504
|
+
result.Append('"');
|
|
505
|
+
|
|
506
|
+
int slashCount = 0;
|
|
507
|
+
|
|
508
|
+
for (int i = 0; i < value.Length; i++)
|
|
509
|
+
{
|
|
510
|
+
char c = value[i];
|
|
511
|
+
|
|
512
|
+
if (c == '\\')
|
|
513
|
+
{
|
|
514
|
+
slashCount++;
|
|
515
|
+
continue;
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
if (c == '"')
|
|
519
|
+
{
|
|
520
|
+
result.Append(
|
|
521
|
+
new string(
|
|
522
|
+
'\\',
|
|
523
|
+
slashCount * 2 + 1
|
|
524
|
+
)
|
|
525
|
+
);
|
|
526
|
+
|
|
527
|
+
result.Append('"');
|
|
528
|
+
slashCount = 0;
|
|
529
|
+
continue;
|
|
530
|
+
}
|
|
531
|
+
|
|
532
|
+
if (slashCount > 0)
|
|
533
|
+
{
|
|
534
|
+
result.Append(
|
|
535
|
+
new string('\\', slashCount)
|
|
536
|
+
);
|
|
537
|
+
|
|
538
|
+
slashCount = 0;
|
|
539
|
+
}
|
|
540
|
+
|
|
541
|
+
result.Append(c);
|
|
542
|
+
}
|
|
543
|
+
|
|
544
|
+
if (slashCount > 0)
|
|
545
|
+
{
|
|
546
|
+
result.Append(
|
|
547
|
+
new string(
|
|
548
|
+
'\\',
|
|
549
|
+
slashCount * 2
|
|
550
|
+
)
|
|
551
|
+
);
|
|
552
|
+
}
|
|
553
|
+
|
|
554
|
+
result.Append('"');
|
|
555
|
+
|
|
556
|
+
return result.ToString();
|
|
557
|
+
}
|
|
558
|
+
|
|
559
|
+
private static void LogError(string message)
|
|
560
|
+
{
|
|
561
|
+
try
|
|
562
|
+
{
|
|
563
|
+
string directory = Path.Combine(
|
|
564
|
+
Environment.GetFolderPath(
|
|
565
|
+
Environment.SpecialFolder.CommonApplicationData
|
|
566
|
+
),
|
|
567
|
+
"SynapseCTRL"
|
|
568
|
+
);
|
|
569
|
+
|
|
570
|
+
Directory.CreateDirectory(directory);
|
|
571
|
+
|
|
572
|
+
File.AppendAllText(
|
|
573
|
+
Path.Combine(
|
|
574
|
+
directory,
|
|
575
|
+
"shim-error.log"
|
|
576
|
+
),
|
|
577
|
+
DateTime.Now.ToString("O") +
|
|
578
|
+
Environment.NewLine +
|
|
579
|
+
message +
|
|
580
|
+
Environment.NewLine +
|
|
581
|
+
Environment.NewLine
|
|
582
|
+
);
|
|
583
|
+
}
|
|
584
|
+
catch
|
|
585
|
+
{
|
|
586
|
+
}
|
|
587
|
+
}
|
|
588
|
+
}
|
|
589
|
+
'@
|
|
590
|
+
|
|
591
|
+
$tempShim = Join-Path `
|
|
592
|
+
$InstallDir `
|
|
593
|
+
("RazerInspectShim-" + [Guid]::NewGuid().ToString('N') + ".exe")
|
|
594
|
+
|
|
595
|
+
try {
|
|
596
|
+
Add-Type `
|
|
597
|
+
-TypeDefinition $source `
|
|
598
|
+
-Language CSharp `
|
|
599
|
+
-OutputAssembly $tempShim `
|
|
600
|
+
-OutputType WindowsApplication
|
|
601
|
+
|
|
602
|
+
if (-not (Test-Path $tempShim)) {
|
|
603
|
+
throw "C# compiler did not produce the launcher executable."
|
|
604
|
+
}
|
|
605
|
+
|
|
606
|
+
Copy-Item `
|
|
607
|
+
-LiteralPath $tempShim `
|
|
608
|
+
-Destination $ShimExe `
|
|
609
|
+
-Force
|
|
610
|
+
}
|
|
611
|
+
finally {
|
|
612
|
+
Remove-Item `
|
|
613
|
+
-LiteralPath $tempShim `
|
|
614
|
+
-Force `
|
|
615
|
+
-ErrorAction SilentlyContinue
|
|
616
|
+
}
|
|
617
|
+
|
|
618
|
+
# Delete the old PowerShell-based shim if this is an upgrade.
|
|
619
|
+
Remove-Item `
|
|
620
|
+
-LiteralPath $OldShimPs1 `
|
|
621
|
+
-Force `
|
|
622
|
+
-ErrorAction SilentlyContinue
|
|
623
|
+
|
|
624
|
+
New-Item `
|
|
625
|
+
-Path $IFEOBase `
|
|
626
|
+
-Force | Out-Null
|
|
627
|
+
|
|
628
|
+
New-ItemProperty `
|
|
629
|
+
-Path $IFEOBase `
|
|
630
|
+
-Name UseFilter `
|
|
631
|
+
-PropertyType DWord `
|
|
632
|
+
-Value 1 `
|
|
633
|
+
-Force | Out-Null
|
|
634
|
+
|
|
635
|
+
New-Item `
|
|
636
|
+
-Path $FilterKey `
|
|
637
|
+
-Force | Out-Null
|
|
638
|
+
|
|
639
|
+
if (-not $filterProperties) {
|
|
640
|
+
$useFilterWasPresent = [int]($null -ne $baseProperties -and $null -ne $baseProperties.UseFilter)
|
|
641
|
+
New-ItemProperty -LiteralPath $FilterKey -Name UseFilterWasPresent -PropertyType DWord -Value $useFilterWasPresent -Force | Out-Null
|
|
642
|
+
if ($useFilterWasPresent) {
|
|
643
|
+
New-ItemProperty -LiteralPath $FilterKey -Name PreviousUseFilter -PropertyType DWord -Value $baseProperties.UseFilter -Force | Out-Null
|
|
644
|
+
}
|
|
645
|
+
}
|
|
646
|
+
New-ItemProperty -LiteralPath $FilterKey -Name HookVersion -PropertyType DWord -Value 2 -Force | Out-Null
|
|
647
|
+
|
|
648
|
+
New-ItemProperty `
|
|
649
|
+
-Path $FilterKey `
|
|
650
|
+
-Name FilterFullPath `
|
|
651
|
+
-PropertyType String `
|
|
652
|
+
-Value $StableExe `
|
|
653
|
+
-Force | Out-Null
|
|
654
|
+
|
|
655
|
+
# The critical change:
|
|
656
|
+
# IFEO now invokes a GUI-subsystem .exe directly.
|
|
657
|
+
# powershell.exe is no longer anywhere in the normal Synapse launch path.
|
|
658
|
+
$debuggerCommand = "`"$ShimExe`""
|
|
659
|
+
|
|
660
|
+
New-ItemProperty `
|
|
661
|
+
-Path $FilterKey `
|
|
662
|
+
-Name Debugger `
|
|
663
|
+
-PropertyType String `
|
|
664
|
+
-Value $debuggerCommand `
|
|
665
|
+
-Force | Out-Null
|
|
666
|
+
|
|
667
|
+
Write-Host ""
|
|
668
|
+
Write-Host "Installed native no-console Synapse inspector hook."
|
|
669
|
+
Write-Host ""
|
|
670
|
+
Write-Host "IFEO launcher:"
|
|
671
|
+
Write-Host " $ShimExe"
|
|
672
|
+
Write-Host ""
|
|
673
|
+
Write-Host "Synapse will receive:"
|
|
674
|
+
Write-Host " --inspect=127.0.0.1:9229"
|
|
675
|
+
Write-Host ""
|
|
676
|
+
Write-Host "No PowerShell process participates in normal Synapse launches."
|
|
677
|
+
Write-Host ""
|
|
678
|
+
Write-Host "Fully Exit Synapse once, then open the normal Razer Synapse shortcut."
|
|
679
|
+
Write-Host ""
|
|
680
|
+
Write-Host "Verify inspector:"
|
|
681
|
+
Write-Host " Invoke-RestMethod http://127.0.0.1:9229/json/list"
|
|
682
|
+
Write-Host ""
|
|
683
|
+
Write-Host "Verify no shim is lingering:"
|
|
684
|
+
Write-Host " Get-Process RazerInspectShim -ErrorAction SilentlyContinue"
|
|
685
|
+
Write-Host ""
|
|
686
|
+
Write-Host "That command should return nothing after Synapse has started."
|
|
687
|
+
Write-Host ""
|
|
688
|
+
}
|
|
689
|
+
|
|
690
|
+
try {
|
|
691
|
+
Invoke-SynapseHook
|
|
692
|
+
$resultCode = 0
|
|
693
|
+
} catch {
|
|
694
|
+
Write-Error "SynapseCTRL hook operation failed: $_" -ErrorAction Continue
|
|
695
|
+
$resultCode = 1
|
|
696
|
+
}
|
|
697
|
+
if (-not $NoPause) { Read-Host "Press Enter to continue..." | Out-Null }
|
|
698
|
+
exit $resultCode
|