110次浏览

C/C++使用DD鼠键模拟达到驱动级模拟操作

DD虚拟键盘虚拟鼠标官方网站 http://www.ddxoft.com/ 里边有SDK库下载, 你只需下载下来
调用即可!

视频效果:

我的计算机是64位, 如果你是32请进入32文件夹!
解压后找到文件夹, 免安装版->64这个文件夹里边有2个dll, DD81200x64.32.dll, DD81200x64.64.dll
我使用的是 DD81200x64.32.dll, 我个人是这么理解的: 这个x64.32 应该是在64位cpu上编写32位程序, 如有不对欢迎纠正!
dll不要选错, 否则无法加载! 一定要根据你的机器情况选择!

然后把dll丢到你项目下, 代码中加载就行了! dll中的函数可以去官网看帮助;

软件流程->用户输入账号密码->模拟输入->判断是否登陆成功 当然我这里只是简单概要! 还是得看下面代码!

#include<Windows.h>
#include<Shlobj.h>
#include<iostream>

#include <OleAcc.h>
#include <atlbase.h>
#include <comdef.h>
#include<string>

#pragma comment(lib,"OleAcc.lib")

#define SAFE_RELEASE(p_) { if (p_ != NULL) p_->Release(); p_ = NULL; }

using namespace std;
typedef int (FAR WINAPI *pDD_btn)(int btn);
typedef int (FAR WINAPI *pDD_whl)(int whl);
typedef int (FAR WINAPI *pDD_key)(int keycode, int flag);
typedef int (FAR WINAPI *pDD_mov)(int x, int y);
typedef int (FAR WINAPI *pDD_str)(char *str);
typedef int (FAR WINAPI *pDD_todc)(int vk);
typedef int (FAR WINAPI *pDD_movR)(int dx, int dy);

static int _erro = 0;
HINSTANCE hdll;//DLL句柄
int login(char* user, char* passworld);
int GetQ();//获取本地QQ

char* TCHAR2char(TCHAR* tchStr)
{
	int iLen = 2 * wcslen(tchStr);
	char* chRtn = new char[iLen + 1];
	_tsetlocale(LC_ALL, _T(""));
	int len = wcstombs(chRtn, tchStr, iLen + 1);
	return chRtn;
}
void GetObjectName(IAccessible* child, VARIANT* varChild, char* objName, int len)
{
	BSTR strTmp;
	HRESULT hr = child->get_accName(*varChild, &strTmp);
	if (S_OK != hr)
	{
		return;
	}
	_bstr_t str = strTmp;
	char* tmp = str;

	strcpy_s(objName, MAX_PATH, tmp);
}
void GetObjectRole(IAccessible* child, VARIANT* varChild, char* objRole, int len)
{
	VARIANT pvarRole;
	DWORD roleId;
	child->get_accRole(*varChild, &pvarRole);

	if (varChild->vt != VT_I4)
	{
		pvarRole.vt = VT_EMPTY;
		return /*E_INVALIDARG*/;
	}
	roleId = pvarRole.lVal;
	UINT   roleLength;
	LPTSTR lpszRoleString;

	// Get the length of the string.
	roleLength = GetRoleText(roleId, NULL, 0);

	// Allocate memory for the string. Add one character to
	// the length you got in the previous call to make room
	// for the null character.
	lpszRoleString = (LPTSTR)malloc((roleLength + 1) * sizeof(TCHAR));
	if (lpszRoleString != NULL)
	{
		// Get the string.
		GetRoleText(roleId, lpszRoleString, roleLength + 1);
	}
	char* tmp = TCHAR2char(lpszRoleString);
	free(lpszRoleString);
	strcpy_s(objRole, MAX_PATH, tmp);
	return /*S_OK*/;

}

void GetObjectClass(IAccessible* child, char* objClass, int len)
{
	HWND htmp;
	LPTSTR strClass;
	strClass = (LPTSTR)malloc(MAX_PATH);
	WindowFromAccessibleObject(child, &htmp);
	if (0 == ::GetClassName(htmp, strClass, MAX_PATH))
	{
		free(strClass);
		return;
	}
	char* tmp = TCHAR2char(strClass);

	strcpy_s(objClass, MAX_PATH, tmp);
	free(strClass);
}

// UI元素的状态也表示成整型形式。因为一个状态可以有多个值,
//例如可选的、可做焦点的,该整数是反映这些值的位的或操作结果。

BOOL GetObjectState(IAccessible* pAcc,
	VARIANT* pvarChild,
	UINT& nState)
	//=============================================================================
{
	BOOL bRet = FALSE;

	nState = 0;

	_ASSERTE(pAcc);
	_ASSERTE(pvarChild);

	if (pAcc && pvarChild)
	{
		VARIANT varState;
		VariantInit(&varState);

		HRESULT hr = pAcc->get_accState(*pvarChild, &varState);

		if (SUCCEEDED(hr) && (varState.vt == VT_I4))
		{
			nState = varState.lVal;
			bRet = TRUE;
		}

		VariantClear(&varState);
	}

	return bRet;
}

BOOL Find(IAccessible* paccParent,
	IAccessible** paccChild,//传出参数
	VARIANT* pvarChild//传出参数
	)
{
	_ASSERTE(paccParent);
	_ASSERTE(paccChild);
	_ASSERTE(pvarChild);

	HRESULT hr;
	long numChildren = 0;//子控件数量
	unsigned long numFetched = 0;//索引
	VARIANT varChild;
	IAccessible* pCAcc = NULL;
	IEnumVARIANT* pEnum = NULL;
	IDispatch* pDisp = NULL;
	BOOL found = false;
	char szObjName[256] = { 0 }, szObjRole[256] = { 0 }, szObjClass[256] = { 0 };


	//得到父亲支持的IEnumVARIANT接口
	hr = paccParent->QueryInterface(IID_IEnumVARIANT, (PVOID*)&pEnum);
	if (pEnum)
	{
		pEnum->Reset();
	}
	//取得父亲拥有的可访问的子的数目
	paccParent->get_accChildCount(&numChildren);
	//cout << endl << "取得父亲拥有的可访问的子的数目: " << numChildren << endl;
	//搜索并比较每一个子ID,找到名字、角色、类与输入相一致的。
	for (long index = 1; (index <= numChildren && !found); index++)
	{

		VariantClear(&varChild);
		pCAcc = NULL;
		//cout << "index: " << index << endl;

		// 如果支持IEnumVARIANT接口我们就调用Next方法,得到下一个子ID
		//以及其对应的 IDispatch 接口
		if (pEnum)
		{
			hr = pEnum->Next(1, &varChild, &numFetched);
			if (!SUCCEEDED(hr))
			{
				found = true;
				break;
			}
		}
		else
		{
			//如果一个父亲不支持IEnumVARIANT接口,子ID就是它的序号
			varChild.vt = VT_I4;
			varChild.lVal = index;
		}

		// 找到此子ID对应的 IDispatch 接口
		if (varChild.vt == VT_I4)
		{

			//通过子ID序号得到对应的 IDispatch 接口
			pDisp = NULL;
			hr = paccParent->get_accChild(varChild, &pDisp);
		}
		else
		{

			//如果父支持IEnumVARIANT接口可以直接得到子IDispatch 接口
			pDisp = varChild.pdispVal;
		}

		// 通过 IDispatch 接口得到子的 IAccessible 接口 pCAcc
		if (pDisp)
		{
			hr = pDisp->QueryInterface(IID_IAccessible, (void**)&pCAcc);
			SAFE_RELEASE(pDisp);
		}

		// Get information about the child
		if (pCAcc)
		{
			//如果子支持IAccessible 接口,那么子ID就是CHILDID_SELF
			VariantInit(&varChild);
			varChild.vt = VT_I4;
			varChild.lVal = CHILDID_SELF;

			*paccChild = pCAcc;
		}
		else
		{
			//如果子不支持IAccessible 接口
			*paccChild = paccParent;

		}
		//跳过了有不可访问状态的元素
		UINT nState = 0;
		GetObjectState(*paccChild, &varChild, nState);

		// check if object is available
		if (nState & (/*STATE_SYSTEM_UNAVAILABLE |*/ STATE_SYSTEM_INVISIBLE /*| STATE_SYSTEM_OFFSCREEN*/))
		{
			SAFE_RELEASE(pCAcc);
			continue;
		}


		//通过get_accName得到Name
		GetObjectName(*paccChild, &varChild, szObjName, sizeof(szObjName));
		//通过get_accRole得到Role
		GetObjectRole(*paccChild, &varChild, szObjRole, sizeof(szObjRole));
		//通过WindowFromAccessibleObject和GetClassName得到Class
		GetObjectClass(*paccChild, szObjClass, sizeof(szObjClass));
		//以上实现代码比较简单,大家自己看代码吧。

		//cout << "Name---" << szObjName << " Role---" << szObjRole << " Calss---" << szObjClass << endl;
		//cout << endl;
		//如果这些参数与输入相符或输入为NULL
		if (strstr(szObjName, "需输验证码") != NULL){
			_erro = -1;
		}

		if (strstr(szObjName, "帐户名或密码不正确") != NULL){
			_erro = -2;
		}


		if (!found && pCAcc)
		{
			// 以这次得到的子接口为父递归调用
			found = Find(
				pCAcc,
				paccChild,
				pvarChild);

			if (*paccChild != pCAcc)
			{
				SAFE_RELEASE(pCAcc);
			}
		}


	}//End for

	// Clean up

	//SAFE_RELEASE(pCAcc);
	SAFE_RELEASE(pEnum);
	VariantClear(&varChild);

	return found;
}


//主函数
int main()
{
	CoInitialize(NULL);//初始化com


	if (!IsUserAnAdmin()){
		cout << "请以管理员身份运行....." << endl;
		getchar();
		return 0;
	}
	if (GetFileAttributes(L"DD81200x64.32.dll") == -1){
		cout << "请检测当前目录DLL是否存在!" << endl;
		getchar();
		return 0;
	}
	hdll = LoadLibrary(L"DD81200x64.32.dll");
	if (hdll == NULL){

		cout << "加载DLL失败!" << "错误代码:" << GetLastError() << endl;
		getchar();
		return 0; // 载入库错误
	}


	char user[10] = { 0 };
	char passworld[16] = { 0 };
	cout << "账号:";
	cin >> user;
	cout << "密码:";
	cin >> passworld;

	login(user, passworld);

	FreeLibrary(hdll);//释放dll
	return 0;
}

int login(char* user, char* passworld)
{
	pDD_btn DD_btn = (pDD_btn)GetProcAddress(hdll, "DD_btn");
	pDD_whl DD_whl = (pDD_whl)GetProcAddress(hdll, "DD_whl");
	pDD_key DD_key = (pDD_key)GetProcAddress(hdll, "DD_key");
	pDD_mov DD_mov = (pDD_mov)GetProcAddress(hdll, "DD_mov");
	pDD_str DD_str = (pDD_str)GetProcAddress(hdll, "DD_str");
	pDD_todc DD_todc = (pDD_todc)GetProcAddress(hdll, "DD_todc");
	pDD_movR DD_movR = (pDD_movR)GetProcAddress(hdll, "DD_movR");


	HWND Qhwnd = NULL;//qq窗口句柄
	RECT r;//QQ窗口矩形
	//判断一下,是否正确获取了函数地址
	if (DD_btn && DD_whl && DD_key && DD_mov && DD_str  && DD_todc && DD_movR){
		cout << "函数地址已全部获取完毕..." << endl;
	}
	else{
		cout << "函数地址获取错误..." << endl;
		getchar();
		return -1; // 获取函数地址错误
	}

	Qhwnd = FindWindow(L"TXGuiFoundation", L"QQ");
	if (!Qhwnd){
		cout << "请先打开QQ登陆界面!" << endl;
		return 0;
	}
	int Q = GetQ();
	if (Q > 0){
		cout << "检测到本地已登陆" << Q << "个QQ, 请先关闭! 再使用本软件!" << endl;
		return 0;
	}

	GetWindowRect(Qhwnd, &r);//获取登陆窗口矩形

	/***************账号输入******************/
	SetCursorPos(r.left + 224, r.top + 255);//移动鼠标到账号位置

	DD_btn(1);//左键按下
	Sleep(200);
	DD_btn(2);//左键放开

	DD_str(user);//输入账号
	/*****************END********************/

	Sleep(500);

	/***************密码输入******************/
	SetCursorPos(r.left + 196, r.top + 289);//移动鼠标到账号位置
	DD_btn(1);//左键按下
	Sleep(200);
	DD_btn(2);//左键放开

	DD_str(passworld); //输入密码
	/*****************END********************/

	SetCursorPos(r.left + 238, r.top + 362);//移动鼠标到账号位置
	DD_btn(1);//左键按下
	Sleep(200);
	DD_btn(2);//左键放开



	IAccessible *paccMainWindow = NULL;
	IAccessible*    paccControl = NULL;
	VARIANT         varControl;


	AccessibleObjectFromWindow(Qhwnd, OBJID_WINDOW, IID_IAccessible, (void**)&paccMainWindow);

	while (true)
	{
		if (GetQ()>0){
			cout << "登陆成功!" << endl;
			break;
		}

		system("cls");
		cout << "登陆中...." << endl;

		Find(paccMainWindow, &paccControl, &varControl);

		if (_erro == -1)
		{
			cout << "请输入验证码!" << endl;
			//.....................
			break;
		}
		else if (_erro == -2)
		{
			cout << "密码错误!!" << endl;
			//.....................
			break;
		}
		

	}

	
	 
	VariantClear(&varControl);//释放
	paccControl->Release();//释放
}
int GetQ()
{
	HWND hWnd = GetDesktopWindow();//获取桌面句柄
	HWND a = GetWindow(hWnd, GW_CHILD);
	char data[100] = { 0 }, data1[11] = { 0 };
	int j = 0;


	while (a)
	{
		GetWindowTextA(a, data, MAX_PATH);
		if (strstr(data, "QQ_"))
		{
			for (int i = 0; i < 10; i++)
			{
				if (isdigit(data[3 + i]))//检查是否为数字
				{
					data1[i] = data[3 + i];//保存起来
				}
			}
			cout << "本地已登录QQ:" << data1 << endl;
			j++;//记录已登陆QQ个数
		}

		a = GetWindow(a, GW_HWNDNEXT);
	}
	if (j > 0){
		return j;//有QQ在本地登陆,返回个数!
	}

	return 0;
}

注意由于用到了MSAA接口, 请先安装.NET4.5库下载地址https://www.microsoft.com/en-us/download/details.aspx?id=42642  

否则验证密码是否正确功能失效, 是否需要输入验证码功能也失效! 还可能引发未知错误!

发表评论

电子邮件地址不会被公开。 必填项已用*标注