RDP tabs on Windows: no visible mstsc windows and resolution follows the tab size. mstsc is started with /v: instead of an unsigned .rdp file (avoids the mandatory "unknown publisher" security warning; a file is still used when the host needs drive redirection, clipboard/audio off or scaling); a WinEvent hook adopts the session window while it is still hidden (borderless, parked off-screen until logged in), keeps the "connecting" progress dialog invisible and shows real prompts (certificate, errors) centered over the tab; servers without dynamic resolution (mstsc clamps the window to the session size) are detected and reconnected with the new size after resizing, re-confirming a certificate the user already accepted for this session
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
+25
-9
@@ -473,30 +473,46 @@ handle('rdp:open', async (id, hostRef, bounds) => {
|
||||
const host = hostWithOverrides(hostRef);
|
||||
if (host.id) store.addHistory({ hostId: host.id, at: Date.now() });
|
||||
if (await vpn.ensureForHost(host)) send('vpn:changed');
|
||||
const emb = createEmbed(win.getNativeWindowHandle());
|
||||
const sess = { emb, cancelled: false };
|
||||
const sess = { cancelled: false };
|
||||
rdpSessions.set(id, sess);
|
||||
return startRdpEmbed(id, sess, host, bounds);
|
||||
});
|
||||
async function startRdpEmbed(id, sess, host, bounds, acceptCert = false) {
|
||||
const emb = createEmbed(win.getNativeWindowHandle());
|
||||
emb.acceptCert = acceptCert;
|
||||
sess.emb = emb;
|
||||
const size = { width: bounds.width, height: bounds.height };
|
||||
let proc;
|
||||
if (process.platform === 'win32') {
|
||||
const file = rdp.writeRdpFile(host, size);
|
||||
proc = await new Promise((resolve) => rdp.withWinCredentials(host, () => resolve(emb.start({ rdpFile: file }))));
|
||||
const args = rdp.mstscArgs(host, size);
|
||||
proc = await new Promise((resolve) => rdp.withWinCredentials(host, () => resolve(emb.start({ args }))));
|
||||
} else {
|
||||
proc = emb.start({ client: rdp.linuxClient(null, true), args: rdp.freerdpArgs(host, size), password: host.password });
|
||||
}
|
||||
proc.on('error', (e) => send('rdp:event', id, 'exit', { code: -1, message: e.message }));
|
||||
const current = () => rdpSessions.get(id) === sess && sess.emb === emb;
|
||||
proc.on('error', (e) => { if (current()) send('rdp:event', id, 'exit', { code: -1, message: e.message }); });
|
||||
proc.on('exit', (code) => {
|
||||
if (!current()) return; // durch Neuverbindung ersetzt
|
||||
const err = (emb.err || '').split('\n').filter((l) => /ERROR|error/.test(l)).slice(-1)[0] || '';
|
||||
if (rdpSessions.get(id) === sess) send('rdp:event', id, 'exit', { code, message: err });
|
||||
send('rdp:event', id, 'exit', { code, message: err });
|
||||
rdpSessions.delete(id);
|
||||
});
|
||||
const ok = await emb.attach(bounds, () => sess.cancelled);
|
||||
if (ok) {
|
||||
// Server ohne dynamische Auflösung: mit neuer Größe neu verbinden (die Sitzung auf dem Server bleibt erhalten)
|
||||
emb.onResizeNeeded = (b) => {
|
||||
if (!current()) return;
|
||||
send('rdp:event', id, 'resizing');
|
||||
emb.kill();
|
||||
startRdpEmbed(id, sess, host, b, emb.certAccepted).then((ok) => {
|
||||
if (!ok && rdpSessions.get(id) === sess && !sess.cancelled) send('rdp:event', id, 'exit', { code: -1, message: '' });
|
||||
});
|
||||
};
|
||||
const ok = await emb.attach(bounds, () => sess.cancelled || !current());
|
||||
if (ok && current()) {
|
||||
if (sess.hidden) emb.hide();
|
||||
send('rdp:event', id, 'attached');
|
||||
}
|
||||
return ok;
|
||||
});
|
||||
}
|
||||
ipcMain.on('rdp:bounds', (_e, id, b) => { try { rdpSessions.get(id)?.emb.setBounds(b); } catch {} });
|
||||
ipcMain.on('rdp:show', (_e, id) => { const s = rdpSessions.get(id); if (s) { s.hidden = false; try { s.emb.show(); s.emb.focus(); } catch {} } });
|
||||
ipcMain.on('rdp:hide', (_e, id) => { const s = rdpSessions.get(id); if (s) { s.hidden = true; try { s.emb.hide(); } catch {} } });
|
||||
|
||||
Reference in New Issue
Block a user