2012-05-02 44 views
17

我想知道,擺弄各種與SendInput,SendKeys,PostMessage,SendMessage,SendNotifyMessage,keybd_event等等問題。爲了找到那個......試圖將鍵盤輸入發送到另一個非前景過程是相當挑剔和不可靠的。通過鉤子直接發送擊鍵到另一個進程

我嘗試了一種SendInput方法,在這裏我欺騙Z順序(保持當前窗口在最前面)並快速前臺第三方窗口,發送輸入並重新前景我的窗口。其中最終失敗了,並且不知何故,設法還在我的窗口上處理了按鍵,而不是前景(導致在兩個窗口之間發送和接收的無限循環,直到我設法關閉該過程)。我試過不同的SendMessage和PostMessage的組合。一個用於向下,一個向上,因爲使用向下和向上導致問題,例如兩個PostMessage,導致密鑰在接收窗口上覆制。或兩者的SendMessage,這導致文本輸入問題,但其他功能工作正常。用於keyyd的KeyMessage和PostMessage的SendMessage適用於所有函數,但可靠性大幅下降,並且在關鍵事件中增加了延遲。只有用於keyydown的PostMessage和用於keyup的SendMessage的組合才能夠做到有用的事情,並且可能有5-10%的密鑰註冊失敗率。 SentNotifyMessage也是如此(在可靠性方面,其行爲與SendMessage的行爲基本相同)。

所以基本上,我在惠普的最後,我想知道如何直接向目標窗口注入一個鉤子,並做一些巫術以這種方式發送擊鍵,繞過消息隊列等。一種不會處理全局關鍵事件的方式,只會影響目標窗口。唯一的事情是,當我注射/掛鉤時,我很不知道,所以我轉向你,社區。

這樣做嗎?

+0

發送輸入後,您需要等待一段時間才能讓它在您將自己的進程設置回前臺之前交付。儘管有這個名字,SendInput實際上會*輸入輸入隊列的輸入;如果您的窗口在之後立即獲得焦點,那麼當輸入由系統處理時,它可能會最終進入您的窗口! – BrendanMcK

+0

對於它的價值,將輸入發送到非前臺應用程序確實不受支持;有沒有乾淨可靠的方法來做到這一點。使目標應用成爲前景並使用SendInput是唯一可靠的方法。 SendMessage和PostMessage都有問題。你在這裏試圖解決的更大的問題是什麼 - 你爲什麼試圖首先發送輸入? – BrendanMcK

+0

感謝您的回覆,BrendanMcK。我可以嘗試玩重新前景窗口的時間,但我基本上想避免100%。我需要輸入的原因是,我在多個應用程序和局域網上的物理機器上編寫了用於鍵盤/鼠標輸入的多路複用器。其中用於多盒電子遊戲(通常是MMO和RTS遊戲)。 對我來說,最好的情況下,如果我能弄清楚如何做到這一點,就是直接掛鉤到第三方進程,並以某種方式以這種方式發送輸入。我的選擇是什麼?我應該在哪裏指出我的研究? – Hydra

回答

13

這是一個小的代碼,使您可以發送短信到中背景的應用程序。例如,要發送「A」字符,只需調用sendKeystroke(Keys.A),並且不要忘記使用名稱空間System.windows.forms以便能夠使用Keys對象。

using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Text; 
using System.Runtime.InteropServices; 
using System.Windows.Forms; 

namespace keybound 
{ 
class WindowHook 
{ 
    [DllImport("user32.dll")] 
    public static extern IntPtr FindWindow(string lpClassName, string lpWindowName); 
    [DllImport("user32.dll")] 
    public static extern IntPtr SendMessage(IntPtr hWnd, uint Msg, IntPtr wParam, IntPtr lParam); 
    [DllImport("user32.dll")] 
    public static extern IntPtr PostMessage(IntPtr hWnd, uint Msg, IntPtr wParam, IntPtr lParam); 

    public static void sendKeystroke(ushort k) 
    { 
     const uint WM_KEYDOWN = 0x100; 
     const uint WM_SYSCOMMAND = 0x018; 
     const uint SC_CLOSE = 0x053; 

     IntPtr WindowToFind = FindWindow(null, "Untitled1 - Notepad++"); 

     IntPtr result3 = SendMessage(WindowToFind, WM_KEYDOWN, ((IntPtr)k), (IntPtr)0); 
     //IntPtr result3 = SendMessage(WindowToFind, WM_KEYUP, ((IntPtr)c), (IntPtr)0); 
    } 
} 
} 
+2

PostMessage爲此工作得非常好。使用Spy ++幫助我構建消息的外觀與我的實際鍵盤完全一樣。 – Hydra

+0

剛剛在Windows 10上試過這個,它沒有任何效果。不知道我做錯了什麼,或者它不再工作? – Sam

5

您可能需要解決這個問題,但您可以通過流程發送數據。這可能不適合你的情況,但這是一個想法。

using System; 
using System.Runtime.InteropServices; 
using System.Diagnostics; 

    [DllImport("user32.dll", EntryPoint = "FindWindowEx")] 
    public static extern IntPtr FindWindowEx(IntPtr hwndParent, IntPtr hwndChildAfter,   string lpszClass, string lpszWindow); 
    [DllImport("User32.dll")] 
    public static extern int SendMessage(IntPtr hWnd, int uMsg, int wParam, string lParam); 

    static void Send(string message) 
    { 
     Process[] notepads = Process.GetProcessesByName("notepad"); 

     if (notepads.Length == 0) 
      return; 

     if (notepads[0] != null) 
     { 
      IntPtr child = FindWindowEx(notepads[0].MainWindowHandle, 
       new IntPtr(0), "Edit", null); 

      SendMessage(child, 0x000C, 0, message); 
     } 
    } 

如果不工作,你總是可以做:

System.Threading.Thread.Sleep(1000); 
//User clicks on active form. 
System.Windows.Forms.Sendkeys.Sendwait("<Message>"); 
+0

這正是我需要的。但是,我如何才能知道lpszClass適用於我的流程? – FinnTheHuman

+0

你可以使用Spy ++來找出lpClassName是什麼。如果lpClassName爲NULL,則FindWindow將查找標題與lpWindowName參數相匹配的任何窗口。 (https://msdn.microsoft.com/en-us/library/windows/desktop/ms633499(v=vs.85).aspx) –

相關問題