【问题标题】:Send Win API paste cmd from background C# app从后台 C# 应用程序发送 Winapi 粘贴命令
【发布时间】:2012-08-19 13:56:48
【问题描述】:

目标:编写一个在后台运行的 C# 应用程序,侦听 Win-V 组合键,然后将剪贴板内容粘贴到当前活动窗口(一些任意应用程序) .本质上,我是在尝试模仿 PureText,但我不想先将文本转换为纯文本。

问题:无法粘贴到当前活动的窗口中。

详细信息:为了在后台监听按键,我使用了来自 A Simple C# Global Low Level Keyboard HookglobalKeyboardHook 类。我能够捕获 Win-V 事件,但无法正确发送粘贴命令。我可以使用 SendKeys.Sendkeybd_event 函数发送粘贴。但是,他们发送另一个“V”按下管道,该管道被 gkh_KeyDown 事件捕获并导致多个粘贴事件触发。

我预计我需要使用 SendMessagePostMessage,但到目前为止我所做的所有尝试都失败了。下面是完整的代码,其中最后一个函数 SendCtrlV 是我们感兴趣的函数。 cmets 解释了我迄今为止所尝试的一切。你能看到我错过了什么吗?

using System;
using System.IO;
using System.Runtime.InteropServices;
using System.Windows.Forms;
using Utilities;

namespace KeyHookTest
{
    public partial class Form1 : Form
    {
        private bool LWin_down;
        private bool V_down;
        globalKeyboardHook gkh = new globalKeyboardHook();

        [DllImport("user32.dll", CharSet = CharSet.Auto)]
        static public extern IntPtr GetForegroundWindow();

        [DllImport("user32.dll")]
        static extern void keybd_event(byte bVk, byte bScan, uint dwFlags, uint dwExtraInfo);

        [DllImport("user32.dll")]
        private static extern int SendMessage(IntPtr hwnd, int msg, int wParam, int lParam);

        [DllImport("user32.dll")]
        public static extern IntPtr PostMessage(IntPtr hWnd, uint Msg, IntPtr wParam, IntPtr lParam);  

        public Form1()
        {
            InitializeComponent();
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            gkh.HookedKeys.Add(Keys.V);
            gkh.HookedKeys.Add(Keys.LWin);
            gkh.KeyDown += new KeyEventHandler(gkh_KeyDown);
            gkh.KeyUp += new KeyEventHandler(gkh_KeyUp);
        }

        void gkh_KeyUp(object sender, KeyEventArgs e)
        {
            if (e.KeyCode == Keys.LWin)
                LWin_down = false;
            else
                V_down = false;
        }

        void gkh_KeyDown(object sender, KeyEventArgs e)
        {
            if (e.KeyCode == Keys.LWin)
                LWin_down = true;
            else
                V_down = true;

            if (LWin_down && V_down)
            {
                LogDebug("Enter Win+V");

                try
                {
                    SendCtrlV();
                }
                catch { }
            }

        }

        private void SendCtrlV()
        {
            uint KEYEVENTF_KEYUP = 2;
            int KEYDOWN = 0x0100;
            int KEYUP = 0x0101;
            byte KEY_LCONTROL1 = 0x11;
            IntPtr KEY_LCONTROL2 = new IntPtr(0x11);
            byte KEY_V1 = 0x56;
            IntPtr KEY_V2 = new IntPtr(0x56);
            int WM_PASTE1 = 0x302;
            uint WM_PASTE2 = 0x302;

            IntPtr hWnd = GetForegroundWindow();

            // Works, but causes multiple gkh_KeyDown to fire so it's slow and buggy
            /*keybd_event(KEY_LCONTROL1, 0, 0, 0);
            keybd_event(KEY_V1, 0, 0, 0);
            keybd_event(KEY_V1, 0, KEYEVENTF_KEYUP, 0);
            keybd_event(KEY_LCONTROL1, 0, KEYEVENTF_KEYUP, 0);*/

            // Works, but causes multiple gkh_KeyDown to fire so it's slow and buggy
            //SendKeys.Send("^v");

            // Doesn't work, causes UAC prompt
            //SendKeys.Send("{^}v");

            // Doesn't work, nothing gets pasted to the foregroundwindow
            //SendMessage(hWnd, WM_PASTE1, 0, 0);

            // Doesn't work, nothing gets pasted to the foregroundwindow
            //PostMessage(hWnd, WM_PASTE2, IntPtr.Zero, IntPtr.Zero);

            // Doesn't work, nothing gets pasted to the foregroundwindow
            /*SendMessage(hWnd, KEYDOWN, KEY_LCONTROL1, 0);
            SendMessage(hWnd, KEYDOWN, KEY_V1, 0);
            SendMessage(hWnd, KEYUP, KEY_V1, 0);
            SendMessage(hWnd, KEYUP, KEY_LCONTROL1, 0);*/

            // Doesn't work, nothing gets pasted to the foregroundwindow
            /*PostMessage(hWnd, 0x0100, KEY_LCONTROL2, IntPtr.Zero);
            PostMessage(hWnd, 0x0100, KEY_V2, IntPtr.Zero);
            PostMessage(hWnd, 0x0101, KEY_V2, IntPtr.Zero);
            PostMessage(hWnd, 0x0101, KEY_LCONTROL2, IntPtr.Zero);*/
        }

        private void LogDebug(string msg)
        {
            string logpath = Environment.GetEnvironmentVariable("USERPROFILE") + @"\Desktop\KeyHookTest.txt";
            File.AppendAllText(logpath, DateTime.Now.ToString("HH:mm:ss:fff") + ": " + msg + "\r\n");
        }
    }
}

【问题讨论】:

  • IIRC,您不会将 WM_PASTE 消息发送到窗口句柄;您将其发送到要直接将内容粘贴到的控件的句柄。您必须在从GetForegroundWindow 获得的hWnd 上使用EnumChildWindows 才能找到该控件的句柄。 (您不会粘贴到窗口本身,而是粘贴到窗口上的编辑控件中。)
  • 这当然是有道理的。通过一些快速搜索,我没有找到太多关于如何做到这一点的信息,但我会继续朝这个方向寻找。
  • 如果您检查文档中的 EnumChildWindows,鉴于您目前编写的代码,我怀疑您在弄清楚它时会遇到太多麻烦(肯定比我在 C# 中更容易 - 在C 或 Delphi 很容易 )。顺便说一句,这个问题 +1 - 我正忙着写评论,以至于我第一次忘记了。 :-)
  • 也许this link 会有所帮助。我是从this SO question 那里得到的。

标签: c# winforms winapi winforms-interop


【解决方案1】:

这些额外的链接帮助我找到了答案:

这对我有用:

private void SendCtrlV()
{
    IntPtr hWnd = GetFocusedHandle();
    PostMessage(hWnd, WM_PASTE, IntPtr.Zero, IntPtr.Zero);
}

static IntPtr GetFocusedHandle()
{
    var info = new GuiThreadInfo();
    info.cbSize = Marshal.SizeOf(info);
    if (!GetGUIThreadInfo(0, ref info))
        throw new Win32Exception();
    return info.hwndFocus;
}

【讨论】:

    【解决方案2】:

    它可以工作,但如果你想让它生效,你必须使用 TextBox 的原生窗口句柄

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2012-08-14
      • 1970-01-01
      • 1970-01-01
      • 2011-08-25
      • 2012-06-26
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多