|
27 | 27 | # On any timeout the script enumerates every window the process owns and writes a full |
28 | 28 | # minidump of the LIVE process into -DumpDir (defaults to $env:WER_DUMP_DIR) before killing |
29 | 29 | # it, so a hang is as diagnosable as a crash. |
30 | | -# |
31 | | -# The exe must be launchable in the calling environment. A uiAccess-manifested build will not |
32 | | -# launch AT ALL where UAC is disabled (GitHub-hosted runners: EnableLUA=0 means Windows |
33 | | -# cannot mint a UIAccess token, so CreateProcess fails outright) — the CI workflow strips |
34 | | -# uiAccess from the binary first. On a normal desktop, point this at any signed build. |
35 | 30 |
|
36 | 31 | param( |
37 | 32 | # Exactly one of these. -ExePath launches a loose exe (note: outside a secure path a |
@@ -133,6 +128,43 @@ public static class ClippSmoke |
133 | 128 | [DllImport("dbghelp.dll", SetLastError = true)] |
134 | 129 | public static extern bool MiniDumpWriteDump(IntPtr hProcess, uint ProcessId, IntPtr hFile, |
135 | 130 | int DumpType, IntPtr ExceptionParam, IntPtr UserStreamParam, IntPtr CallbackParam); |
| 131 | +
|
| 132 | + [DllImport("kernel32.dll", SetLastError = true)] |
| 133 | + static extern IntPtr OpenProcess(uint access, bool inherit, uint pid); |
| 134 | + [DllImport("advapi32.dll", SetLastError = true)] |
| 135 | + static extern bool OpenProcessToken(IntPtr hProcess, uint access, out IntPtr hToken); |
| 136 | + [DllImport("advapi32.dll", SetLastError = true)] |
| 137 | + static extern bool GetTokenInformation(IntPtr hToken, int infoClass, out uint info, uint len, out uint retLen); |
| 138 | + [DllImport("kernel32.dll")] |
| 139 | + static extern bool CloseHandle(IntPtr h); |
| 140 | +
|
| 141 | + // Whether the process token carries the UIAccess flag (TokenUIAccess, class 26) — i.e. |
| 142 | + // Windows actually granted the manifest's uiAccess request, the configuration whose |
| 143 | + // faults this test exists to catch. Opens its own PROCESS_QUERY_LIMITED_INFORMATION |
| 144 | + // handle: that right is grantable across the UIPI/integrity boundary a UIAccess target |
| 145 | + // sits behind, where the PROCESS_ALL_ACCESS handle .NET's Process.Handle wants is not. |
| 146 | + // Returns 1 (granted), 0 (not granted), or the negated Win32 error on failure. |
| 147 | + public static int GetUIAccess(uint pid) |
| 148 | + { |
| 149 | + IntPtr proc = OpenProcess(0x1000 /* PROCESS_QUERY_LIMITED_INFORMATION */, false, pid); |
| 150 | + if (proc == IntPtr.Zero) |
| 151 | + return -Marshal.GetLastWin32Error(); |
| 152 | + IntPtr token = IntPtr.Zero; |
| 153 | + try |
| 154 | + { |
| 155 | + if (!OpenProcessToken(proc, 0x0008 /* TOKEN_QUERY */, out token)) |
| 156 | + return -Marshal.GetLastWin32Error(); |
| 157 | + uint val, len; |
| 158 | + if (!GetTokenInformation(token, 26 /* TokenUIAccess */, out val, 4, out len)) |
| 159 | + return -Marshal.GetLastWin32Error(); |
| 160 | + return val != 0 ? 1 : 0; |
| 161 | + } |
| 162 | + finally |
| 163 | + { |
| 164 | + if (token != IntPtr.Zero) CloseHandle(token); |
| 165 | + CloseHandle(proc); |
| 166 | + } |
| 167 | + } |
136 | 168 | } |
137 | 169 |
|
138 | 170 | // Packaged-app activation. shell:AppsFolder via Start-Process hands off to the AppX broker |
@@ -236,6 +268,22 @@ if ($PSCmdlet.ParameterSetName -eq 'Package') { |
236 | 268 | $p = Start-Process -FilePath $ExePath -PassThru |
237 | 269 | } |
238 | 270 |
|
| 271 | +# Report whether Windows actually granted the manifest's uiAccess request. Without the token |
| 272 | +# a green run says nothing about the uiAccess configuration winget validates — the packaged |
| 273 | +# case warns loudly rather than failing, so the exit-code reading below still happens. |
| 274 | +$ui = [ClippSmoke]::GetUIAccess([uint32]$p.Id) |
| 275 | +if ($ui -eq 1) { |
| 276 | + Write-Host 'UIAccess token: GRANTED' |
| 277 | +} elseif ($ui -eq 0) { |
| 278 | + if ($PSCmdlet.ParameterSetName -eq 'Package') { |
| 279 | + Write-Host '::warning::UIAccess token NOT granted to the packaged app — this run does not exercise the uiAccess configuration winget validates.' |
| 280 | + } else { |
| 281 | + Write-Host 'UIAccess token: not granted (expected for a loose exe outside a secure path).' |
| 282 | + } |
| 283 | +} else { |
| 284 | + Write-Host ("UIAccess token: unreadable (Win32 {0})" -f (-$ui)) |
| 285 | +} |
| 286 | + |
239 | 287 | # On a clean profile (no group key) Clipp opens its main XAML-Islands window unprompted. |
240 | 288 | # Islands cold-start can be slow on a runner, hence the generous timeout. |
241 | 289 | $dialog = [IntPtr]::Zero |
|
0 commit comments