在c#中将键击发送到应用程序(sendkeys,postmessage,sendmessage都不起作用)

时间:2013-03-20 23:50:38

标签: c# .net windows keyboard-events sendkeys

我正在尝试执行以下操作之一  1.打开所需程序并以编程方式按键  2.找到程序的打开窗口并以编程方式按键  (或者没事)

我尝试了很多SendKeys.SendWait(),PostMessage()和SendMessage()的实现失败。以下是我的代码片段

    //included all these for attempts 
    [DllImport("User32.dll")] 
    static extern IntPtr FindWindow(string lpClassName, string lpWindowName);  

    [DllImport("User32.dll")] 
    static extern int SetForegroundWindow(IntPtr hWnd);

    [DllImport("User32.Dll", EntryPoint = "PostMessageA")]
    static extern bool PostMessage(IntPtr hWnd, uint msg, int wParam, int lParam);

    [DllImport("user32.dll")]
    static extern byte VkKeyScan(char ch);

    [DllImport("user32.dll", CharSet = CharSet.Auto)]
    static extern IntPtr SendMessage(IntPtr hWnd, UInt32 Msg, IntPtr wParam, IntPtr lParam);

获取windowme句柄,sendmessage / postmessage / sendkeys使用的变量

IntPtr ptrOBS = proc.Handle;//this works properly, proc is instantiated properly
//IntPtr ptrOBS = FindWindow(null, "Open Broadcaster Software v0.472b");
SetForegroundWindow(ptrOBS);
const UInt32 WM_CHAR = 0x0102;
const uint WM_KEYDOWN = 0x100;
const int VK_R = 0x52; // taken from http://msdn.microsoft.com/en-us/library/dd375731(v=vs.85).aspx
const int VK_S = 0x53;

SendMessage尝试:

SendMessage(ptrOBS, WM_KEYDOWN, (IntPtr)VK_R, (IntPtr)1);//tried both WM_CHAR and WM_KEYDOWN

PostMessage尝试:

string message = "rs";
bool sent = PostMessage(ptrOBS, WM_KEYDOWN, VkKeyScan(message[0]), 0);

SendKeys尝试:

SendKeys.SendWait("{r}");

在父窗口(应用程序)和子窗口上尝试SetFocus(由按键触发的按钮即可尝试发送):

static void SetFocus(IntPtr hwndTarget, string childClassName)
    {
        // hwndTarget is the other app's main window 
        // ...
        IntPtr targetThreadID = GetWindowThreadProcessId(hwndTarget, IntPtr.Zero); //target thread id
        IntPtr myThreadID = GetCurrentThread(); // calling thread id, our thread id
        try
        {
            bool lRet = AttachThreadInput(myThreadID, targetThreadID, -1); // attach current thread id to target window

            // if it's not already in the foreground...
            lRet = BringWindowToTop(hwndTarget);
            SetForegroundWindow(hwndTarget);

            // if you know the child win class name do something like this (enumerate windows using Win API again)...
            IntPtr hwndChild = (IntPtr)1183492;//(IntPtr)EnumAllWindows(hwndTarget, childClassName).FirstOrDefault();

            if (hwndChild == IntPtr.Zero)
            {
                // or use keyboard etc. to focus, i.e. send keys/input...
                // SendInput (...);
                return;
            }

            // you can use also the edit control's hwnd or some child window (of target) here
            SetFocus(hwndChild); // hwndTarget);
            SendKeys.SendWait("{r}");
        }
        finally
        {
            SendKeys.SendWait("{r}");
            bool lRet = AttachThreadInput(myThreadID, targetThreadID, 0); //detach from foreground window
            SendKeys.SendWait("{r}");
        }
    }

对于NSGaga:

    string windowName = "Open Broadcaster Software v0.472b";
IntPtr outerPtr = FindWindow(null, windowName);
IntPtr ptrOBS = (IntPtr)527814;//button that im trying to trigger keypress on
SetForegroundWindow(outerPtr);
SetForegroundWindow(ptrOBS);
SetFocus(outerPtr, "OBSWindowClass");//SetFocus(ptrOBS, "Button");
const UInt32 WM_CHAR = 0x0102;
const int VK_R = 0x52; // taken from http://msdn.microsoft.com/en-us/library/dd375731(v=vs.85).aspx
const int VK_S = 0x53;

//SetForegroundWindow(ptrOBS);
System.Threading.Thread.Sleep(3000);
SendKeys.SendWait("{r}");
SendMessage(outerPtr, WM_KEYDOWN, (IntPtr)VK_R, (IntPtr)1);
PostMessage(outerPtr, WM_KEYDOWN, VkKeyScan('r'), 0);

2 个答案:

答案 0 :(得分:5)

You cannot reliably use SendMessage and PostMessage for synthesizing keyboard input。它们不是为此而设计的。这些消息(WM_CHARWM_KEYDOWN等)是在接收,处理并转发给相应收件人的键盘输入时由较低级子系统引发的通知。自己发送或发布这些消息就像恶作剧一样。

SendKeys(与所有其他输入合成器方法一样,包括明确设计用于合成键盘输入的SendInput函数,至少在某些实现中,SendKeys实际上在引擎盖下使用)仅当您希望接收键盘输入的窗口具有焦点时才起作用。在Windows中,只有聚焦(活动)窗口才会接收输入事件。

所以SendKeys可能是你要去做的工作方式(或者P / Invoking SendInput及其所有相关结构),但是你确实需要尊重收件人窗口必须具有焦点的警告。否则,它不会得到任何东西。

从您的示例代码看,您尝试使用SetForegroundWindow函数来满足此前提条件。不幸的是,你传递的是一个无效的值,并没有做任何可能提醒你注意这个错误的错误检查。具体来说,这段代码是错误的:

IntPtr ptrOBS = proc.Handle;//this works properly, proc is instantiated properly
SetForegroundWindow(ptrOBS); // WRONG, ptrOBS is not a window handle

即使我信任你ptrOBS正确初始化,这也使它成为进程的有效句柄,这与的有效句柄完全不同窗口。除了明显的名义差异外,进程可以有多个窗口,只有一个窗口可以有焦点(即“在前台”)。

在调用SetForegroundWindow之前,您需要获取特定窗口的句柄,并且假设我们知道某个进程可以有多个窗口,这可能很棘手。您需要一些可靠的方法来确定您想要的窗口。很多人通过将窗口的名称硬编码为字符串来实现这一目标,这种方法很有效,直到目标应用程序被重新编译并且此实现细节发生变化。我能想到的唯一防弹方法是让用户点击目标窗口和你的代码来检索当前鼠标指针下的窗口的句柄。

当然,所有这些都假定您已经观察到SetForegroundWindow的使用限制,这些限制在链接的SDK文档的“备注”部分中列举。

答案 1 :(得分:2)

为了让它工作,有很多反复试验 这里是我之前发布的一些代码,您可能想尝试一下(并附上更多信息)......

Pinvoke SetFocus to a particular control

首先尝试设置焦点(使用提到的机制) - 然后使用SendKeysSendInput

以下是SendInput ...

的一些详细代码

How to send a string to other application including Microsoft Word