Files
XCDesktop/remote/src/services/clipboard/ClipboardService.js
2026-03-08 01:34:54 +08:00

132 lines
3.7 KiB
JavaScript

const { spawn } = require('child_process');
const logger = require('../../utils/logger');
const CLIPBOARD_THRESHOLD = 500 * 1024;
class ClipboardService {
constructor() {
this.threshold = CLIPBOARD_THRESHOLD;
}
async read() {
return new Promise((resolve, reject) => {
const psCode = `
Add-Type -AssemblyName System.Windows.Forms
Add-Type -AssemblyName System.Drawing
if ([Windows.Forms.Clipboard]::ContainsText()) {
$text = [Windows.Forms.Clipboard]::GetText()
Write-Output "TYPE:TEXT"
Write-Output $text
} elseif ([Windows.Forms.Clipboard]::ContainsImage()) {
$image = [Windows.Forms.Clipboard]::GetImage()
if ($image -ne $null) {
$ms = New-Object System.IO.MemoryStream
$image.Save($ms, [System.Drawing.Imaging.ImageFormat]::Png)
$bytes = $ms.ToArray()
$base64 = [Convert]::ToBase64String($bytes)
Write-Output "TYPE:IMAGE"
Write-Output $base64
}
} else {
Write-Output "TYPE:EMPTY"
}
`;
const ps = spawn('powershell', ['-NoProfile', '-Command',
'[Console]::OutputEncoding = [System.Text.Encoding]::UTF8; ' + psCode
], { encoding: 'utf8' });
let output = '';
ps.stdout.on('data', (data) => {
output += data.toString();
});
ps.stderr.on('data', (data) => {
logger.error('Clipboard read error', { error: data.toString() });
});
ps.on('close', () => {
const lines = output.trim().split('\n');
const typeLine = lines[0];
if (typeLine && typeLine.includes('TYPE:TEXT')) {
const text = lines.slice(1).join('\n');
resolve({
type: 'text',
data: text,
size: Buffer.byteLength(text, 'utf8')
});
} else if (typeLine && typeLine.includes('TYPE:IMAGE')) {
const base64 = lines.slice(1).join('');
const size = Math.ceil(base64.length * 0.75);
resolve({
type: 'image',
data: base64,
size: size
});
} else {
resolve({ type: 'empty', data: null, size: 0 });
}
});
ps.on('error', (err) => {
logger.error('Clipboard read failed', { error: err.message });
resolve({ type: 'empty', data: null, size: 0 });
});
});
}
async set(content) {
return new Promise((resolve, reject) => {
let psCode;
if (content.type === 'text') {
const escapedText = content.data
.replace(/'/g, "''")
.replace(/\r\n/g, '`r`n')
.replace(/\n/g, '`n');
psCode = `
$OutputEncoding = [Console]::OutputEncoding = [Text.UTF8Encoding]::UTF8
Add-Type -AssemblyName System.Windows.Forms
[Windows.Forms.Clipboard]::SetText('${escapedText}')
Write-Output "SUCCESS"
`;
} else if (content.type === 'image') {
psCode = `
$OutputEncoding = [Console]::OutputEncoding = [Text.UTF8Encoding]::UTF8
Add-Type -AssemblyName System.Windows.Forms
Add-Type -AssemblyName System.Drawing
$bytes = [Convert]::FromBase64String('${content.data}')
$ms = New-Object System.IO.MemoryStream(,$bytes)
$image = [System.Drawing.Image]::FromStream($ms)
[Windows.Forms.Clipboard]::SetImage($image)
Write-Output "SUCCESS"
`;
} else {
resolve(false);
return;
}
const ps = spawn('powershell', ['-NoProfile', '-Command', psCode]);
let output = '';
ps.stdout.on('data', (data) => {
output += data.toString();
});
ps.on('close', () => {
resolve(output.includes('SUCCESS'));
});
ps.on('error', () => resolve(false));
});
}
isSmallContent(size) {
return size <= this.threshold;
}
}
module.exports = ClipboardService;