您的位置:首页 > 其它

VC中的adb通信

2016-01-19 18:19 316 查看
以前的工作中涉及到了VC开发PC软件通过ADB通信与android设备连接的内容,现在简单记录下连接的流程

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,可以发送相关的指令进行操作
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: