【问题标题】:Trigger OS to copy (ctrl+c or Ctrl-x) programmatically触发操作系统以编程方式复制(ctrl+c 或 Ctrl-x)
【发布时间】:2011-02-22 15:01:03
【问题描述】:

我正在开发一个程序来触发剪切和粘贴

我没有问题的粘贴(我只是将一个字符串转储到剪贴板)

事实证明,剪切和/或复制有点困难

我的程序失焦,并且有几个用操作系统注册的热键 CTRL+ALT+2 CTRL +ALT+3等)

我想用它来触发 Windows 复制在焦点窗口中突出显示的任何内容

我试着做一个发送键

SendKeys.Send("^c");

但这似乎可以工作一两次,如果有的话就停止工作。

有没有更好的方法来尝试触发窗口以应对不同窗口上突出显示的内容

【问题讨论】:

    标签: c# windows api sendkeys


    【解决方案1】:

    一种方法是使用 Win32 SendInput 函数。使用SendInput,您必须模拟按键按下和按键向上事件,才能注册完整的按键。要模拟 CTRL+C,你必须这样做:

    • CTRL 按下键
    • C 按下键
    • C 向上键
    • CTRL 向上键

    pinvoke.net 有一些SendInput 用法的示例。需要注意的一个问题是该键是否已被按下。如果按键尚未按下,您可以使用GetAsyncKeyState 仅模拟按键按下事件。

    下面是一些如何模拟 CTRL+C 的示例代码。使用下面的代码,您可以简单地调用 Keyboard.SimulateKeyStroke('c', ctrl: true); 请注意,这就像用户按字面按 CTRL+C 一样,因此活动应用程序将像往常一样运行当此类事件发生时(即,如果正常情况下没有复制任何内容,那么使用此方法也不会复制任何内容)。

    编辑:请参阅下面大卫关于批处理发送的输入的评论。下面的代码应该通过对SendInput 的一次调用发送整个输入事件序列,以避免与真实的用户输入事件交错(和误解)。

    using System;
    using System.Collections.Generic;
    using System.Runtime.InteropServices;
    using System.Threading;
    
    namespace SimulateKeys
    {
        static class Keyboard
        {
            public static void SimulateKeyStroke(char key, bool ctrl = false, bool alt = false, bool shift = false)
            {
                List<ushort> keys = new List<ushort>();
    
                if (ctrl)
                    keys.Add(VK_CONTROL);
    
                if (alt)
                    keys.Add(VK_MENU);
    
                if (shift)
                    keys.Add(VK_SHIFT);
    
                keys.Add(char.ToUpper(key));
    
                INPUT input = new INPUT();
                input.type = INPUT_KEYBOARD;
                int inputSize = Marshal.SizeOf(input);
    
                for (int i = 0; i < keys.Count; ++i)
                {
                    input.mkhi.ki.wVk = keys[i];
    
                    bool isKeyDown = (GetAsyncKeyState(keys[i]) & 0x10000) != 0;
    
                    if (!isKeyDown)
                        SendInput(1, ref input, inputSize);
                }
    
                input.mkhi.ki.dwFlags = KEYEVENTF_KEYUP;
                for (int i = keys.Count - 1; i >= 0; --i)
                {
                    input.mkhi.ki.wVk = keys[i];
                    SendInput(1, ref input, inputSize);
                }
            }
    
            [DllImport("user32.dll")]
            static extern uint SendInput(uint nInputs, ref INPUT pInputs, int cbSize);
    
            [DllImport("user32.dll")]
            static extern short GetAsyncKeyState(ushort vKey);
    
            struct MOUSEINPUT
            {
                public int dx;
                public int dy;
                public uint mouseData;
                public uint dwFlags;
                public uint time;
                public IntPtr dwExtraInfo;
            }
    
            struct KEYBDINPUT
            {
                public ushort wVk;
                public ushort wScan;
                public uint dwFlags;
                public uint time;
                public IntPtr dwExtraInfo;
            }
    
            struct HARDWAREINPUT
            {
                public int uMsg;
                public short wParamL;
                public short wParamH;
            }
    
            [StructLayout(LayoutKind.Explicit)]
            struct MOUSEKEYBDHARDWAREINPUT
            {
                [FieldOffset(0)]
                public MOUSEINPUT mi;
    
                [FieldOffset(0)]
                public KEYBDINPUT ki;
    
                [FieldOffset(0)]
                public HARDWAREINPUT hi;
            }
    
            struct INPUT
            {
                public int type;
                public MOUSEKEYBDHARDWAREINPUT mkhi;
            }
    
            const int INPUT_KEYBOARD = 1;
            const uint KEYEVENTF_KEYUP = 0x0002;
    
            const ushort VK_SHIFT = 0x10;
            const ushort VK_CONTROL = 0x11;
            const ushort VK_MENU = 0x12;
        }
    
        class Program
        {
            static void Main(string[] args)
            {
                Thread.Sleep(3000);
                Keyboard.SimulateKeyStroke('c', ctrl: true);
            }
        }
    }
    

    【讨论】:

    • ding ding ding 看起来我们可能有中奖了!今晚我会测试它并标记它,如果它有效
    • 我在第 10 行收到一个错误,说我不能“设置默认参数”,其中 bool shift = false。那是 .net3.5 的东西吗(我使用的是 express 2008)
    • 这实际上是 .NET 4.0 的东西(默认参数)。如果您想在 .NET 3.5 中执行可选参数,则需要进行多次重载。
    • 这里的弱点是你完全错过了SendInput的重点。它意味着要交给一个结构数组。如果您希望关键事件以正确的顺序到达,而不是与真实事件交错,您需要对 SendInput 进行一次调用,传递所有结构。
    • @DavidHeffernan:你是对的,这是一个非常现实的可能性。我没有更新代码,但我至少添加了一个解释情况的编辑。谢谢。
    【解决方案2】:

    如果您可以从焦点窗口中获取所选文本(可能是更容易解决的问题),那么您最好使用System.Windows.Forms.Clipboard 类的SetText 方法。

    【讨论】:

    • 我不确定是否正在使用其中一个(可能是我),但我将如何设置文本?这就是我遇到麻烦的地方。您能否发布一些代码,因为您的答案可能刚刚飞过我的脑海
    • 从您的编辑来看,这听起来有点困难 ;-) 我没有在 .Net 中这样做过,但我似乎记得在我的 Delphi 时代,如果您知道该控件的句柄具有选定的文本,您可以向该窗口发布消息并取回选定的文本。我也听说过使用 MS UI 自动化来执行此操作,但我从未尝试过。你可以在这里阅读:msdn.microsoft.com/en-us/library/ms747327(v=VS.100).aspx
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多