这段时间每日每夜写iTunesmobiledevice的C# API,奈何之前找的开源库早在一、两年前就停止开发,而很多关键函数支持不全,只好自己找资料。无奈网上关于C#这方面的资料很少,大部分是关于libimobile的或者是其他C的开源库。只有一个一个慢慢尝试来修正。
不过目前在原来库的基础上完成了IPA传输、安装、卸载以及获取本机已经安装的IPA列表。下一步计划做备份。
分享给大家。
IPA传输安装:
首先需要调用AMDeviceTransferApplication上传IPA到/var/mobile/Media/PublicStaging中,然后调用AMDeviceInstallApplication来执行安装,这两个函数声明:
[DllImport("iTunesMobileDevice.dll", CallingConvention = CallingConvention.Cdecl)] public static extern unsafe int AMDeviceTransferApplication(void* conn, void* path, void* options, DeviceInstallApplicationCallback callback, void* unknow1);
传入参数:
void* conn “com.apple.mobile.installation_proxy”的Service的服务端口,需要之前用AMDeviceStartService启动“com.apple.mobile.installation_proxy”获得
void* path IPA文件本机地址,需要转换为CFString转换方法在库里面有
void* options 可选参数,需要用pilst传入,请参考CFDictionary写法,具体参数没有尝试
DeviceInstallApplicationCallback callback 回调函数。可用于接收安装进度通知,返回给回调函数的是plist文件,需要自己解析Status字段,获得当前状态
void* unknow1 未知,传入给null值即可。
返回值:
0 执行成功
其他任何错误请参考kAMDError说明
[DllImport("iTunesMobileDevice.dll", CallingConvention=CallingConvention.Cdecl)] public static extern unsafe int AMDeviceInstallApplication(void* conn, void* path, void* options, void* callback, void* unknow1);
传入参数:
void* conn 同上,安装服务的端口
void* path 同上,此处依然写本地地址,需要转换为CFString。其实本处可以直接写IPA文件名即可。
void* callback 回调函数。
void* unknow1 未知
返回值:
0 执行成功
其他任何错误请参考kAMDError说明
IPA卸载:
IPA卸载需要调用AMDeviceTransferApplication函数来完成。函数声明如下:
</pre> [DllImport("iTunesMobileDevice.dll", CallingConvention = CallingConvention.Cdecl)] public static extern unsafe int AMDeviceUninstallApplication(void* conn, void* bundleIdentifier, IntPtr installOption, void* unknown0, void* unknown1);
传入参数:
void* conn 此处通以上,写安装服务的端口
void* bundleIdentifier 此处需要填写app的BundleIdentifier,例如QQ的com.tencent.qq这个值,可以在info.plist获取这个值,也可以利用后面的获得当前安装ipa列表获得。这个值需要以CFString形式传入。
IntPtr installOption 安装选项,此处也是以plist形式传入,未具体测试,直接给的null。
void* unknown0 未知
void* unknown1 未知
返回值:
0 执行成功
其他任何错误请参考kAMDError说明
只要检测到返回值是0,则代表程序已经被卸载。
获取本机已经安装的IPA列表:
功能函数:AMDeviceLookupApplications
函数原型:
[DllImport("iTunesMobileDevice.dll", CallingConvention = CallingConvention.Cdecl)] public static extern unsafe int AMDeviceLookupApplications(void* conn, IntPtr AppType,ref IntPtr result);
传入参数:
void* conn AFC服务端口。
IntPtr AppType 此处可指定传出的plist包含哪些数据,写0的话,默认传出全部数据。
ref IntPtr result 获取结果
传出结果:
返回值:
0 执行成功
其他任何错误请参考kAMDError说明
执行成功之后,只需要读取result即可获取当前安装的列表。注意是plist文件,需要先做转换成为标准xml才能读取,关于遍历值的方法我还没有去写。
另外Manzana的库不支持中文文件名和路径会报错,解决办法是所有涉及到path的传入,统一转成byte[]传入即可。编码建议UTF8
附kAMDError枚举
internal enum kAMDError { kAMDSuccess = 0, kAMDAlreadyArchivedError = -402653094, kAMDApplicationAlreadyInstalledError = -402653130, kAMDApplicationMoveFailedError = -402653129, kAMDApplicationSandboxFailedError = -402653127, kAMDApplicationSINFCaptureFailedError = -402653128, kAMDApplicationVerificationFailedError = -402653126, kAMDArchiveDestructionFailedError = -402653125, kAMDBadHeaderError = -402653182, kAMDBundleVerificationFailedError = -402653124, kAMDBusyError = -402653167, kAMDCarrierBundleCopyFailedError = -402653123, kAMDCarrierBundleDirectoryCreationFailedError = -402653122, kAMDCarrierBundleMissingSupportedSIMsError = -402653121, kAMDCheckinTimeoutError = -402653148, kAMDCommCenterNotificationFailedError = -402653120, kAMDContainerCreationFailedError = -402653119, kAMDContainerP0wnFailedError = -402653118, kAMDContainerRemovalFailedError = -402653117, kAMDCryptoError = -402653166, kAMDDigestFailedError = -402653135, kAMDEmbeddedProfileInstallFailedError = -402653116, kAMDEOFError = -402653170, kAMDErrorError = -402653115, kAMDExecutableTwiddleFailedError = -402653114, kAMDExistenceCheckFailedError = -402653113, kAMDFileExistsError = -402653168, kAMDGetProhibitedError = -402653162, kAMDImmutableValueError = -402653159, kAMDInstallMapUpdateFailedError = -402653112, kAMDInvalidActivationRecordError = -402653146, kAMDInvalidArgumentError = -402653177, kAMDInvalidCheckinError = -402653149, kAMDInvalidDiskImageError = -402653133, kAMDInvalidHostIDError = -402653156, kAMDInvalidResponseError = -402653165, kAMDInvalidServiceError = -402653150, kAMDInvalidSessionIDError = -402653152, kAMDIsDirectoryError = -402653175, kAMDiTunesArtworkCaptureFailedError = -402653096, kAMDiTunesMetadataCaptureFailedError = -402653095, kAMDManifestCaptureFailedError = -402653111, kAMDMapGenerationFailedError = -402653110, kAMDMissingActivationRecordError = -402653145, kAMDMissingBundleExecutableError = -402653109, kAMDMissingBundleIdentifierError = -402653108, kAMDMissingBundlePathError = -402653107, kAMDMissingContainerError = -402653106, kAMDMissingDigestError = -402653132, kAMDMissingHostIDError = -402653157, kAMDMissingImageTypeError = -402653136, kAMDMissingKeyError = -402653164, kAMDMissingOptionsError = -402653137, kAMDMissingPairRecordError = -402653147, kAMDMissingServiceError = -402653151, kAMDMissingSessionIDError = -402653153, kAMDMissingValueError = -402653163, kAMDMuxError = -402653131, kAMDNoResourcesError = -402653181, kAMDNotConnectedError = -402653173, kAMDNotFoundError = -402653176, kAMDNotificationFailedError = -402653105, kAMDOverrunError = -402653171, kAMDPackageExtractionFailedError = -402653104, kAMDPackageInspectionFailedError = -402653103, kAMDPackageMoveFailedError = -402653102, kAMDPasswordProtectedError = -402653158, kAMDPathConversionFailedError = -402653101, kAMDPermissionError = -402653174, kAMDProvisioningProfileNotValid = -402653140, kAMDReadError = -402653180, kAMDReceiveMessageError = -402653138, kAMDRemoveProhibitedError = -402653160, kAMDRestoreContainerFailedError = -402653100, kAMDSeatbeltProfileRemovalFailedError = -402653099, kAMDSendMessageError = -402653139, kAMDSessionActiveError = -402653155, kAMDSessionInactiveError = -402653154, kAMDSetProhibitedError = -402653161, kAMDStageCreationFailedError = -402653098, kAMDStartServiceError = -402653134, kAMDSUFirmwareError = -402653141, kAMDSUPatchError = -402653142, kAMDSUVerificationError = -402653143, kAMDSymlinkFailedError = -402653097, kAMDTimeOutError = -402653172, kAMDTrustComputerError = -402653034, kAMDUndefinedError = -402653183, kAMDUnknownPacketError = -402653178, kAMDUnsupportedError = -402653169, kAMDWriteError = -402653179, kAMDWrongDroidError = -402653144 }
Comments 9 条评论
博主 LJH
你好~请问AMDeviceLookupApplications怎么获得结果集呢?
博主 nivalxer
@LJH 按照文章上面写的方法,获取的结果集是一个plist,建议先传参Intptr类型,然后对结果先进行CFString转换(CFString转换方法网上有库,你可以搜索CoreFoundation C#版本,里面提供)。转换之后就是一个完整的Plist文件,需要自己写方法来遍历这个字典,提取相关信息即可。
博主 飙风小子
@nivalxer 你好,帮我看看,我取回来的数据为空呢。是不是转换出问题了?QQ:304197867
//获取本机已经安装的IPA列表
public unsafe string getInstallList()
{
string s = null;
IntPtr ptr = IntPtr.Zero;
MobileDevice.AMDeviceLookupApplications(this.hAFC, IntPtr.Zero, ref ptr);
if (ptr != IntPtr.Zero)
{
byte len = Marshal.ReadByte(ptr, 8);
if (len > 0)
{
s = Marshal.PtrToStringAnsi(new IntPtr(ptr.ToInt64() + 9L), len);
}
}
return s;
}
博主 nivalxer
@飙风小子 MobileDevice.AMDeviceLookupApplications(this.hAFC, IntPtr.Zero, ref ptr);这里调用第一个参数错了。
第一个参数应该是通过AMDeviceStartService启动“com.apple.mobile.installation_proxy”服务拿到的新的值。因为应用程序服务是com.apple.mobile.installation_proxy来管理的,hAFC是AFC服务,所以一直会出错的。关于启动服务方法参考库里面已经有的AMDeviceStartService函数即可。建议直接把这个启动服务放到启动AFC服务之后。
博主 飙风小子
@nivalxer 你好,非常感谢的回答,但是我按你说的做了修改,返回结果仍然是空字符串。
1、新定义hInstall、hInstallService两个指针。
2、在启动com.apple.afc时同时启动com.apple.mobile.installation_proxy服务并以hInstallService作为参数传回。
3、AFCConnectionOpen时用hInstallService作为参数打开连接hInstall。
4、getInstallList时传递hInstall作为参数。
结果:
getInstallList()返回空字符串。
疑问:
1、com.apple.afc和com.apple.mobile.installation_proxy是需要同时开启吗?
2、以上步骤哪儿错误了?
可以qq交流吗。我用的MobileDevice.cs,追加了上面你介绍的几个函数。
////////////////////////////////////////////////////////////////////////////////////
internal unsafe void* hAFC;
internal unsafe void* hInstall;
internal unsafe void* hService;
internal unsafe void* hInstallService;
internal unsafe void* iPhoneHandle;
////////////////////////////////////////////////////////////////////////////////////
//越狱系统
if (0 != MobileDevice.AMDeviceStartService(this.iPhoneHandle, MobileDevice.__CFStringMakeConstantString(MobileDevice.StringToCString(“com.apple.afc2”)), ref this.hService, null))
{
//文件系统
if (0 != MobileDevice.AMDeviceStartService(this.iPhoneHandle, MobileDevice.__CFStringMakeConstantString(MobileDevice.StringToCString(“com.apple.afc”)), ref this.hService, null))
{
return false;
}
//应用管理
if (0 != MobileDevice.AMDeviceStartService(this.iPhoneHandle, MobileDevice.__CFStringMakeConstantString(MobileDevice.StringToCString(“com.apple.mobile.installation_proxy”)), ref this.hInstallService, null))
{
return false;
}
}
else
{
this.wasAFC2 = true;
}
//文件系统
if (MobileDevice.AFCConnectionOpen(this.hService, 0, ref this.hAFC) != 0)
{
return false;
}
//应用管理
if (MobileDevice.AFCConnectionOpen(this.hInstallService, 0, ref this.hInstall) != 0)
{
return false;
}
////////////////////////////////////////////////////////////////////////////////////////
//获取本机已经安装的IPA列表
public unsafe string getInstallList()
{
string s = null;
IntPtr ptr = IntPtr.Zero;
MobileDevice.AMDeviceLookupApplications(this.hInstall, IntPtr.Zero, ref ptr);
if (ptr != IntPtr.Zero)
{
byte len = Marshal.ReadByte(ptr, 8);
if (len > 0)
{
s = Marshal.PtrToStringAnsi(new IntPtr(ptr.ToInt64() + 9L), len);
}
}
return s;
}
博主 cmjiang
@飙风小子 请问下你这个问题最终怎么解决的呀?我也遇到了,取到空字符串,还请不吝赐教,万分感谢感谢!!
博主 cmjiang
大神,几个问题麻烦给解答下,万分感谢啊!!!
一、请问获取程序列表是按照下面的顺序调用api么?
1.AMDeviceStartService
2.AFCConnectionOpen
3.AMDeviceLookupApplications
二、你文章里说AMDeviceLookupApplications第一个参数是AFC服务端口,而在回复下面一位仁兄的问题的时候又说是通过AMDeviceStartService启“com.apple.mobile.installation_proxy”服务拿到的新的值,这个是什么意思呢?
多谢多谢
博主 nivalxer
@cmjiang com.apple.mobile.installation_proxy服务主要是用于卸载、安装和iOS9以前的打包并备份IPA功能用的。AMDeviceStartService就是用于打开iOS上面各类服务,例如com.apple.mobile.battery电池服务,com.apple.mobile.iTunes iTunes相关服务等等。
个别涉及到这类功能,就需要先打开服务,然后通过服务返回的SocketID来进行进一步操作。
AMDeviceLookupApplications这个接口时间久了,我有点儿忘了。
AFCConnectionOpen主要用于打开AFC通讯的,比如文件系统的通讯,还有端口映射的通讯。
博主 nivalxer
@cmjiang 具体看你要实现什么功能,可以把功能需求发出来,有空的情况下,我会进行调试,如果成功,我会把这部分功能更新到开源库上面去。