问题描述:
Avalonia的剪切板IClipboard实现目前(Ver11.3.10)在UOS国产系统下不能成功将文件写入剪切板,在桌面文件夹粘贴时会报错:
尝试了Avalonia的DataObject旧方法和直接用C#操纵X11,也试了xclip-copyfile命令行,均未能成功,最后曲线救国,采用自编译可执行文件,在软件里调用成功,实现在软件内选择文件复制,然后在桌面文件夹粘贴OK。
以下是需要的代码:
一、实现思路
使用X11库与系统剪切板交互
将文件路径转换为URI格式
设置剪切板数据为x-special/gnome-copied-files格式
二、完整代码
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <X11/Xlib.h>
#include <X11/Xatom.h>
// 将文件路径转换为文件URI
char* file_to_uri(const char* filepath) {
// 简单的实现,实际应该进行URL编码
int length = strlen(filepath) + 8; // "file://" + 原路径 + '\0'
char* uri = malloc(length);
if (uri == NULL) return NULL;
snprintf(uri, length, "file://%s", filepath);
return uri;
}
// 设置文件到剪切板
int set_file_to_clipboard(const char* filepath) {
Display* display = XOpenDisplay(NULL);
if (!display) {
fprintf(stderr, "无法打开X显示\n");
return 1;
}
Window window = XCreateSimpleWindow(display,
DefaultRootWindow(display),
0, 0, 1, 1, 0, 0, 0);
// 获取剪切板原子
Atom clipboard = XInternAtom(display, "CLIPBOARD", False);
Atom targets_atom = XInternAtom(display, "TARGETS", False);
Atom text_atom = XInternAtom(display, "TEXT", False);
Atom utf8_atom = XInternAtom(display, "UTF8_STRING", False);
Atom x_special_atom = XInternAtom(display, "x-special/gnome-copied-files", False);
Atom uri_list_atom = XInternAtom(display, "text/uri-list", False);
// 转换为URI格式
char* uri = file_to_uri(filepath);
if (!uri) {
fprintf(stderr, "无法创建URI\n");
XCloseDisplay(display);
return 1;
}
// 创建剪切板数据
// GNOME格式: copy\nfile://path
int copy_cmd_len = strlen("copy\n") + strlen(uri) + 1;
char* copy_cmd = malloc(copy_cmd_len);
if (!copy_cmd) {
free(uri);
XCloseDisplay(display);
return 1;
}
snprintf(copy_cmd, copy_cmd_len, "copy\n%s", uri);
// URI列表格式: file://path
char* uri_list = malloc(strlen(uri) + 2); // uri + \n + \0
if (!uri_list) {
free(uri);
free(copy_cmd);
XCloseDisplay(display);
return 1;
}
snprintf(uri_list, strlen(uri) + 2, "%s\n", uri);
// 设置剪切板所有权
XSetSelectionOwner(display, clipboard, window, CurrentTime);
if (XGetSelectionOwner(display, clipboard) != window) {
fprintf(stderr, "无法获取剪切板所有权\n");
free(uri);
free(copy_cmd);
free(uri_list);
XCloseDisplay(display);
return 1;
}
// 等待剪切板请求
XEvent event;
while (1) {
XNextEvent(display, &event);
if (event.type == SelectionRequest) {
XSelectionRequestEvent* req = &event.xselectionrequest;
XSelectionEvent notify = {0};
notify.type = SelectionNotify;
notify.display = req->display;
notify.requestor = req->requestor;
notify.selection = req->selection;
notify.target = req->target;
notify.property = req->property;
notify.time = req->time;
if (req->target == x_special_atom) {
// 设置x-special/gnome-copied-files格式
XChangeProperty(req->display, req->requestor, req->property,
utf8_atom, 8, PropModeReplace,
(unsigned char*)copy_cmd, strlen(copy_cmd));
notify.property = req->property;
XSendEvent(req->display, req->requestor, False, 0, (XEvent*)¬ify);
}
else if (req->target == uri_list_atom) {
// 设置text/uri-list格式
XChangeProperty(req->display, req->requestor, req->property,
utf8_atom, 8, PropModeReplace,
(unsigned char*)uri_list, strlen(uri_list));
notify.property = req->property;
XSendEvent(req->display, req->requestor, False, 0, (XEvent*)¬ify);
}
else if (req->target == targets_atom) {
// 返回支持的格式
Atom targets[] = { x_special_atom, uri_list_atom, text_atom, utf8_atom };
XChangeProperty(req->display, req->requestor, req->property,
XA_ATOM, 32, PropModeReplace,
(unsigned char*)targets, sizeof(targets)/sizeof(targets[0]));
notify.property = req->property;
XSendEvent(req->display, req->requestor, False, 0, (XEvent*)¬ify);
}
else if (req->target == text_atom || req->target == utf8_atom) {
// 纯文本格式(文件路径)
XChangeProperty(req->display, req->requestor, req->property,
utf8_atom, 8, PropModeReplace,
(unsigned char*)filepath, strlen(filepath));
notify.property = req->property;
XSendEvent(req->display, req->requestor, False, 0, (XEvent*)¬ify);
}
else {
// 不支持的目标
notify.property = None;
XSendEvent(req->display, req->requestor, False, 0, (XEvent*)¬ify);
}
}
else if (event.type == SelectionClear) {
break;
}
}
// 清理
free(uri);
free(copy_cmd);
free(uri_list);
XDestroyWindow(display, window);
XCloseDisplay(display);
printf("文件 '%s' 已复制到剪切板\n", filepath);
printf("现在可以在文件管理器中右键粘贴或使用Ctrl+V粘贴\n");
return 0;
}
int main(int argc, char* argv[]) {
if (argc != 2) {
fprintf(stderr, "用法: %s <文件路径>\n", argv[0]);
fprintf(stderr, "示例: %s /home/user/example.txt\n", argv[0]);
return 1;
}
// 检查文件是否存在
FILE* file = fopen(argv[1], "r");
if (!file) {
fprintf(stderr, "错误: 文件 '%s' 不存在或无法读取\n", argv[1]);
return 1;
}
fclose(file);
return set_file_to_clipboard(argv[1]);
}
三、编译方法
# 安装必要的开发库
sudo apt-get install libx11-dev
# 编译程序
gcc -o set-file-clipboard set-file-clipboard.c -lX11
四、使用方法
# 设置文件到剪切板
./set-file-clipboard /path/to/your/file.txt
View Code
#region UOS(dde-file-manager)剪切文件
public static async Task SetFilesToClipboard(List<string> filePaths)
{
if (filePaths == null || filePaths.Count == 0)
return;
// 每个带空格的文件路径都需要用引号包围
var arguments = string.Join(" ", filePaths.Select(fp =>
$"\"{fp.Replace("\"", "\\\"")}\""
));
const string exePath = "set-files-to-clipboard";
if (!File.Exists(exePath))
throw new FileNotFoundException($"可执行文件不存在: {exePath}");
if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
try
{
var mode = File.GetUnixFileMode(exePath);
if ((mode & UnixFileMode.UserExecute) == 0)
{
File.SetUnixFileMode(exePath,
mode | UnixFileMode.UserExecute |
UnixFileMode.GroupExecute |
UnixFileMode.OtherExecute);
LogHelper.Info($"已为 {exePath} 设置执行权限");
}
}
catch (Exception ex)
{
LogHelper.Error($"设置权限失败: {ex.Message}");
}
}
var process = new Process
{
StartInfo = new ProcessStartInfo
{
FileName = exePath,
Arguments = arguments,
UseShellExecute = false,
RedirectStandardOutput = true,
RedirectStandardError = true,
CreateNoWindow = true
}
};
LogHelper.Info($"执行命令: {process.StartInfo.FileName} {arguments}");
process.Start();
var output = await process.StandardOutput.ReadToEndAsync();
var error = await process.StandardError.ReadToEndAsync();
await process.WaitForExitAsync();
if (process.ExitCode != 0)
{
throw new Exception($"设置剪切板失败: {error}");
}
LogHelper.Info(output);
}
#endregion
View Code
关键词:复制,剪切,粘贴文件,文件管理器,Avalonia,Clipboard,dde-file-manager,UOS,Linux