[转]仿91助手的PC与android手机通讯

仿91助手的PC与android手机通讯

原文

  知道91助手和豌豆莢吧? 说到这两个东西,最让人好奇的应该是就是和手机的交互了。我之前有研究过电脑和安卓的交互,基本功能已经走通了,在这里我想分享一下。 初初看这个问题觉得很简单,然后如果你有点计算机基础的话深入想一下却发现有很多实现上的空白。

---------------------------------------------------------上面是废话,进入正题。

  1. 检测设备插入
  2. 检测是否是手机
  3. 检测是否已经安装驱动
  4. 自动安装手机驱动
  5. 获取手机信息

一、检测设备插入

首先要解决的问题就是自动在设备插入电脑时作出响应。

这个问题得解决方法就是:WM_DEVICECHANGE 事件。

这个事件是一个全局事件,不需要预先向系统注册之类的,只要你的程序有窗口就能随时响应设备变更事件WM_DEVICECHANGE,当然全局事件是进程的Top-Level窗口才能收的到,如果你想在子窗口或者模态窗口中直接收到的话可以考虑使用RegisterDeviceNotification注册一下。

事件的WPARAM参数包含了设备更新的类型,设备变更(Device Change)有很多种情况的嘛,有插入、移除、驱动安装成功神马神马的。类型种类在MSDN中有说,如下:

  • DBT_CONFIGCHANGECANCELED
  • A request to change the current configuration (dock or undock) has been canceled.
  • (设备设置取消,我还没怎么研究过这个设置的问题,不过这个跟我现在讲的主题都没关系的。)
  • DBT_CONFIGCHANGED
  • The current configuration has changed, due to a dock or undock.
  • (设备设置变更)
  • DBT_CUSTOMEVENT
  • A custom event has occurred.
  • (这个只是告诉你,设备驱动发出了一个消息)
  • DBT_DEVICEARRIVAL
  • A device or piece of media has been inserted and is now available.
  • (设备或者多媒体插入)
  • DBT_DEVICEQUERYREMOVE
  • Permission is requested to remove a device or piece of media. Any application can deny this request and cancel the removal.
  • (用戶请求弹出设备,返回TRUE允许弹出,返回BROADCAST_QUERY_DENY拒绝弹出,这就是为什么有些时候会发现U盘死活弹不出,非得强拔,这是因为有些进程一直拒绝弹出。)
  • DBT_DEVICEQUERYREMOVEFAILED
  • A request to remove a device or piece of media has been canceled.
  • (请求弹出失败)
  • DBT_DEVICEREMOVECOMPLETE
  • A device or piece of media has been removed.
  • (请求成功)
  • DBT_DEVICEREMOVEPENDING
  • A device or piece of media is about to be removed. Cannot be denied.
  • (强制弹出U盘,这个在360的强制弹出USB时会收到的了)
  • DBT_DEVICETYPESPECIFIC
  • A device-specific event has occurred.
  • (这个是某些个性设备自定义的消息的方法了,自定义部分在LPARAM指针指向的区域中)
  • DBT_DEVNODES_CHANGED
  • A device has been added to or removed from the system.
  • (DevNodes就是设备管理器里面显示的那棵树的节点,这个跟DBT_DEVICEARRIVAL有一点点区别,因为add有可能是因为你新装了某些驱动产生的消息。另外提个醒SAMSUNG手机插入时就是很扑街的收不到DBT_DEVICEARRIVAL类型,只能收到这个。。。)
  • DBT_QUERYCHANGECONFIG
  • Permission is requested to change the current configuration (dock or undock).
  • (请求修改设备设置)
  • DBT_USERDEFINED
  • The meaning of this message is user-defined.
  • (这个类型主要是给用户一个自定义的方法,上面DBT_DEVICETYPESPECIFIC是设备自定义的,这个主要是进程通过BroadcastSystemMessage 来广播的,我还没怎么用过这个玩意。)

好吧,检测设备插入的问题解决了,后面是判断设备是否是手机了。

二、检测是否是手机

上一篇日志说了如何响应设备插入,但是设备有很多中,多媒体设备,鼠标键盘什么的都是,那如何判断是不是USB设备或者是手机插入呢? 这里就介绍一下我自己的研究结果,当然我没有去研究过苹果设备,但是按道理是类似的。

这里是我自己本人的思路,不一定是最好的方法,如果发现更好的方法我会再拿出来,当然如果你发现更好的方法的话可以留言告诉我。

我的思路是枚举USB设备,并且检查设备的兼容ID和硬件ID

枚举用到的方法包括 SetupDiGetClassDevs 和 SetupDiEnumDeviceInfo, 这两个方法可以在MSDN2008里面查到。

这里简单说明一下用法:

//获取设备信息句柄

HDEVINFO hDevInfo = SetupDiGetClassDevs(NULL,L"USB" ,NULL,DIGCF_ALLCLASSES|DIGCF_PRESENT);

//获取设备信息数据

SP_DEVINFO_DATA deviceInfoData;

deviceInfoData.cbSize = sizeof(SP_DEVINFO_DATA);

for(int i = 0;SetupDiEnumDeviceInfo(hDevInfo,i,&deviceInfoData);i++) //对USB设备集进行枚举

{

....

}

上面是枚举USB设备的思路,下面讲一下如何分辨USB设备是否就是手机

这里我的思路是判断 兼容ID 和 硬件ID , 这两个ID可以在设备管理器中看得到如下(写这篇日志时我没有android手机,所以我截的图不是手机信息,只是告诉你这么一样东西,你可以自己插入手机试验一下):

有一部分手机直接判断 兼容ID 是否是"usb\\class_ff&subclass_42"即可,

但是有些手机的兼容ID不是这个串,那就比较麻烦了,需要匹配 硬件ID ,但是 硬件ID
不是固定的,好像是跟手机的硬件有关,但是同一款手机型号是一样的,有些品牌如vivo是一个系列都基本一样的硬件ID,甚至现在市面上的很多山寨手机直
接就是用HTC的一个硬件ID(哈哈,那时候我去手机店采集硬件ID的时候就感概现在的山寨不硬件识别的ID都直接copy了,这个可能是商业问题,我也
不懂.)

获取兼容ID的方法,使用上面枚举获得的HDEVINFO句柄和SP_DEVINFO_DATA数据调用SetupDiGetDeviceRegistryProperty方法:

WORD dataType= 0;

DWORD buffSize = 0;

SetupDiGetDeviceRegistryProperty(hDevInfo,&deviceInfoData,SPDRP_COMPATIBLEIDS,&dataType,NULL,buffSize,&buffSize);

int err = GetLastError();

if(err != ERROR_INSUFFICIENT_BUFFER)

return;

LPTSTR szCompatibleID = (LPTSTR)LocalAlloc(LPTR,buffSize+1);

SetupDiGetDeviceRegistryProperty(hDevInfo,&deviceInfoData,SPDRP_COMPATIBLEIDS,&dataType,(PBYTE)szCompatibleID ,buffSize,&buffSize);

//szCompatibleID 即是兼容ID

如果 兼容ID == "usb\\class_ff&subclass_42" 就直接可以知道这个是手机设备了(注意要兼容ID的大小写不确定的)

如果 兼容ID != "usb\\class_ff&subclass_42" 那么就要匹配硬件ID了,获取硬件ID的方法和获取兼容ID的方法类似:

WORD dataType= 0;

DWORD buffSize = 0;

SetupDiGetDeviceRegistryProperty(hDevInfo,&deviceInfoData,SPDRP_HARDWAREID,&dataType,NULL,buffSize,&buffSize);

int err = GetLastError();

if(err != ERROR_INSUFFICIENT_BUFFER)

return;

LPTSTR szHardwareID = (LPTSTR)LocalAlloc(LPTR,buffSize+1);

SetupDiGetDeviceRegistryProperty(hDevInfo,&deviceInfoData,SPDRP_HARDWAREID,&dataType,(PBYTE)szHardwareID ,buffSize,&buffSize);

//szHardwareID 即是硬件ID

硬件ID 的样子大概是: VID_1234&PID_4321  (1234,4321根据设备有差异,其中VID代表Vendor ID(厂家ID) , PID代表Product ID(产品ID)),VID基本一个厂商. 这个需要收集,但是网上好像有一些VID_PID大全可以满足一般使用.

(其中VID只能判断厂商,有些手机生产商也有别的设备产品,好像索尼有手机也有相机,所以不能轻判哦.

OK,手机判断完成,后面是驱动安装的介绍,手机插入后不一定有驱动,需要有驱动才能进行PC操作手机的功能.

三、检测是否已经安装驱动

上一篇日志说到判断是否是手机设备,但是要与手机进行通讯就必须有驱动程序,否则只能当做“便携储存设备”使用,只能往里面放文件,也许你已经满足了,但 是你想一下91助手只是给你提供存放文件那么简单吗?如果是的话91助手还有鸟用啊?因为我们直接打开“我的电脑”就能打开这个类似U盘的东西了.  再想一想,如果你的程序可以跟手机说"我给个apk你,你安装一下",然后你的手机就装上去了,那不就方便了吗? 这才是卖点~

好,上面都是废话----------------------------------------------------------------------------------------------------------------------

上一篇日志中说到的枚举过程只是判断是否有手机的话似乎太浪费了. 所以这里要提前交代一下再枚举过程发现了手机设备要记录下该设备的
"实例ID" ,这个和"兼容ID"、"硬件ID"不一样,前者是电脑给设备分配的ID便于电脑对I/O设备的管理,而后两者是设备本身的属性信息。

获取实例ID的方法:

	LPTSTR szInstanceID = NULL

WORD iBuffSize = 0;

SetupDiGetDeviceInstanceId(hDevInfoSet,&deviceInfoData,szInstanceID ,iBuffSize,&iBuffSize ); //获取实例ID的buff需要的大小, hDevInfoSet和deviceInfoData

int err = GetLastError();

if(err != ERROR_INSUFFICIENT_BUFFER)

return;

szInstanceID = (LPTSTR)LocalAlloc(LPTR,buffSize*sizeof(WCHAR)); SetupDiGetDeviceInstanceId(hDevInfoSet,&deviceInfoData,szInstanceID ,iBuffSize ,&iBuffSize ); //获取实例ID

要判断设备是否有安装驱动使用到两个方法:CM_Locate_DevNode 和 CM_Get_DevNode_Status ,头文件#include <cfgmgr32.h>,主要实现如下:

DEVINST deviceInstance;

if (CM_Locate_DevNode(&deviceInstance,szInstanceID ,CM_LOCATE_DEVNODE_NORMAL) == CR_SUCCESS) //获取设备ID对应的设备实例句柄

{

DWORD tatus;

DWORD problemNumber;

if (CM_Get_DevNode_Status(&status,&problemNumber,deviceInstance,0) == CR_SUCCESS)

//获取设备状态和设备状态细节

{

if (!(status&DN_HAS_PROBLEM)) //判断设备是否存在问题,代表驱动已安装

{

//设备无异常,就是说驱动正常

}

else

{

if (problemNumber == CM_PROB_DRIVER_FAILED_PRIOR_UNLOAD

|| problemNumber == CM_PROB_DRIVER_FAILED_LOAD)

{

//设备驱动加载不成功

}

else

{

//有不明原因,可以归结为没安装驱动

}

}

}

}

好吧,驱动是否安装的判断就这样子。一点都不麻烦。

四、自动安装手机驱动

  上一节讲到检查驱动安装情况,那么如果遇到没安装手机驱动的话是没办法和手机进行通讯的(除非你是要直接把文件拷贝到手机目录下,好像txt,视频,音乐的话是不用考虑驱动都可以的,当然有驱动这几种文件的拷贝也会是更方便的。)

好吧,开始说一下安装驱动的实现吧:

首先驱动也是分厂商和机型的(当然好像是有万能驱动这个东西的,但是我测试过万能驱动不是完全适合所有手机的),那么说到厂商和机型,应该就会想到VID
和PID了,前面说过VID代表厂商PID代表型号。
那么就知道用什么来匹配驱动了,当然说到匹配的话就说明驱动有很多,虽然有些厂商的所有机型或者某一系列的机型是使用同一个驱动就行了,但是也有很多例外
的(这就说明,自己弄一个仿91助手的东西还是做来自己玩玩的,要做成商业软件的话你还得去收集驱动呢。)

在这里说一下题外话,怎么收集驱动呢? 官网?手机自带光碟?
 这些方法都很蛋疼的,你自己去实践一下就知道了,除非有专门人员帮你收集,否则真的很蛋疼。而我以前的收集驱动的方法就是用豌豆荚插手机,然后豌豆荚会
在C盘的临时文件夹中存放该VID/PID对应的驱动准备安装,然后拷贝下来(-
-),等驱动安装完成豌豆荚会把驱动文件删除,所以你要在豌豆荚询问“是否安装驱动”的时候别确定也别取消,然后去拷贝驱动吧,这里你可能需要用到一个辅
助软件(Everything)帮你定位这个临时文件夹,这个是一个本地文件搜索软件,搜索速度别拿windows的来比,因为windows那个根本没
法比,那个快是瞬间~~~,十分high。
----------想到就心酸的操作。

在收集驱动的过程中,我发现了驱动有两种,一种就是exe的例如SAMSUNG的就是这样,另外一种就是dll的,如下图:

1、exe类型:

2、dll类型,有两层目录,首层是.inf硬件信息文件和.cat安全文件,次层是.dll动态链接库文件:

-------------------------------------------------------------------------------------------

驱动类型和匹配的说完,现在该说如何安装了吧。

第一种类型,exe文件直接运行就行,可以用WinAPI中的CreateProcess()来启动,这个用户交互会多一点,因为会很多“确
认”“下一步”的东西,但是这个我们无法控制,用91助手的时候你也会遇到有些驱动就是要点下一步、下一步,有些就是确定要安装后就后台静默安装了。

第二种类型,用户基本不需要交互,直接后台默认就可以完成安装,我们主要也是要处理这一种。

主要是用到UpdateDriverForPlugAndPlayDevices(HWND hwndParent,LPCWSTR HardwareId,LPCWSTR FullInfPath,DWORD
InstallFlags,PBOOL bRebootRequired);这
个API,不敢相信还有那么长名称的API。这个API第一个参数可以为空句柄,第二个参数是硬件ID(这个在第二节讲过,和兼容ID一起的那个),第三
个是.inf文件的全路径(就是上面第二张图里面那个),第四个参数填0就行(有需要的可以再详细研究这个参数值),第四个要传一个BOOL型的指针进去
等API返回一个是否要重启的值告诉你这个驱动安装后要真正运作起来是否要重启。

五、使用adb获取手机信息

到这里,我知道的就差不多了。后面就是跟android手机的命令传递了。这些操作主要使用到android工具包---adb(android debug bridge)。这个东西是google提供的,网上有大量的教程,使用起来很简单。

介绍一下获取android手机信息的基本流程:

 WinExec("adb -d devices",SW_HIDE); Sleep(1200);

SECURITY_ATTRIBUTES sa;  

sa.nLength = sizeof(SECURITY_ATTRIBUTES);  

char buffer[1024] = {0}; //用1K的空间来存储输出的内容,只要不是显示文件内容,一般情况下是够用了。 

DWORD recvLen;  

DWORD occupyLen = 0;

TCHAR command[1024] = _T("adb -d shell getprop ro.product.brand");  //获取厂商名称

                                //_T("adb -d shell getprop ro.product.model")    //设备型号
                                //_T("adb -d shell getprop ro.build.version.release")    //android版本
                                //_T("adb -d shell dumpsys iphonesubinfo"}    //IMEI码
                                //_T("adb -d shell cat /sys/class/net/wlan0/address")    //MAC地址

HANDLE hRead,hWrite;  

if (!CreatePipe(&hRead,&hWrite,&sa,0))  

return 0;

PROCESS_INFORMATION pi;

STARTUPINFO si; 

si.cb = sizeof(STARTUPINFO); 

GetStartupInfo(&si);  

si.wShowWindow = SW_HIDE;  

si.dwFlags = STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES;

si.hStdError = hWrite; //把子进程的标准错误输出重定向到管道输入  

si.hStdOutput = hWrite; //把子进程的标准输出重定向到管道输入  

si.hStdInput = hRead; //把子进程的标准输入重定向到管道输出

TCHAR command[1024] = _T("adb -d shell getprop ro.product.brand");  //获取厂商名称

if (! CreateProcess(NULL, command,NULL,NULL,TRUE,NULL,NULL,NULL,&si,&pi)) // 启动进程以调用ADB

{  

CloseHandle(hWrite);  

CloseHandle(hRead);  

return FALSE;  

}  

CloseHandle(hWrite);

if(WaitForSingleObject(pi.hProcess,800) == WAIT_TIMEOUT) //800ms的处理等待时间.

{

TerminateProcess(pi.hProcess,WAIT_TIMEOUT);

CloseHandle(pi.hProcess);

return FALSE;

}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

if(ReadFile(hRead,buffer,200,&recvLen,NULL)) //IMEI码的要做特殊处理。

{

CStringA strIMEI = buffer;

if(strIMEI.Find("error:") == -1)

{

strIMEI = strIMEI.Mid(strIMEI.FindOneOf("=")+2);

strcpy_s(buffer,1024,strIMEI.GetBuffer());

strIMEI.ReleaseBuffer();

occupyLen=strIMEI.GetLength();}

}

}

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

ReadFile(hRead,buffer,1024,&occupyLen,NULL); //其他信息直接返回读取到的东西就行。

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

buffer[occupyLen-3] = ‘\0‘;

CloseHandle(hRead);

CloseHandle(pi.hThread);

CloseHandle(pi.hProcess);

TerminateProcess(pi.hProcess,0);

回到目录

时间: 2024-10-10 00:29:22

[转]仿91助手的PC与android手机通讯的相关文章

简单实现Android手机“全局可调试”(ro.debuggable = 1)的方法【锤子坚果3】

在Android真机上调试程序有一个前提,就是这个apk包必须有 debuggable=true 的属性才行.而除了自己开发的apk能够控制打包属性之外,其他的程序发行之后显然不会设这个值为 true 的(不然随随便便就能被Debug ,岂不是很没安全感 ).为了调试这些第三方的apk,我们可以从整个手机系统入手 —— 因为除了每个apk中的 debuggable 标志以外,这个标志还可以在系统中全局指定,换句话说,只要把系统里的 debuggable 值设为true,那么不管apk的这个属性是

ORB-SLAM2 运行 —— ROS + Android 手机摄像头

转载请注明出处,谢谢 原创作者:Mingrui 原创链接:https://www.cnblogs.com/MingruiYu/p/12404730.html 本文要点: ROS 配置安装 解决 sudo rosdep init 报错 Website may be down. ORB-SLAM2 ROS 配置安装 解决报错 DSO missing from command line Android 手机摄像头与 PC 进行基于 ROS 的通信 手机摄像头标定 采集标定图像 OpenCV sampl

91助手SDK快速入门

91助手SDK介绍 91助手SDK是方便同时拥有PC端和移动端的开发者,实现在PC上快速为移动设备安装移动端应用功能的产品.最新版的91助手SDK具备以下功能: 1.监听设备接入 调用此接口,会监听设备接入PC,并连接设备(支持iOS和Android). 应用示例:接入移动设备,提示用户(弹窗等UI资源需自行开发) 2.获取设备信息 调用此接口,可获得设备的指定信息. 应用示例:弹窗显示接入设备信息 3.获取iOS设备越狱状态 调用此接口可以获取到接入的iOS设备的越狱状态. 应用示例:弹窗显示

91助手 上传pdf到 ibooks

因为嫌itunes麻烦,搜索了下,可以通过91助手 上传pdf到 ibooks,亲测可用. 有些TX会问,为什么一定要用ibooks,用Quickoffice不是连word等一众都搞定吗?我用了后发现,有些大的PDF文件,Quickoffice有闪退的问题,另外可以打开的,阅读时,跳转也没有ibook方便. 参考,见:http://jingyan.baidu.com/article/86f4a73e39ac1037d6526923.html 工具:91助手

使用多字节字符集的跨平台(PC、Android、IOS、WP)编码/解码方法

随着移动端的发展,跨平台已成为通讯架构设计的重要考虑因素,PC.Android.IOS.WP等跨多平台间的数据通讯,必然要解决字符编码/解码的问题. 多字节字符集MBCS不是跨平台的首选字符集,面向跨平台.国际化的推荐字符集肯定是UNICODE. 写VC的人都知道,在以前VC++6.0中默认的字符集是多字节字符集,而VS2005及以后默认的字符集是Unicode,VS2013中默认不再对多字节字符串进行支持. 但对很多较早的服务端项目,依然使用的是多字节字符集,不过使用多字节字符集依然可以实现跨

[Android]PC控制Android终端-RealVNC2DroidVNCserver

制Android手机 - Droid VNC Server使用方法 WM系统有许多PC端控制手机的工具,像Pocket Controller等,通过电脑鼠标键盘直接操作手机,省去了来回切换的麻烦.Android系统当然不能落后,使用VNC工具同样可以达到电脑控制手机的目的,需要两个工具:手机端的Droid VNC Server.电脑端的VNC Viewer. 1.手机端 - Droid VNC Server电子市场搜索Droid VNC Server,下载安装,最新版本0.991不好意思,没有A

[email&#160;protected]:在PC屏幕上显示Android手机屏幕

这里介绍一款工具——[email protected],用来获取手机屏幕,显示在PC屏幕上.它集截图.录像等多种功能于一体. 安装 1.    下载地址:http://droid-at-screen.org/download.html,下载后是一个jar包,放到某个目录就可以. 2.    安装JDK6或以上版本 3.    安装Android SDK(从Android官方下载一个完整包解压即可) 4.    设置ANDROID_HOME环境变量指向AndroidSDK主目录(这步如果不做,则需

我的Android进阶之旅------&gt;Android实现用Android手机控制PC端的关机和重启的功能(一)PC服务器端

因为经常开着笔记本工作,累了就坐床上玩手机,但晚上要睡觉了又不想下床去关电脑,就想做个Android软件来控制PC端的关机和重启.要想实现此功能,就得让Android手机和PC之间进行通信才能,因此采用Socket编程来实现. ==================================Shutdown命令的用法======================================== 首先得了解一下关于Shutdown命令的用法. 选择win+R,打开命令行窗口,输入shutdo

聚融网贷款投标助手源码(Android)

聚融网贷款投标助手源码(Android) 本项目是一个安卓版本的聚融网贷款投标助手项目源码,一般公司或者开发者都不会把这类内部使用的项目源码泄漏出来,这个项目是依托于他的官网的orong.cn,但是现在官网挂掉了,估计整个公司跑路了才会分享出来,因为项目缺少部分图片资源所以原项目会报错,我使用资源目录下的其他图片文件对缺少的图片资源进行了替代,可能图片显示会有错误但是整个项目可以正常的运行起来,项目很大.本项目默认编译版本4.2.2编码UTF-8,项目有比较详细的注释代码质量也很高,相信里面的功