找回密码
 立即注册
首页 业界区 业界 UOS环境C#/Avalonia将文件剪切到剪切(粘贴)板实现 ...

UOS环境C#/Avalonia将文件剪切到剪切(粘贴)板实现

倡遍竽 前天 15:10
问题描述:
Avalonia的剪切板IClipboard实现目前(Ver11.3.10)在UOS国产系统下不能成功将文件写入剪切板,在桌面文件夹粘贴时会报错:
1.png

尝试了Avalonia的DataObject旧方法和直接用C#操纵X11,也试了xclip-copyfile命令行,均未能成功,最后曲线救国,采用自编译可执行文件,在软件里调用成功,实现在软件内选择文件复制,然后在桌面文件夹粘贴OK。
以下是需要的代码:
2.gif
3.gif
  1. 一、实现思路
  2. 使用X11库与系统剪切板交互
  3. 将文件路径转换为URI格式
  4. 设置剪切板数据为x-special/gnome-copied-files格式
  5. 二、完整代码
  6. #include <stdio.h>
  7. #include <stdlib.h>
  8. #include <string.h>
  9. #include <X11/Xlib.h>
  10. #include <X11/Xatom.h>
  11. // 将文件路径转换为文件URI
  12. char* file_to_uri(const char* filepath) {
  13.     // 简单的实现,实际应该进行URL编码
  14.     int length = strlen(filepath) + 8; // "file://" + 原路径 + '\0'
  15.     char* uri = malloc(length);
  16.     if (uri == NULL) return NULL;
  17.    
  18.     snprintf(uri, length, "file://%s", filepath);
  19.     return uri;
  20. }
  21. // 设置文件到剪切板
  22. int set_file_to_clipboard(const char* filepath) {
  23.     Display* display = XOpenDisplay(NULL);
  24.     if (!display) {
  25.         fprintf(stderr, "无法打开X显示\n");
  26.         return 1;
  27.     }
  28.    
  29.     Window window = XCreateSimpleWindow(display,
  30.                                        DefaultRootWindow(display),
  31.                                        0, 0, 1, 1, 0, 0, 0);
  32.    
  33.     // 获取剪切板原子
  34.     Atom clipboard = XInternAtom(display, "CLIPBOARD", False);
  35.     Atom targets_atom = XInternAtom(display, "TARGETS", False);
  36.     Atom text_atom = XInternAtom(display, "TEXT", False);
  37.     Atom utf8_atom = XInternAtom(display, "UTF8_STRING", False);
  38.     Atom x_special_atom = XInternAtom(display, "x-special/gnome-copied-files", False);
  39.     Atom uri_list_atom = XInternAtom(display, "text/uri-list", False);
  40.    
  41.     // 转换为URI格式
  42.     char* uri = file_to_uri(filepath);
  43.     if (!uri) {
  44.         fprintf(stderr, "无法创建URI\n");
  45.         XCloseDisplay(display);
  46.         return 1;
  47.     }
  48.    
  49.     // 创建剪切板数据
  50.     // GNOME格式: copy\nfile://path
  51.     int copy_cmd_len = strlen("copy\n") + strlen(uri) + 1;
  52.     char* copy_cmd = malloc(copy_cmd_len);
  53.     if (!copy_cmd) {
  54.         free(uri);
  55.         XCloseDisplay(display);
  56.         return 1;
  57.     }
  58.     snprintf(copy_cmd, copy_cmd_len, "copy\n%s", uri);
  59.    
  60.     // URI列表格式: file://path
  61.     char* uri_list = malloc(strlen(uri) + 2); // uri + \n + \0
  62.     if (!uri_list) {
  63.         free(uri);
  64.         free(copy_cmd);
  65.         XCloseDisplay(display);
  66.         return 1;
  67.     }
  68.     snprintf(uri_list, strlen(uri) + 2, "%s\n", uri);
  69.    
  70.     // 设置剪切板所有权
  71.     XSetSelectionOwner(display, clipboard, window, CurrentTime);
  72.    
  73.     if (XGetSelectionOwner(display, clipboard) != window) {
  74.         fprintf(stderr, "无法获取剪切板所有权\n");
  75.         free(uri);
  76.         free(copy_cmd);
  77.         free(uri_list);
  78.         XCloseDisplay(display);
  79.         return 1;
  80.     }
  81.    
  82.     // 等待剪切板请求
  83.     XEvent event;
  84.     while (1) {
  85.         XNextEvent(display, &event);
  86.         
  87.         if (event.type == SelectionRequest) {
  88.             XSelectionRequestEvent* req = &event.xselectionrequest;
  89.             XSelectionEvent notify = {0};
  90.             
  91.             notify.type = SelectionNotify;
  92.             notify.display = req->display;
  93.             notify.requestor = req->requestor;
  94.             notify.selection = req->selection;
  95.             notify.target = req->target;
  96.             notify.property = req->property;
  97.             notify.time = req->time;
  98.             
  99.             if (req->target == x_special_atom) {
  100.                 // 设置x-special/gnome-copied-files格式
  101.                 XChangeProperty(req->display, req->requestor, req->property,
  102.                               utf8_atom, 8, PropModeReplace,
  103.                               (unsigned char*)copy_cmd, strlen(copy_cmd));
  104.                 notify.property = req->property;
  105.                 XSendEvent(req->display, req->requestor, False, 0, (XEvent*)&notify);
  106.             }
  107.             else if (req->target == uri_list_atom) {
  108.                 // 设置text/uri-list格式
  109.                 XChangeProperty(req->display, req->requestor, req->property,
  110.                               utf8_atom, 8, PropModeReplace,
  111.                               (unsigned char*)uri_list, strlen(uri_list));
  112.                 notify.property = req->property;
  113.                 XSendEvent(req->display, req->requestor, False, 0, (XEvent*)&notify);
  114.             }
  115.             else if (req->target == targets_atom) {
  116.                 // 返回支持的格式
  117.                 Atom targets[] = { x_special_atom, uri_list_atom, text_atom, utf8_atom };
  118.                 XChangeProperty(req->display, req->requestor, req->property,
  119.                               XA_ATOM, 32, PropModeReplace,
  120.                               (unsigned char*)targets, sizeof(targets)/sizeof(targets[0]));
  121.                 notify.property = req->property;
  122.                 XSendEvent(req->display, req->requestor, False, 0, (XEvent*)&notify);
  123.             }
  124.             else if (req->target == text_atom || req->target == utf8_atom) {
  125.                 // 纯文本格式(文件路径)
  126.                 XChangeProperty(req->display, req->requestor, req->property,
  127.                               utf8_atom, 8, PropModeReplace,
  128.                               (unsigned char*)filepath, strlen(filepath));
  129.                 notify.property = req->property;
  130.                 XSendEvent(req->display, req->requestor, False, 0, (XEvent*)&notify);
  131.             }
  132.             else {
  133.                 // 不支持的目标
  134.                 notify.property = None;
  135.                 XSendEvent(req->display, req->requestor, False, 0, (XEvent*)&notify);
  136.             }
  137.         }
  138.         else if (event.type == SelectionClear) {
  139.             break;
  140.         }
  141.     }
  142.    
  143.     // 清理
  144.     free(uri);
  145.     free(copy_cmd);
  146.     free(uri_list);
  147.     XDestroyWindow(display, window);
  148.     XCloseDisplay(display);
  149.    
  150.     printf("文件 '%s' 已复制到剪切板\n", filepath);
  151.     printf("现在可以在文件管理器中右键粘贴或使用Ctrl+V粘贴\n");
  152.    
  153.     return 0;
  154. }
  155. int main(int argc, char* argv[]) {
  156.     if (argc != 2) {
  157.         fprintf(stderr, "用法: %s <文件路径>\n", argv[0]);
  158.         fprintf(stderr, "示例: %s /home/user/example.txt\n", argv[0]);
  159.         return 1;
  160.     }
  161.    
  162.     // 检查文件是否存在
  163.     FILE* file = fopen(argv[1], "r");
  164.     if (!file) {
  165.         fprintf(stderr, "错误: 文件 '%s' 不存在或无法读取\n", argv[1]);
  166.         return 1;
  167.     }
  168.     fclose(file);
  169.    
  170.     return set_file_to_clipboard(argv[1]);
  171. }
  172. 三、编译方法
  173. # 安装必要的开发库
  174. sudo apt-get install libx11-dev
  175. # 编译程序
  176. gcc -o set-file-clipboard set-file-clipboard.c -lX11
  177. 四、使用方法
  178. # 设置文件到剪切板
  179. ./set-file-clipboard /path/to/your/file.txt
复制代码
View Code
4.gif
5.gif
  1. #region UOS(dde-file-manager)剪切文件
  2. public static async Task SetFilesToClipboard(List<string> filePaths)
  3. {
  4.     if (filePaths == null || filePaths.Count == 0)
  5.         return;
  6.    
  7.     // 每个带空格的文件路径都需要用引号包围
  8.     var arguments = string.Join(" ", filePaths.Select(fp =>
  9.         $""{fp.Replace(""", "\\"")}""
  10.     ));
  11.     const string exePath = "set-files-to-clipboard";
  12.     if (!File.Exists(exePath))
  13.         throw new FileNotFoundException($"可执行文件不存在: {exePath}");
  14.    
  15.     if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
  16.     {
  17.         try
  18.         {
  19.             var mode = File.GetUnixFileMode(exePath);
  20.             if ((mode & UnixFileMode.UserExecute) == 0)
  21.             {
  22.                 File.SetUnixFileMode(exePath,
  23.                     mode | UnixFileMode.UserExecute |
  24.                     UnixFileMode.GroupExecute |
  25.                     UnixFileMode.OtherExecute);
  26.                 LogHelper.Info($"已为 {exePath} 设置执行权限");
  27.             }
  28.         }
  29.         catch (Exception ex)
  30.         {
  31.             LogHelper.Error($"设置权限失败: {ex.Message}");
  32.         }
  33.     }
  34.    
  35.     var process = new Process
  36.     {
  37.         StartInfo = new ProcessStartInfo
  38.         {
  39.             FileName = exePath,
  40.             Arguments = arguments,
  41.             UseShellExecute = false,
  42.             RedirectStandardOutput = true,
  43.             RedirectStandardError = true,
  44.             CreateNoWindow = true
  45.         }
  46.     };
  47.    
  48.     LogHelper.Info($"执行命令: {process.StartInfo.FileName} {arguments}");
  49.    
  50.     process.Start();
  51.     var output = await process.StandardOutput.ReadToEndAsync();
  52.     var error = await process.StandardError.ReadToEndAsync();
  53.     await process.WaitForExitAsync();
  54.    
  55.     if (process.ExitCode != 0)
  56.     {
  57.         throw new Exception($"设置剪切板失败: {error}");
  58.     }
  59.    
  60.     LogHelper.Info(output);
  61. }
  62. #endregion
复制代码
View Code关键词:复制,剪切,粘贴文件,文件管理器,Avalonia,Clipboard,dde-file-manager,UOS,Linux

来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!

相关推荐

您需要登录后才可以回帖 登录 | 立即注册