|
1 | 1 | import * as fs from "fs"; |
| 2 | +import * as os from "os"; |
2 | 3 | import * as path from "path"; |
3 | 4 | import { spawn } from "child_process"; |
4 | 5 | import { ApplicationManagerBase } from "../application-manager-base"; |
@@ -81,6 +82,19 @@ export class WindowsApplicationManager extends ApplicationManagerBase { |
81 | 82 | } |
82 | 83 |
|
83 | 84 | this.$logger.info(`[Windows] Installing MSIX/APPX from: ${packageFilePath}`); |
| 85 | + // If we have an app identifier, try to remove any existing package first to |
| 86 | + // avoid the "package is already installed" HRESULT (0x80073CFB) which |
| 87 | + // blocks re-registration in development flows. |
| 88 | + if (appIdentifier) { |
| 89 | + try { |
| 90 | + this.$logger.info(`[Windows] Attempting to remove existing package: ${appIdentifier}`); |
| 91 | + // uninstallApplication handles EXE cleanup and runs the Remove-AppxPackage |
| 92 | + // command for UWP packages. Ignore errors and proceed to install. |
| 93 | + await this.uninstallApplication(appIdentifier); |
| 94 | + } catch (err) { |
| 95 | + this.$logger.warn(`[Windows] Pre-install uninstall failed: ${err}`); |
| 96 | + } |
| 97 | + } |
84 | 98 | await this.$childProcess.spawnFromEvent( |
85 | 99 | "powershell.exe", |
86 | 100 | [ |
@@ -128,9 +142,33 @@ export class WindowsApplicationManager extends ApplicationManagerBase { |
128 | 142 | await this.startApplication(appData); |
129 | 143 | } |
130 | 144 |
|
| 145 | + /** |
| 146 | + * Returns the path of the runtime's trace log for the most recently started app. |
| 147 | + * For UWP-packaged apps the runtime DLL writes inside the app container's TempState, |
| 148 | + * not the global system temp directory. Falls back to the system temp path used when |
| 149 | + * the app is launched as an unpackaged EXE. |
| 150 | + */ |
| 151 | + public getLogFilePath(): string { |
| 152 | + const systemTempLog = path.join(os.tmpdir(), "ns_trace.log"); |
| 153 | + if (this._packageFamilyNames.size > 0) { |
| 154 | + const pfn = this._packageFamilyNames.values().next().value as string; |
| 155 | + const localAppData = process.env.LOCALAPPDATA; |
| 156 | + if (localAppData && pfn) { |
| 157 | + return path.join(localAppData, "Packages", pfn, "TempState", "ns_trace.log"); |
| 158 | + } |
| 159 | + } |
| 160 | + return systemTempLog; |
| 161 | + } |
| 162 | + |
131 | 163 | public async startApplication( |
132 | 164 | appData: Mobile.IStartApplicationData, |
133 | 165 | ): Promise<void> { |
| 166 | + // Truncate the trace log so the streamer starts from a clean state each run. |
| 167 | + try { |
| 168 | + const logPath = this.getLogFilePath(); |
| 169 | + fs.writeFileSync(logPath, "", "utf8"); |
| 170 | + } catch { /* ignore — log dir may not exist yet */ } |
| 171 | + |
134 | 172 | const exeCandidate = |
135 | 173 | (appData.appId && this._installedExePaths[appData.appId]) || |
136 | 174 | (appData.projectName && this._installedExePaths[appData.projectName]); |
@@ -159,8 +197,11 @@ export class WindowsApplicationManager extends ApplicationManagerBase { |
159 | 197 | if (appData.waitForDebugger) { |
160 | 198 | this._writeDebugBreakMarker(pfn); |
161 | 199 | } |
162 | | - this.$logger.info(`[Windows] Launching UWP: ${pfn}`); |
163 | | - const proc = spawn("explorer.exe", [`ms-windows-app://${pfn}`], { |
| 200 | + // UWP apps are launched via shell:AppsFolder\<PFN>!<ApplicationId>. |
| 201 | + // The ApplicationId comes from the <Application Id="..."> attribute in the manifest. |
| 202 | + const appId = "App"; |
| 203 | + this.$logger.info(`[Windows] Launching UWP: ${pfn}!${appId}`); |
| 204 | + const proc = spawn("explorer.exe", [`shell:AppsFolder\\${pfn}!${appId}`], { |
164 | 205 | detached: true, |
165 | 206 | stdio: "ignore", |
166 | 207 | }); |
|
0 commit comments