.Net Core - copy to clipboard?

前端 未结 6 962
耶瑟儿~
耶瑟儿~ 2020-12-08 04:28

Is it possible to copy something to the clipboard using .Net Core (in a platform-agnostic way)?

It seems that the Clipboard class is missing, and P/Invoking is

6条回答
  •  半阙折子戏
    2020-12-08 04:51

    This project of mine (https://github.com/SimonCropp/TextCopy) uses a mixed approach of PInvoke and command line invocation. it currently supports

    • Windows with .NET Framework 4.6.1 and up
    • Windows with .NET Core 2.0 and up
    • Windows with Mono 5.0 and up
    • OSX with .NET Core 2.0 and up
    • OSX with Mono 5.20.1 and up
    • Linux with .NET Core 2.0 and up
    • Linux with Mono 5.20.1 and up
    • Universal Windows Platform version 10.0.16299 and up

    Usage:

    Install-Package TextCopy
    
    TextCopy.ClipboardService.SetText("Text to place in clipboard");
    

    Or just use the actual code

    Windows

    https://github.com/CopyText/TextCopy/blob/master/src/TextCopy/WindowsClipboard.cs

    static class WindowsClipboard
    {
        public static void SetText(string text)
        {
            OpenClipboard();
    
            EmptyClipboard();
            IntPtr hGlobal = default;
            try
            {
                var bytes = (text.Length + 1) * 2;
                hGlobal = Marshal.AllocHGlobal(bytes);
    
                if (hGlobal == default)
                {
                    ThrowWin32();
                }
    
                var target = GlobalLock(hGlobal);
    
                if (target == default)
                {
                    ThrowWin32();
                }
    
                try
                {
                    Marshal.Copy(text.ToCharArray(), 0, target, text.Length);
                }
                finally
                {
                    GlobalUnlock(target);
                }
    
                if (SetClipboardData(cfUnicodeText, hGlobal) == default)
                {
                    ThrowWin32();
                }
    
                hGlobal = default;
            }
            finally
            {
                if (hGlobal != default)
                {
                    Marshal.FreeHGlobal(hGlobal);
                }
    
                CloseClipboard();
            }
        }
    
        public static void OpenClipboard()
        {
            var num = 10;
            while (true)
            {
                if (OpenClipboard(default))
                {
                    break;
                }
    
                if (--num == 0)
                {
                    ThrowWin32();
                }
    
                Thread.Sleep(100);
            }
        }
    
        const uint cfUnicodeText = 13;
    
        static void ThrowWin32()
        {
            throw new Win32Exception(Marshal.GetLastWin32Error());
        }
    
        [DllImport("kernel32.dll", SetLastError = true)]
        static extern IntPtr GlobalLock(IntPtr hMem);
    
        [DllImport("kernel32.dll", SetLastError = true)]
        [return: MarshalAs(UnmanagedType.Bool)]
        static extern bool GlobalUnlock(IntPtr hMem);
    
        [DllImport("user32.dll", SetLastError = true)]
        [return: MarshalAs(UnmanagedType.Bool)]
        static extern bool OpenClipboard(IntPtr hWndNewOwner);
    
        [DllImport("user32.dll", SetLastError = true)]
        [return: MarshalAs(UnmanagedType.Bool)]
        static extern bool CloseClipboard();
    
        [DllImport("user32.dll", SetLastError = true)]
        static extern IntPtr SetClipboardData(uint uFormat, IntPtr data);
    
        [DllImport("user32.dll")]
        static extern bool EmptyClipboard();
    }
    

    macOS

    https://github.com/CopyText/TextCopy/blob/master/src/TextCopy/OsxClipboard.cs

    static class OsxClipboard
    {
        public static void SetText(string text)
        {
            var nsString = objc_getClass("NSString");
            IntPtr str = default;
            IntPtr dataType = default;
            try
            {
                str = objc_msgSend(objc_msgSend(nsString, sel_registerName("alloc")), sel_registerName("initWithUTF8String:"), text);
                dataType = objc_msgSend(objc_msgSend(nsString, sel_registerName("alloc")), sel_registerName("initWithUTF8String:"), NSPasteboardTypeString);
    
                var nsPasteboard = objc_getClass("NSPasteboard");
                var generalPasteboard = objc_msgSend(nsPasteboard, sel_registerName("generalPasteboard"));
    
                objc_msgSend(generalPasteboard, sel_registerName("clearContents"));
                objc_msgSend(generalPasteboard, sel_registerName("setString:forType:"), str, dataType);
            }
            finally
            {
                if (str != default)
                {
                    objc_msgSend(str, sel_registerName("release"));
                }
    
                if (dataType != default)
                {
                    objc_msgSend(dataType, sel_registerName("release"));
                }
            }
        }
    
        [DllImport("/System/Library/Frameworks/AppKit.framework/AppKit")]
        static extern IntPtr objc_getClass(string className);
    
        [DllImport("/System/Library/Frameworks/AppKit.framework/AppKit")]
        static extern IntPtr objc_msgSend(IntPtr receiver, IntPtr selector);
    
        [DllImport("/System/Library/Frameworks/AppKit.framework/AppKit")]
        static extern IntPtr objc_msgSend(IntPtr receiver, IntPtr selector, string arg1);
    
        [DllImport("/System/Library/Frameworks/AppKit.framework/AppKit")]
        static extern IntPtr objc_msgSend(IntPtr receiver, IntPtr selector, IntPtr arg1, IntPtr arg2);
    
        [DllImport("/System/Library/Frameworks/AppKit.framework/AppKit")]
        static extern IntPtr sel_registerName(string selectorName);
    
        const string NSPasteboardTypeString = "public.utf8-plain-text";
    }
    

    Linux

    https://github.com/CopyText/TextCopy/blob/master/src/TextCopy/LinuxClipboard_2.1.cs

    static class LinuxClipboard
    {
        public static void SetText(string text)
        {
            var tempFileName = Path.GetTempFileName();
            File.WriteAllText(tempFileName, text);
            try
            {
                BashRunner.Run($"cat {tempFileName} | xclip");
            }
            finally
            {
                File.Delete(tempFileName);
            }
        }
    
        public static string GetText()
        {
            var tempFileName = Path.GetTempFileName();
            try
            {
                BashRunner.Run($"xclip -o > {tempFileName}");
                return File.ReadAllText(tempFileName);
            }
            finally
            {
                File.Delete(tempFileName);
            }
        }
    }
    
    static class BashRunner
    {
        public static string Run(string commandLine)
        {
            var errorBuilder = new StringBuilder();
            var outputBuilder = new StringBuilder();
            var arguments = $"-c \"{commandLine}\"";
            using (var process = new Process
            {
                StartInfo = new ProcessStartInfo
                {
                    FileName = "bash",
                    Arguments = arguments,
                    RedirectStandardOutput = true,
                    RedirectStandardError = true,
                    UseShellExecute = false,
                    CreateNoWindow = false,
                }
            })
            {
                process.Start();
                process.OutputDataReceived += (sender, args) => { outputBuilder.AppendLine(args.Data); };
                process.BeginOutputReadLine();
                process.ErrorDataReceived += (sender, args) => { errorBuilder.AppendLine(args.Data); };
                process.BeginErrorReadLine();
                if (!process.WaitForExit(500))
                {
                    var timeoutError = $@"Process timed out. Command line: bash {arguments}.
    Output: {outputBuilder}
    Error: {errorBuilder}";
                    throw new Exception(timeoutError);
                }
                if (process.ExitCode == 0)
                {
                    return outputBuilder.ToString();
                }
    
                var error = $@"Could not execute process. Command line: bash {arguments}.
    Output: {outputBuilder}
    Error: {errorBuilder}";
                throw new Exception(error);
            }
        }
    }
    

提交回复
热议问题