VC中的adb通信
2016-01-19 18:19
316 查看
以前的工作中涉及到了VC开发PC软件通过ADB通信与android设备连接的内容,现在简单记录下连接的流程
vc在界面初始化时启动后台工作者线程
这当中以管道的方式与adb创建链接
然后是启动这个工作者线程
关键的是CPipe类的代码
错误类型的定义如下
线程启动之后是创建两个socket
其中CClientSocket类设计如下
同样的CClientAdbSocket代码如下
其中CClientAdbSocket是用来直接发送ADB指令的,例如安装apk,卸载apk等操作。
当这些都创建好了,开始启动adb服务,以下代码就是执行了“adb start-server”指令
接下来开始执行“adb devices”列举设备
接着开始获取设备的序列号“adb get-serialno”
接下来是将服务器apk安装到设备上,如果设备上已预置,此步骤忽略。
然后执行“adb -s forward tcp:12580 tcp:10086””
成功之后发送启动设备上的服务器的指令(就是启动一个apk或者service来响应PC端发来的请求),例如
“adb shell am start -a android.intent.action.MAIN -c android.intent.category.LAUNCHER -n com.android.aide/com.android.aide.aide”
然后启动一个后台服务的代码
在启动之后开始创建socket连接
其中的参数如下
至此和设备的连接已经ok,可以发送相关的指令进行操作
vc在界面初始化时启动后台工作者线程
bool CHPadSuiteDlg::StartThread() { //启动线程 // create events if (hAdbEvent != NULL) { ResetEvent(hAdbEvent); } else { hAdbEvent = CreateEvent(NULL, TRUE, FALSE, NULL); } if (hWriteEvent != NULL) { ResetEvent(hWriteEvent); } else { hWriteEvent = CreateEvent(NULL, TRUE, FALSE, NULL); } if (hCloseEvent != NULL) { ResetEvent(hCloseEvent); } else { hCloseEvent = CreateEvent(NULL, TRUE, FALSE, NULL); } hEventArray[0] = hCloseEvent; // highest priority hEventArray[1] = hAdbEvent; hEventArray[2] = hWriteEvent; theApp.adbCommandPipe.init(); if (theApp.winThread = AfxBeginThread(AdbThread, this)) { return true; } else { //发送失败消息 AfxMessageBox(theApp.GetResString(IDS_FAIL_THREAD)); SendMessage(WM_CLOSE); return false; } }
这当中以管道的方式与adb创建链接
CPipe adbCommandPipe;//管道
然后是启动这个工作者线程
//工作者线程在后台工作,负责向设备发送adb命令 UINT CHPadSuiteDlg::AdbThread(LPVOID pParam) { CHPadSuiteDlg* pHandle = (CHPadSuiteDlg*)pParam; pHandle->bThreadAlive = TRUE; DWORD Event = 0; for (;;) { Event = WaitForMultipleObjects(3, pHandle->hEventArray, FALSE, INFINITE); switch (Event) { case 0: { // Kill this thread. break is not needed, but makes me feel better. pHandle->bThreadAlive = FALSE; theApp.exitThread = true; TRACE(_T("***thread exit \r\n")); AfxEndThread(100); Sleep(100); } break; case 1: { ResetEvent(pHandle->hAdbEvent); TRACE(_T("***thread adb cmd \r\n")); if(theApp.adbcmd) { int rsp = theApp.adbCommandPipe.SendCommand(theApp.adbcmd->cmd,NULL,theApp.adbcmd->waitResponse); if(rsp < 32) { ::SendMessage(theApp.activeWnd->m_hWnd,WM_USER_FAILURE, FAIL_CREATE_PIPE, 0x0); } } } break; case 2: { theApp.bytesRead = 0; ResetEvent(pHandle->hWriteEvent); TRACE(_T("***thread write \r\n")); //int rsp = theApp.adbCommandPipe.SendCommand(theApp.AdbFilePreName + theApp.strCommand,NULL,theApp.waitResponse); int rsp = theApp.adbCommandPipe.SendCommand(theApp.strCommand,NULL,theApp.waitResponse); if(rsp < 32) { ::SendMessage(theApp.activeWnd->m_hWnd,WM_USER_FAILURE, FAIL_CREATE_PIPE, 0); } } break; } } return 0; }
关键的是CPipe类的代码
#include "stdafx.h" #include "HPadSuite.h" #include "HPadSuiteDlg.h" #include "Pipe.h" // CPipe IMPLEMENT_DYNCREATE(CPipe, CWinThread) CPipe::CPipe() { } CPipe::~CPipe() { } BOOL CPipe::InitInstance() { // TODO: perform and per-thread initialization here return TRUE; } int CPipe::ExitInstance() { // TODO: perform any per-thread cleanup here return CWinThread::ExitInstance(); } BEGIN_MESSAGE_MAP(CPipe, CWinThread) END_MESSAGE_MAP() void CPipe::init() { // initialize critical section InitializeCriticalSection(&m_csCommunicationSync); } void CPipe::CancelCmd() { CancelIoEx(hHandleRead, NULL); } /** 要改的地方有这么几个:cb,dwFlags,hStdOutput,hStdError,wShowWindow。 先说cb,他指的是 STARTUPINFO的大小,还是老手法sizeof。 再说wShowWindow,制定了新进程创建时窗口的现实状态,这个属性为 SW_HIDE了,我们要隐藏新建的DOS进程。 hStdOutput和hStdError,标准输出和错误输出的句柄。关键的地方,只要把这两个句柄设置为hWrite,我们的进程一旦有标准输出, 就会被写入刚刚建立的匿名管道里,我们再用管道的hReadPipe句柄把内容读出来写入Edit控件就达到我们的目的了。 这几个关键参数完成了以后,千万别忘了dwFlags。他是用来制定 STARTUPINFO里这一堆参数那个有效的。 既然我们用了hStdOutput,hStdError和wShowWindow那dwFlags就给为 STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES。 现在回到CreateProcess的最后一个参数lpProcessInformation。 这个参数不用自己填了,他是CreateProcess返回的信息,只要给他一个PROCESS_INFORMATION结构事例的地址就行了。 大功高成,管道一端连在了新进程的标准输出端了,一端可以自己用API函数ReadFile读取了。 管道还有问题。把 hWrite给了hStdOutput和hStdError,那么在新的进程启动时就会在新进程中打开一个管道写入端, 而我们在当前进程中使用了 CreatePipe创建了一个管道,那么在当前进程中也有这个管道的写入端hWrite。 这里出现了一个有两个写入端和一个读出端的畸形管道。这样的管道肯定是有问题的。 由于当前进程并不使用写端,因此我们必须关闭当前进程的写端。这样,管道才算真正的建立成功。 */ // CPipe message handlers int CPipe::SendCommand(CString strCommandLine,CString strApplicationName,bool needResponse) { SECURITY_ATTRIBUTES sa; HANDLE hRead,hWrite; sa.nLength = sizeof(SECURITY_ATTRIBUTES); sa.lpSecurityDescriptor = NULL; sa.bInheritHandle = TRUE; // now it critical! EnterCriticalSection(&m_csCommunicationSync); //创建管道 if (!CreatePipe(&hRead,&hWrite,&sa,0)) { TRACE(_T("***Error On CreatePipe()")); return 1; } STARTUPINFO si; PROCESS_INFORMATION pi; si.cb = sizeof(STARTUPINFO); GetStartupInfo(&si); si.hStdError = hWrite; si.hStdOutput = hWrite; si.wShowWindow = SW_HIDE; si.dwFlags = STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES; TCHAR lpCommandLine[maxCommandSize]; memset(lpCommandLine,0 ,sizeof(lpCommandLine)); wcscpy(lpCommandLine,strCommandLine); TCHAR lpApplicationName[maxCommandSize]; memset(lpApplicationName,0 ,sizeof(lpApplicationName)); wcscpy(lpApplicationName,strApplicationName); if(strApplicationName.IsEmpty()) { if (!CreateProcess(NULL,lpCommandLine,NULL,NULL,TRUE,NULL,NULL,NULL,&si,&pi)) { TRACE(_T("***Error on CreateProcess()")); return 1; } } else { if (!CreateProcess(lpApplicationName,lpCommandLine,NULL,NULL,TRUE,NULL,NULL,NULL,&si,&pi)) { TRACE(_T("***Error on CreateProcess()")); return 1; } } TRACE(_T("***")+strCommandLine+_T("\r\n")); CloseHandle(hWrite); hHandleRead = hRead; memset(theApp.buffer,0,sizeof(theApp.buffer)); //需要返回值时 if(needResponse) { BOOL readResult = FALSE; DWORD bytesOfRead = 0; theApp.bytesRead = 0; //强制退出时此值设置为true while (false == theApp.exitThread) { readResult = ReadFile(hRead,theApp.buffer+theApp.bytesRead,maxBufferSize,&bytesOfRead,NULL); if(readResult) { theApp.bytesRead += bytesOfRead; } else break; Sleep(100); } // close forever loop //此处最大存储为4096字节,考虑大数据包的传输 if(theApp.activeWnd) { ::SendMessage(theApp.activeWnd->m_hWnd,WM_USER_RECEIVEDATA, theApp.bytesRead, 0x0000); } } CloseHandle(hRead); CloseHandle(pi.hThread); CloseHandle(pi.hProcess); // release critical section LeaveCriticalSection(&m_csCommunicationSync); return 32; }
错误类型的定义如下
//错误类型 enum { FAIL_INSTALL_SERVICEAPK = 0,//安装服务器端软件失败 FAIL_INSTALL_APK,//安装APK失败 FAIL_START_APK,//启动应用失败 FAIL_UNINSTALL_APK,//卸载APK失败 FAIL_PULL_APK,//导出APK失败 FAIL_CONNECT_SOCKET,//socket连接失败 FAIL_CREATE_PIPE,//创建管道失败 FAIL_FIND_ADB,//未找到ADB.exe FAIL_CREATE_THREAD,//创建线程失败 FAIL_NO_DEVICE,//未找到设备 FAIL_DEVICE_OFFLINE,//设备离线 FAIL_TIMEOUT,//超时 FAIL_RECONNECT,//重新连接 };
线程启动之后是创建两个socket
//创建socket连接,默认连接类型SOCK_STREAM if(!theApp.clientSocket.Create()) { //发送失败消息 AfxMessageBox(theApp.GetResString(IDS_FAIL_CREATESOCKET)); SendMessage(WM_CLOSE); } if(!theApp.clientAdbSocket.Create()) { //发送失败消息 AfxMessageBox(theApp.GetResString(IDS_FAIL_CREATESOCKET)); SendMessage(WM_CLOSE); }
其中CClientSocket类设计如下
CClientSocket::CClientSocket() { } CClientSocket::~CClientSocket() { } // CClientSocket member functions void CClientSocket::OnReceive(int nErrorCode) { // TODO: Add your specialized code here and/or call the base class memset(socketBuff,0,sizeof(socketBuff)); int nRead; nRead = Receive(socketBuff, maxBufferSize); switch (nRead) { case 0: Close(); break; case SOCKET_ERROR: if (GetLastError() != WSAEWOULDBLOCK) { AfxMessageBox (_T("Error occurred")); Close(); } break; default: socketBuff[nRead] = _T('\0'); //terminate the string TRACE(_T("::SendMessage send data\r\n")); ::SendMessage(theApp.m_pMainWnd->m_hWnd,WM_USER_SOCKETRECEIVE, 0x0, nRead); } CSocket::OnReceive(nErrorCode); } void CClientSocket::OnSend(int nErrorCode) { // TODO: Add your specialized code here and/or call the base class CSocket::OnSend(nErrorCode); } void CClientSocket::SendUtf8Data(CString& strCmd) { char utf8Str[maxBufferSize*3]; unsigned short UnicodeStr[maxBufferSize]; char TempBuf[maxBufferSize*4]; memset(utf8Str,0,maxBufferSize*sizeof(char)); memset(UnicodeStr,0,maxBufferSize*sizeof(unsigned short)); short UnicodeStrLen=2*strCmd.GetLength(); unsigned short utf8StrLen; DWORD dwLength;//转换后的UTF-8编码的长度in Bytes memcpy(UnicodeStr,strCmd,UnicodeStrLen); utf8StrLen=6*UnicodeStrLen; memset(utf8Str,0,maxBufferSize*3*sizeof(char)); dwLength=theApp.FromUnicodeToUTF8( utf8Str, utf8StrLen, UnicodeStr, UnicodeStrLen);//Unicode to UTF-8 Send(utf8Str,dwLength,0); } void CClientSocket::SendBinaryData(BYTE* byteData ,int len) { Send(byteData,len,0); }
同样的CClientAdbSocket代码如下
CClientAdbSocket::CClientAdbSocket() { } CClientAdbSocket::~CClientAdbSocket() { } // CClientAdbSocket member functions void CClientAdbSocket::OnReceive(int nErrorCode) { // TODO: Add your specialized code here and/or call the base class memset(socketBuff,0,sizeof(socketBuff)); int nRead; nRead = Receive(socketBuff, maxAdbBufferSize); switch (nRead) { case 0: Close(); break; case SOCKET_ERROR: if (GetLastError() != WSAEWOULDBLOCK) { AfxMessageBox (_T("Error occurred")); Close(); } break; default: socketBuff[nRead] = _T('\0'); //terminate the string TRACE(_T("::SendMessage send data\r\n")); ::SendMessage(theApp.m_pMainWnd->m_hWnd,WM_USER_ADBSOCKETRECEIVE, 0x0, nRead); } CSocket::OnReceive(nErrorCode); } void CClientAdbSocket::OnSend(int nErrorCode) { // TODO: Add your specialized code here and/or call the base class CSocket::OnSend(nErrorCode); } void CClientAdbSocket::SendBinaryData(BYTE* byteData ,int len) { Send(byteData,len,0); }
其中CClientAdbSocket是用来直接发送ADB指令的,例如安装apk,卸载apk等操作。
当这些都创建好了,开始启动adb服务,以下代码就是执行了“adb start-server”指令
case TIMER_START_SERVER: { TRACE(_T("step 1 begin : search decive")); memset(theApp.buffer,0,sizeof(theApp.buffer)); theApp.strContent.Empty(); strDeviceArray.RemoveAll(); theApp.operateStep = STEP_START_SERVER; theApp.waitResponse = false; theApp.strCommand = _T("\"") + theApp.AdbFilePreName + _T("\"") + _T(" start-server"); SetEvent(((CHPadSuiteDlg*)theApp.m_pMainWnd)->hWriteEvent); //SetTimer(TIMER_TIMEOUT,TIME_TIMEOUT_1,NULL); SetTimer(TIMER_CONNECT_SEARCHDEVICE,TIME_LEVEL_200,NULL); } break;
接下来开始执行“adb devices”列举设备
case TIMER_CONNECT_SEARCHDEVICE: { TRACE(_T("step 1 begin : search decive")); memset(theApp.buffer,0,sizeof(theApp.buffer)); theApp.strContent.Empty(); strDeviceArray.RemoveAll(); theApp.operateStep = STEP_SEARCH_DEVICE; theApp.waitResponse = true; theApp.strCommand = _T("\"") + theApp.AdbFilePreName + _T("\"")+ _T(" devices"); SetEvent(((CHPadSuiteDlg*)theApp.m_pMainWnd)->hWriteEvent); SetTimer(TIMER_TIMEOUT,TIME_TIMEOUT_2,NULL); } break;
接着开始获取设备的序列号“adb get-serialno”
theApp.operateStep = STEP_GET_SERIALNO; theApp.waitResponse = true; theApp.strCommand = _T("\"") + theApp.AdbFilePreName + _T("\"")+ _T(" get-serialno"); SetEvent(((CHPadSuiteDlg*)theApp.m_pMainWnd)->hWriteEvent); SetTimer(TIMER_TIMEOUT,TIME_TIMEOUT_2,NULL);
接下来是将服务器apk安装到设备上,如果设备上已预置,此步骤忽略。
然后执行“adb -s forward tcp:12580 tcp:10086””
bool CChildStart::SwitchTcpPort( ) { mProgress.SetPos(30); #ifdef FEATURE_NULTI_DEVICES theApp.strCommand = _T("\"") + theApp.AdbFilePreName + _T("\"") + _T(" -s ") + theApp.ConnectDevice.serialNumber + _T(" forward tcp:12580 tcp:10086"); #else theApp.strCommand = _T("\"") + theApp.AdbFilePreName + _T("\"") + _T(" forward tcp:12580 tcp:10086"); #endif theApp.waitResponse = false; SetEvent(((CHPadSuiteDlg *) theApp.m_pMainWnd)->hWriteEvent); return true; }
成功之后发送启动设备上的服务器的指令(就是启动一个apk或者service来响应PC端发来的请求),例如
“adb shell am start -a android.intent.action.MAIN -c android.intent.category.LAUNCHER -n com.android.aide/com.android.aide.aide”
bool CChildStart::StartActivity() { theApp.operateStep = STEP_START_ACTIVITY; #ifdef FEATURE_NULTI_DEVICES theApp.strCommand = _T("\"") + theApp.AdbFilePreName + _T("\"") + _T(" -s ") + theApp.ConnectDevice.serialNumber + _T(" shell am start -a android.intent.action.MAIN -c android.intent.category.LAUNCHER -n com.android.aide/com.android.aide.aide"); #else theApp.strCommand = _T("\"") + theApp.AdbFilePreName + _T("\"") + _T(" shell am start -a android.intent.action.MAIN -c android.intent.category.LAUNCHER -n com.android.aide/com.android.aide.aide"); #endif theApp.waitResponse = true; SetEvent(((CHPadSuiteDlg *) theApp.m_pMainWnd)->hWriteEvent); return true; }
然后启动一个后台服务的代码
bool CChildStart::StartService() { theApp.operateStep = STEP_START_SERVICE; #ifdef FEATURE_NULTI_DEVICES theApp.strCommand = _T("\"") + theApp.AdbFilePreName + _T("\"") + _T(" -s ") + theApp.ConnectDevice.serialNumber + _T(" shell am broadcast -a NotifyServiceStart"); #else theApp.strCommand = _T("\"") + theApp.AdbFilePreName + _T("\"") + _T(" shell am broadcast -a NotifyServiceStart"); #endif theApp.waitResponse = true; SetEvent(((CHPadSuiteDlg *) theApp.m_pMainWnd)->hWriteEvent); return true; }
在启动之后开始创建socket连接
bool CChildStart::CreateSocket() { if(theApp.clientSocket.Connect(theApp.m_strIP,theApp.m_nPort)) { return true; } else { int lastErr = theApp.clientSocket.GetLastError(); if(WSAEISCONN == lastErr) { //A connect request was made on an already connected socket. return true; } return false; } } bool CChildStart::CreateAdbSocket() { if(theApp.clientAdbSocket.Connect(theApp.m_strAdbIP,theApp.m_nAdbPort)) { return true; } else { int lastErr = theApp.clientAdbSocket.GetLastError(); if(WSAEISCONN == lastErr) { //A connect request was made on an already connected socket. return true; } return false; } }
其中的参数如下
m_strIP = _T("127.0.0.1"); m_nPort = 12580; m_strAdbIP = _T("127.0.0.1"); m_nAdbPort = 5037;
至此和设备的连接已经ok,可以发送相关的指令进行操作
相关文章推荐
- nginx+uwsgi+django 配置2
- Application类
- python string与list互转
- 编译安装mysql5.7.9
- tomcat处理请求导致页面出现ERR_CONNECTION_RESET错误解决方案
- python简单爬虫--转源码
- 1.1 Getting Started-Core Concepts
- PHP变量作用域
- java操作Excel的工具—POI学习
- Android上传文件
- Java内存泄漏发生的情况
- 1.2 Getting Started--Naming Conventions(命名约定)
- C#静态构造函数和非静态构造函数
- Base64编码说明
- <input>表单元素readonly时光标仍然可见
- MyBatis点滴积累
- Struts2.x教程(一) Struts2介绍
- ERROR 1819 (HY000): Your password does not satisfy the current policy requirements
- 头文件里应该写些什么
- 2016/01/19 javascript学习笔记-name属性