7

我正在尝试执行以下操作之一 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);

获取窗口句柄,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(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);
4

2 回答 2

6

您不能可靠地使用SendMessagePostMessage来合成键盘输入。它们只是不是为此而设计的。当键盘输入被接收、处理并转发给适当的接收者时,这些消息(WM_CHARWM_KEYDOWN等)是由较低级别的子系统发出的通知。自己发送或发布这些消息就像是在恶作剧地打电话给某人。

SendKeys(与所有其他输入合成器方法一样,包括SendInput为合成键盘输入而明确设计的功能,并且至少在某些实现中是SendKeys实际使用的功能)仅当您希望接收键盘输入的窗口具有焦点时才有效。在 Windows 中,只有焦点(活动)窗口接收输入事件。

如果您想让它工作(或者 P/Invoking 及其所有相关结构),那么SendKeys可能是要走的路SendInput,但是您确实需要尊重收件人窗口必须具有焦点的警告。否则,它什么也得不到。

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

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

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

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

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

于 2013-03-21T00:02:40.797 回答
3

有很多试验和错误,才能让它工作
这是我之前发布的一些代码,你可能想试一试(并且附加了一些更多信息)......

Pinvoke SetFocus 到特定控件

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

这是一些详细的代码SendInput...

如何将字符串发送到其他应用程序,包括 Microsoft Word

于 2013-03-21T00:44:38.773 回答