MFC工程中要实现用户登录的代填,了解到可以获取光标焦点窗口然后模拟键盘输入,查了些资料学了学,在此就整理下。
首先是在我们自己的应用程序中该怎样获取有光标闪烁的其他应用程序?即等待输入的焦点窗口
有个GetFocus()函数可获取焦点窗口,但直接调用只能获取本应用程序的焦点窗口,为此我们要用下AttachThreadInput()函数将其他应用程序的线程连接进来,连接后,输入焦点、窗口激活、鼠标捕获、键盘状态及输入队列状态都会进入共享状态了,然后再调用GetFocus()函数获取焦点窗口。MFC中具体代码实现如下:
CWnd *cWnd;
HWND hWnd;
cWnd= GetForegroundWindow();
hWnd=cWnd->m_hWnd;
if (hWnd == this->m_hWnd)
{
//OutputDebugString(L"程序本身窗口!");
return;
}
// Get Target Thread ID and Attach Thread Input
DWORD ProcID;
DWORD ThreadID = GetWindowThreadProcessId(hWnd, &ProcID);
AttachThreadInput(GetCurrentThreadId(), ThreadID, TRUE);
// Get Target Window
hWnd = GetFocus()->m_hWnd;
// Detach Thread Input
AttachThreadInput(GetCurrentThreadId(), ThreadID, FALSE);
获取焦点窗口后,就是模拟键盘输入了
即其次该怎样模拟键盘进行输入?
我们可以直接用keybd_event()函数敲入字符从而循环输入字符串,但因字母数字的编码键值在遇到输入大小写字母、数字键会出现一些非预期的状况,以下即自己遇到的:
//keybd_event('A',0,0,0);
//keybd_event('A',0,KEYEVENTF_KEYUP,0); //输到编辑框为小写a
//keybd_event('a',0,0,0);
//keybd_event('a',0,KEYEVENTF_KEYUP,0); //输到编辑框为1
具体原因大家可以查查看。后来网上有找到人家已封装好的模拟键盘输入函数,具体代码如下:
////////////////模拟键盘输入字符串(包括汉字)////////////////
void SendAscii(wchar_t data, BOOL shift)
{
INPUT input[2];
memset(input, 0, 2 * sizeof(INPUT));
if (shift)
{
input[0].type = INPUT_KEYBOARD;
input[0].ki.wVk = VK_SHIFT;
SendInput(1, input, sizeof(INPUT));
}
input[0].type = INPUT_KEYBOARD;
input[0].ki.wVk = data;
input[1].type = INPUT_KEYBOARD;
input[1].ki.wVk = data;
input[1].ki.dwFlags = KEYEVENTF_KEYUP;
SendInput(2, input, sizeof(INPUT));
if (shift)
{
input[0].type = INPUT_KEYBOARD;
input[0].ki.wVk = VK_SHIFT;
input[0].ki.dwFlags = KEYEVENTF_KEYUP;
SendInput(1, input, sizeof(INPUT));
}
}
void SendUnicode(wchar_t data)
{
INPUT input[2];
memset(input, 0, 2 * sizeof(INPUT));
input[0].type = INPUT_KEYBOARD;
input[0].ki.wVk = 0;
input[0].ki.wScan = data;
input[0].ki.dwFlags = 0x4;//KEYEVENTF_UNICODE;
input[1].type = INPUT_KEYBOARD;
input[1].ki.wVk = 0;
input[1].ki.wScan = data;
input[1].ki.dwFlags = KEYEVENTF_KEYUP | 0x4;//KEYEVENTF_UNICODE;
SendInput(2, input, sizeof(INPUT));
}
void SendKeys(CString msg)
{
short vk;
BOOL shift;
USES_CONVERSION;
wchar_t* data = T2W(msg.GetBuffer(0));
int len = wcslen(data);
for(int i=0;i
if (data[i]>=0 && data[i]<256) //ascii字符
{
vk = VkKeyScanW(data[i]);
if (vk == -1)
{
SendUnicode(data[i]);
}
else
{
if (vk < 0)
{
vk = ~vk + 0x1;
}
shift = vk >> 8 & 0x1;
if (GetKeyState(VK_CAPITAL) & 0x1)
{
if (data[i]>='a' && data[i]<='z' || data[i]>='A' && data[i]<='Z')
{
shift = !shift;
}
}
SendAscii(vk & 0xFF, shift);
}
}
else //unicode字符
{
SendUnicode(data[i]);
}
}
}
/////////////////////////////////////////////////////////////////
即需要输入时直接调用SendKeys()函数即可。
以下为自己写的一个例子:
void CTEditLoginDlg::OnBnClickedOk()
{
// TODO: 在此添加控件通知处理程序代码
PROCESS_INFORMATION pi;
STARTUPINFO si;
memset(&si, 0, sizeof(si));
si.cb = sizeof(si);
si.wShowWindow = SW_SHOW;
si.dwFlags = STARTF_USESHOWWINDOW;
if(!CreateProcess(_T("C:\\Users\\Administrator\\Desktop\\Login.exe"), _T(""), NULL, FALSE, NULL, NULL, NULL, NULL, &si, &pi))
{
AfxMessageBox(_T("打开失败!"), MB_OK | MB_ICONERROR);
return;
}
WaitForInputIdle(pi.hProcess, 10 * 1000);
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
/////////////////////登录代填用户名和密码/////////////////////
CString strUserName=_T("adminstrator");
CString strUserPwd=_T("123456i");
CWnd *cWnd;
HWND hWnd;
cWnd= GetForegroundWindow();
hWnd=cWnd->m_hWnd;
if (hWnd == this->m_hWnd)
{
OutputDebugString(L"程序本身窗口!");
return;
}
// Get Target Thread ID and Attach Thread Input
DWORD ProcID;
DWORD ThreadID = GetWindowThreadProcessId(hWnd, &ProcID);
AttachThreadInput(GetCurrentThreadId(), ThreadID, TRUE);
// Get Target Window
hWnd = GetFocus()->m_hWnd;
// Detach Thread Input
AttachThreadInput(GetCurrentThreadId(), ThreadID, FALSE);
SendKeys(strUserName); //发送用户名到光标所在编辑框
keybd_event(VK_TAB,0,0,0); //按下Tab键使光标跳到下一编辑框
keybd_event(VK_TAB,0,KEYEVENTF_KEYUP,0);
SendKeys(strUserPwd); //发送用户密码到光标所在编辑框
CDialogEx::OnOK();
}
程序执行后如下:
点击确定按钮后启动一个新的应用程序Login.exe,并自动代填进用户名和密码,如下:
注意:这里的Login.exe一定要有闪烁的光标即输入焦点,否则有时输不到正确的栏里。
怎样设置输入焦点?
1.给程序启动起来后要成为输入焦点的控件添加变量
2.在程序实现文件的OnInitDialog()中用添加的变量调用SetFocus()函数,然后一定要返回FAlSE,如下:
m_CEditUserName.SetFocus();
return FALSE; //OnInitialDialog在改变了FOCUS的情况下要返回FALSE
//return TRUE; // 除非将焦点设置到控件,否则返回 TRUE
这样就实现了在其他应用程序中的代填,但如果打开的是个有光标闪烁的有待输入的网页(如163邮箱登陆网页),其中的用户名和密码就代填不进去,还不晓得怎么整,完了再查资料看看了。