Fix VirtViewer detection on Windows + manual path override
VirtViewer's installer folder name includes the version number (e.g. "VirtViewer v11.0-256"), so the previous hardcoded path never matched a real install. Now scans Program Files for any VirtViewer* folder, and adds a Settings dialog (Windows only) to pick the remote-viewer.exe path manually as a fallback.
This commit is contained in:
+65
-12
@@ -155,6 +155,28 @@ ipcMain.handle('proxmox:connect', async (_e, { node, vmid }) => {
|
||||
}
|
||||
});
|
||||
|
||||
// ---------- IPC: settings ----------
|
||||
|
||||
ipcMain.handle('settings:platform', () => process.platform);
|
||||
|
||||
ipcMain.handle('settings:getViewerPath', () => store.get('viewerPath', null));
|
||||
|
||||
ipcMain.handle('settings:clearViewerPath', () => {
|
||||
store.delete('viewerPath');
|
||||
});
|
||||
|
||||
ipcMain.handle('settings:browseViewerPath', async () => {
|
||||
const result = await dialog.showOpenDialog(mainWindow, {
|
||||
title: 'remote-viewer.exe auswählen',
|
||||
properties: ['openFile'],
|
||||
filters: [{ name: 'remote-viewer', extensions: ['exe'] }],
|
||||
});
|
||||
if (result.canceled || result.filePaths.length === 0) return null;
|
||||
const chosen = result.filePaths[0];
|
||||
store.set('viewerPath', chosen);
|
||||
return chosen;
|
||||
});
|
||||
|
||||
// ---------- SPICE helpers ----------
|
||||
|
||||
function buildVVFile(params, vmid) {
|
||||
@@ -187,24 +209,55 @@ function buildVVFile(params, vmid) {
|
||||
return vvPath;
|
||||
}
|
||||
|
||||
// VirtViewer's Windows installer names its folder after the bundled version
|
||||
// (e.g. "VirtViewer v11.0-256"), so a fixed path breaks on every version bump.
|
||||
// Scan the two Program Files dirs for any "VirtViewer*" folder instead.
|
||||
function findWindowsViewerCandidates() {
|
||||
const roots = [
|
||||
process.env['ProgramFiles'] || 'C:\\Program Files',
|
||||
process.env['ProgramFiles(x86)'] || 'C:\\Program Files (x86)',
|
||||
];
|
||||
const found = [];
|
||||
for (const root of roots) {
|
||||
let entries;
|
||||
try {
|
||||
entries = fs.readdirSync(root, { withFileTypes: true });
|
||||
} catch {
|
||||
continue;
|
||||
}
|
||||
for (const entry of entries) {
|
||||
if (!entry.isDirectory() || !/^virtviewer/i.test(entry.name)) continue;
|
||||
const exe = path.join(root, entry.name, 'bin', 'remote-viewer.exe');
|
||||
if (fs.existsSync(exe)) found.push(exe);
|
||||
}
|
||||
}
|
||||
return found;
|
||||
}
|
||||
|
||||
function launchRemoteViewer(vvPath) {
|
||||
const candidates =
|
||||
process.platform === 'win32'
|
||||
? [
|
||||
'remote-viewer',
|
||||
path.join('C:', 'Program Files', 'VirtViewer', 'bin', 'remote-viewer.exe'),
|
||||
path.join('C:', 'Program Files (x86)', 'VirtViewer', 'bin', 'remote-viewer.exe'),
|
||||
]
|
||||
: ['remote-viewer', 'virt-viewer'];
|
||||
let candidates;
|
||||
if (process.platform === 'win32') {
|
||||
const customPath = store.get('viewerPath', null);
|
||||
candidates = [
|
||||
...(customPath ? [customPath] : []),
|
||||
'remote-viewer',
|
||||
...findWindowsViewerCandidates(),
|
||||
];
|
||||
} else {
|
||||
candidates = ['remote-viewer', 'virt-viewer'];
|
||||
}
|
||||
|
||||
function tryNext(i) {
|
||||
if (i >= candidates.length) {
|
||||
const hint =
|
||||
process.platform === 'win32'
|
||||
? 'Windows: https://virt-manager.org/download/\n\n' +
|
||||
'Falls VirtViewer an einem nicht-standardmäßigen Ort installiert ist, ' +
|
||||
'trage den Pfad unter „Einstellungen → Viewer-Pfad" ein.'
|
||||
: 'Linux: sudo apt install virt-viewer';
|
||||
dialog.showErrorBox(
|
||||
'remote-viewer nicht gefunden',
|
||||
'Bitte installiere virt-viewer:\n\n' +
|
||||
' Linux: sudo apt install virt-viewer\n' +
|
||||
' Windows: https://virt-manager.org/download/\n\n' +
|
||||
'Danach bitte erneut verbinden.'
|
||||
'Bitte installiere virt-viewer:\n\n' + hint + '\n\nDanach bitte erneut verbinden.'
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user