Question

Goal: write a C# app that runs in the background, listens for the key combination Win-V, and when that occurs, pastes the clipboard contents into the current active window (some arbitrary app). Essentially I'm trying to mimic PureText, but I'm not bothering to convert the text to plain text first.

Problem: pasting into the currently active windows is not working.

Details: To listen in the background for key presses I'm using the globalKeyboardHook class from A Simple C# Global Low Level Keyboard Hook. I'm able to catch Win-V events, but I'm not able to send the paste command properly. I can send the paste by using the functions SendKeys.Send or keybd_event. However, they send another "V" press down the pipeline which gets caught by the gkh_KeyDown event and causes multiple paste events to fire.

I'm expecting that I need to use SendMessage or PostMessage, but all my attempts to do that have failed so far. Below is the full code with the last function, SendCtrlV, being the one of interest. The comments explain everything I've tried so far. Can you see what I'm missing?

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");
        }
    }
}
Was it helpful?

Solution

These additional links helped lead me to the answer:

Here's what's working for me:

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;
}

OTHER TIPS

It works, but you must use the TextBox's native window handle if you want it to be effective

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top