我需要在第三方应用程序中模拟按键。假设我有一个需要向Calculator应用程序发送“8”的C#应用程序。我不能使用.Net的SendKeys或win32 api的keybd_event,因为它们都要求窗口是最活跃的窗口,在我的情况下不是这种情况。
这样我就可以调用sendMessage和postMessage。我在过去的三个小时里一直试图得到一些结果,但现在我完全没有希望了。
我有以下内容:
[DllImport("user32.dll")]
public static extern int FindWindow(string lpClassName,string lpWindowName);
[DllImport("user32.dll")]
public static extern int SendMessage(int hWnd, uint Msg, int wParam, int lParam);
[return: MarshalAs(UnmanagedType.Bool)]
[DllImport("user32.dll", SetLastError = true)]
public static extern bool PostMessage(int hWnd, uint Msg, int wParam, int lParam);
private void button1_Click(object sender, EventArgs e)
{
const int WM_KEYDOWN = 0x100;
const int WM_SYSCOMMAND = 0x018;
const int SC_CLOSE = 0x053;
int WindowToFind = FindWindow(null,"Calculator");
int result = SendMessage(WindowToFind, WM_SYSCOMMAND, SC_CLOSE, 0);
Boolean result2 = PostMessage(WindowToFind, WM_SYSCOMMAND, SC_CLOSE, 0);
int result3 = SendMessage(WindowToFind, WM_KEYDOWN,((int)Keys.NumPad7), 0);
Boolean result4 = PostMessage(WindowToFind, WM_KEYDOWN, ((int)Keys.NumPad7), 0);
}
如您所见,我尝试四次与计算器进行通信。使用sendMessage和PostMessage关闭窗口并发送密钥7.没有任何作用。 FindWindow方法的工作原因是我得到了应用程序的处理程序(我甚至尝试自己启动进程并使用process.MainWindowHandler访问它,但没有运气)。没有错误或例外,但它在计算器中没有任何作用。
我也尝试过与记事本完全相同的事情,也没有任何改变。
答案 0 :(得分:12)
你有机会在64位机器上运行吗?如果是这样,我相信那些实际上是hWnds的'int'值(发送/发布的第一个参数,从FindWindow返回值)需要是IntPtr。
经过多次检查后,看起来对于SendMessage和PostMessage,第1,第3和第4个参数应该是IntPtr而不是int(以及所有这些参数的返回值)
因此,正确的签名将是:
[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);
答案 1 :(得分:3)
在CodeProject上有一篇关于这个的好文章: http://www.codeproject.com/KB/cs/SendKeys.aspx
SendKeys实际上是正确的想法,但您需要获取目标窗口的HWND(窗口句柄)。 This MSDN sample显示了如何有效地使用SendKeys,但不是如何发现除最顶层窗口之外的其他任何内容的HWND。
结合这两种技术,使用CodeProject示例找到要定位的应用程序的HWND,然后使用MSDN文章使用SendKeys将键击(或鼠标事件)发送到目标应用程序。
答案 2 :(得分:2)
不直接是您的问题,但SendMessage
和PostMessage
之间的区别在于Send
是一个阻止调用,Post
会立即返回(在接收应用程序处理它之前) )。
MSDN解释了差异:http://msdn.microsoft.com/en-us/library/ms644950(VS.85).aspx
此外,如果您使用Vista而不是.NET 3.0,那也可能是个问题:
已针对.NET Framework 3.0更新了SendKeys类,以便在Windows Vista上运行的应用程序中使用它。 Windows Vista的增强安全性(称为用户帐户控制或UAC)可防止先前的实施按预期工作。
答案 3 :(得分:1)
因为它是记事本窗口内的编辑子窗口。您应该将消息发送到正确的子窗口。这是C:
中的一个工作示例#include <windows.h>
#include <stdio.h>
void main(void) {
STARTUPINFO si;
PROCESS_INFORMATION pi;
HWND mainwnd,editwnd;
char c;
si.cb=sizeof(si);
si.lpReserved=NULL;
si.lpDesktop=NULL;
si.lpTitle=NULL;
si.dwFlags=0;
si.cbReserved2=0;
si.lpReserved2=NULL;
if(!CreateProcess("c:\\windows\\notepad.exe",NULL,NULL,NULL,FALSE,0,NULL,NULL,&si,&pi)) {
printf("Failed to run app");
return;
}
WaitForInputIdle(pi.hProcess,INFINITE);
mainwnd=FindWindow(NULL,"Untitled - Notepad");
if(!mainwnd) {
printf("Main window not found");
return;
}
editwnd=FindWindowEx(mainwnd,NULL,"Edit","");
if(!editwnd) {
printf("Edit window not found");
return;
}
for(c='1';c<='9';c++) {
PostMessage(editwnd,WM_CHAR,c,1);
Sleep(100);
}
}
答案 4 :(得分:1)
这里的解决方案帮助我但我必须编辑它,现在它也更短了:
另外here 虚拟密钥代码
的有用列表 [DllImport("user32.dll")]
public static extern IntPtr FindWindow(string lpClassName, string lpWindowName);
[DllImport("user32.dll")]
static extern bool PostMessage(IntPtr hWnd, UInt32 Msg, int wParam, int lParam);
private void button1_Click(object sender, EventArgs e)
{
const int WM_SYSKEYDOWN = 0x0104;
IntPtr WindowToFind = FindWindow(null, "Calculator");
PostMessage(WindowToFind, WM_SYSKEYDOWN, ((int)Keys.NumPad7), 0);
}