实现方式是:
3 环通过IOServiceOpen打开连接驱动
0 环在 start 中设置一个与之交互的Client(方式有设置键值对或 new一个Client)
3 环:
// UserClientShared.h
//
// UserClientShared.h
// UserSpaceClient
//
#include <stdint.h>
typedef struct TimerValue
{
uint64_t time;
uint64_t timebase;
} TimerValue;
// 用于用户客户端方法的控制请求码
enum TimerRequestCode {
kTestUserClientStartTimer,
kTestUserClientStopTimer,
kTestUserClientGetElapsedTimerTime,
kTestUserClientGetElapsedTimerValue,
kTestUserClientDelayForMs,
kTestUserClientDelayForTime,
kTestUserClientInstallTimer,
kTestUserClientMethodCount
};
//main.cpp
//
// main.c
// UserSpaceClient
//
#include <CoreFoundation/CoreFoundation.h>
#include <IOKit/IOKitLib.h>
#include "UserClientShared.h"
kern_return_t StartTimer (io_connect_t connection);
kern_return_t StopTimer (io_connect_t connection);
kern_return_t GetElapsedTimerTime (io_connect_t connection, uint32_t* timerTime);
kern_return_t GetElapsedTimerValue (io_connect_t connection, TimerValue* timerValue);
kern_return_t DelayForMs (io_connect_t connection, uint32_t milliseconds);
kern_return_t DelayForTime (io_connect_t connection, const TimerValue* timerValue);
//以下这些控制请求 都会调试驱动中IOUserClient的externalMethod方法(也就是必须实现)
kern_return_t StartTimer (io_connect_t connection)
{
//IOConnectCallMethod用于控制请求
return IOConnectCallMethod(
//连接(端口)
connection,
//控制码(函数)
kTestUserClientStartTimer,
//参数
NULL, 0, NULL, 0, NULL, NULL, NULL, NULL);
}
kern_return_t StopTimer (io_connect_t connection)
{
return IOConnectCallMethod(connection, kTestUserClientStopTimer, NULL, 0, NULL, 0, NULL, NULL, NULL, NULL);
}
kern_return_t GetElapsedTimerTime (io_connect_t connection, uint32_t* timerTime)
{
uint64_t scalarOut[1];
uint32_t scalarOutCount;
kern_return_t result;
scalarOutCount = 1; // 初始化为scalarOut数组的长度
result = IOConnectCallScalarMethod(connection, kTestUserClientGetElapsedTimerTime, NULL, 0, scalarOut, &scalarOutCount);
if (result == kIOReturnSuccess)
*timerTime = (uint32_t)scalarOut[0];
return result;
}
kern_return_t GetElapsedTimerValue (io_connect_t connection, TimerValue* timerValue)
{
size_t structOutSize;
structOutSize = sizeof(TimerValue);
return IOConnectCallStructMethod(connection, kTestUserClientGetElapsedTimerValue, NULL, 0, timerValue, &structOutSize);
}
kern_return_t DelayForMs (io_connect_t connection, uint32_t milliseconds)
{
uint64_t scalarIn[1];
scalarIn[0] = milliseconds;
return IOConnectCallScalarMethod(connection, kTestUserClientDelayForMs, scalarIn, 1, NULL, NULL);
}
kern_return_t DelayForTime (io_connect_t connection, const TimerValue* timerValue)
{
return IOConnectCallStructMethod(connection, kTestUserClientDelayForTime, timerValue, sizeof(TimerValue), NULL, 0);
}
IONotificationPortRef gAsyncNotificationPort = NULL;
IONotificationPortRef MyDriverGetAsyncCompletionPort ()
{
// If the port has been allocated, return the existing instance
if (gAsyncNotificationPort != NULL)
return gAsyncNotificationPort;
gAsyncNotificationPort = IONotificationPortCreate(kIOMasterPortDefault);
return gAsyncNotificationPort;
}
kern_return_t InstallTimer (io_connect_t connection, uint32_t milliseconds, IOAsyncCallback0 timerCallback, void* context)
{
io_async_ref64_t asyncRef;
uint64_t scalarIn[1];
//设置回调函数
asyncRef[kIOAsyncCalloutFuncIndex] = (uint64_t)timerCallback;
asyncRef[kIOAsyncCalloutRefconIndex] = (uint64_t)context;
//设置输入参数
scalarIn[0] = milliseconds;
return IOConnectCallAsyncScalarMethod(connection, kTestUserClientInstallTimer, IONotificationPortGetMachPort(gAsyncNotificationPort),
asyncRef, kIOAsyncCalloutCount, scalarIn, 1, NULL, NULL);
}
void DelayCallback (void *refcon, IOReturn result)
{
printf("DelayCallback - refcon %08x and result %08x\n", (uint32_t)refcon, result);
if (refcon == (void*)0xdeadbeef)
CFRunLoopStop(CFRunLoopGetMain());
}
int main (int argc, const char * argv[])
{
CFDictionaryRef matchingDict = NULL;
io_iterator_t iter = 0;
io_service_t service = 0;
kern_return_t kr;
////创建一个匹配字典,用于查找任意的 USB 设备
matchingDict = IOServiceMatching("com_osxkernel_driver_IOKitTest");
//为匹配字典的所有IO注册表对象创建迭代器
kr = IOServiceGetMatchingServices(kIOMasterPortDefault, matchingDict, &iter);
if (kr != KERN_SUCCESS)
return -1;
////迭代所有匹配的对象
while ((service = IOIteratorNext(iter)) != 0)
{
task_port_t owningTask = mach_task_self();
uint32_t type = 0;
//就是一个端口
io_connect_t driverConnection;
//建立驱动连接
//会触发驱动程序 IOService类中的newUserClient方法实例化一个新的Client对象 驱动中可以不要实现
//只要设置一个值就好
// bool com_osxkernel_driver_IOKitTest::start (IOService *provider)
// {
// bool res = super::start(provider);
// setProperty("IOUserClientClass", "com_osxkernel_driver_IOKitTestUserClient");
// registerService();
// return res;
// }
// virtual IOReturn newUserClient(
//用户传递的参数
//task_t owningTask, void * securityID,
// UInt32 type, OSDictionary * properties,
//handler返回给调用者
// IOUserClient ** handler );
//
// virtual IOReturn newUserClient( task_t owningTask, void * securityID,
// UInt32 type, IOUserClient ** handler );
kr = IOServiceOpen(
//表示希望连接到的驱动程序
service,
//表示运行的应用程序
owningTask,
//无符号 32位×××
type,
//成功后的返回值
&driverConnection);
if (kr == KERN_SUCCESS)
{
uint32_t timerTime;
TimerValue timerValue;
IONotificationPortRef notificationPort;
//获取一个通知端口
notificationPort = MyDriverGetAsyncCompletionPort();
if (notificationPort)
{
CFRunLoopSourceRef runLoopSource;
runLoopSource = IONotificationPortGetRunLoopSource(notificationPort);
CFRunLoopAddSource(CFRunLoopGetCurrent(), runLoopSource, kCFRunLoopDefaultMode);
}
kr = StopTimer(driverConnection);
printf("StopTimer - %08x\n", kr);
kr = StartTimer(driverConnection);
printf("StartTimer - %08x\n", kr);
kr = GetElapsedTimerTime(driverConnection, &timerTime);
printf("GetElapsedTimerTime - %08x, time %d\n", kr, timerTime);
kr = DelayForMs(driverConnection, 100);
printf("DelayForMs - %08x\n", kr);
kr = GetElapsedTimerTime(driverConnection, &timerTime);
printf("GetElapsedTimerTime - %08x, time %d\n", kr, timerTime);
kr = GetElapsedTimerValue(driverConnection, &timerValue);
printf("GetElapsedTimerValue - %08x, time %lld / %lld\n", kr, timerValue.time, timerValue.timebase);
timerValue.timebase = 0;
timerValue.time = 500;
kr = DelayForTime(driverConnection, &timerValue);
printf("DelayForTime - %08x\n", kr);
timerValue.timebase = 1000;
kr = DelayForTime(driverConnection, &timerValue);
printf("DelayForTime - %08x\n", kr);
kr = GetElapsedTimerTime(driverConnection, &timerTime);
printf("GetElapsedTimerTime - %08x, time %d\n", kr, timerTime);
kr = StopTimer(driverConnection);
printf("StopTimer - %08x\n", kr);
kr = InstallTimer(driverConnection, 10000, DelayCallback, (void*)0xdeadbeef);
printf("InstallTimer - %08x\n", kr);
kr = InstallTimer(driverConnection, 5000, DelayCallback, (void*)0xdead0005);
printf("InstallTimer - %08x\n", kr);
kr = InstallTimer(driverConnection, 2000, DelayCallback, (void*)0xdead0002);
printf("InstallTimer - %08x\n", kr);
kr = InstallTimer(driverConnection, 7000, DelayCallback, (void*)0xdead0007);
printf("InstallTimer - %08x\n", kr);
CFRunLoopRun();
IONotificationPortDestroy(gAsyncNotificationPort);
gAsyncNotificationPort = NULL;
//关闭驱动连接
IOServiceClose(driverConnection);
}
IOObjectRelease(service);
}
IOObjectRelease(iter);
return 0;
}
0 环:
//IOKitTestUserClient.h
//
// IOKitTestUserClient.h
// IOKitTest
//
#include <IOKit/IOUserClient.h>
#include "IOKitTest.h"
#include "UserClientShared.h"
class com_osxkernel_driver_IOKitTestUserClient : public IOUserClient
{
OSDeclareDefaultStructors(com_osxkernel_driver_IOKitTestUserClient)
private:
task_t m_task;
com_osxkernel_driver_IOKitTest* m_driver;
bool m_timerRunning;
uint64_t m_timerStartTime;
static const IOExternalMethodDispatch sMethods[kTestUserClientMethodCount];
static IOReturn sStartTimer (OSObject* target, void* reference, IOExternalMethodArguments* arguments);
static IOReturn sStopTimer (OSObject* target, void* reference, IOExternalMethodArguments* arguments);
static IOReturn sGetElapsedTimerTime (OSObject* target, void* reference, IOExternalMethodArguments* arguments);
static IOReturn sGetElapsedTimerValue (OSObject* target, void* reference, IOExternalMethodArguments* arguments);
static IOReturn sDelayForMs (OSObject* target, void* reference, IOExternalMethodArguments* arguments);
static IOReturn sDelayForTime (OSObject* target, void* reference, IOExternalMethodArguments* arguments);
static IOReturn sInstallTimer (OSObject* target, void* reference, IOExternalMethodArguments* arguments);
static void DelayThreadFunc (void *parameter, wait_result_t);
IOReturn startTimer ();
IOReturn stopTimer ();
IOReturn getElapsedTimerTime (uint32_t* timerTime);
IOReturn getElapsedTimerValue (TimerValue* timerValue);
IOReturn delayForMs (uint32_t milliseconds);
IOReturn delayForTime (const TimerValue* timerValue);
public:
virtual bool initWithTask (task_t owningTask, void* securityToken, UInt32 type, OSDictionary* properties);
virtual IOReturn clientClose (void);
virtual IOReturn clientDied (void);
virtual bool start (IOService* provider);
virtual void stop (IOService* provider);
virtual void free (void);
virtual IOReturn externalMethod (
//32位的请求控制码 指定客户端应用程序请求的是哪一个操作
uint32_t selector,
//标题及结体体参数
IOExternalMethodArguments* arguments,
//应该调用的类方法
IOExternalMethodDispatch* dispatch = 0, OSObject* target = 0, void* reference = 0);
};
//UserClientShared.h
//
// UserClientShared.h
// UserSpaceClient
//
#include <stdint.h>
typedef struct TimerValue
{
uint64_t time;
uint64_t timebase;
} TimerValue;
// 用于用户客户端方法的控制请求码
enum TimerRequestCode {
kTestUserClientStartTimer,
kTestUserClientStopTimer,
kTestUserClientGetElapsedTimerTime,
kTestUserClientGetElapsedTimerValue,
kTestUserClientDelayForMs,
kTestUserClientDelayForTime,
kTestUserClientInstallTimer,
kTestUserClientMethodCount
};
IOKitTestUserClient.cpp
//
// IOKitTestUserClient.cpp
// IOKitTest
//
#include <IOKit/IOLib.h>
#include "IOKitTestUserClient.h"
#define super IOUserClient
OSDefineMetaClassAndStructors(com_osxkernel_driver_IOKitTestUserClient, IOUserClient)
bool com_osxkernel_driver_IOKitTestUserClient::initWithTask (task_t owningTask, void* securityToken, UInt32 type, OSDictionary* properties)
{
if (!owningTask)
return false;
if (! super::initWithTask(owningTask, securityToken , type, properties))
return false;
m_task = owningTask;
//判断进程的权限
IOReturn ret = clientHasPrivilege(securityToken, kIOClientPrivilegeAdministrator);
if ( ret == kIOReturnSuccess )
{
// m_taskIsAdmin = true;
}
return true;
}
bool com_osxkernel_driver_IOKitTestUserClient::start (IOService* provider)
{
if (! super::start(provider))
return false;
m_driver = OSDynamicCast(com_osxkernel_driver_IOKitTest, provider);
if (!m_driver)
return false;
return true;
}
void com_osxkernel_driver_IOKitTestUserClient::stop (IOService* provider)
{
IOLog("userClient::stop\n");
super::stop(provider);
}
void com_osxkernel_driver_IOKitTestUserClient::free (void)
{
IOLog("userClient::free\n");
super::free();
}
IOReturn com_osxkernel_driver_IOKitTestUserClient::clientClose (void)
{
terminate();
return kIOReturnSuccess;
}
IOReturn com_osxkernel_driver_IOKitTestUserClient::clientDied (void)
{
IOLog("userClient::clientDied\n");
return super::clientDied();
}
IOReturn com_osxkernel_driver_IOKitTestUserClient::sStartTimer (OSObject* target, void* reference, IOExternalMethodArguments* arguments)
{
com_osxkernel_driver_IOKitTestUserClient* me = (com_osxkernel_driver_IOKitTestUserClient*)target;
return me->startTimer();
}
IOReturn com_osxkernel_driver_IOKitTestUserClient::sStopTimer (OSObject* target, void* reference, IOExternalMethodArguments* arguments)
{
com_osxkernel_driver_IOKitTestUserClient* me = (com_osxkernel_driver_IOKitTestUserClient*)target;
return me->stopTimer();
}
IOReturn com_osxkernel_driver_IOKitTestUserClient::sGetElapsedTimerTime (OSObject* target, void* reference, IOExternalMethodArguments* arguments)
{
com_osxkernel_driver_IOKitTestUserClient* me = (com_osxkernel_driver_IOKitTestUserClient*)target;
uint32_t timerTime;
IOReturn result;
//调用实现操作的方法
result = me->getElapsedTimerTime(&timerTime);
//将操作的标量结果返回给调用进程
arguments->scalarOutput[0] = timerTime;
return result;
}
IOReturn com_osxkernel_driver_IOKitTestUserClient::sGetElapsedTimerValue (OSObject* target, void* reference, IOExternalMethodArguments* arguments)
{
com_osxkernel_driver_IOKitTestUserClient* me = (com_osxkernel_driver_IOKitTestUserClient*)target;
return me->getElapsedTimerValue((TimerValue*)arguments->structureOutput);
}
IOReturn com_osxkernel_driver_IOKitTestUserClient::sDelayForMs (OSObject* target, void* reference, IOExternalMethodArguments* arguments)
{
com_osxkernel_driver_IOKitTestUserClient* me = (com_osxkernel_driver_IOKitTestUserClient*)target;
return me->delayForMs((uint32_t)arguments->scalarInput[0]);
}
IOReturn com_osxkernel_driver_IOKitTestUserClient::sDelayForTime (OSObject* target, void* reference, IOExternalMethodArguments* arguments)
{
com_osxkernel_driver_IOKitTestUserClient* me = (com_osxkernel_driver_IOKitTestUserClient*)target;
return me->delayForTime((TimerValue*)arguments->structureInput);
}
//保存后台操作所需参数的结构体
struct TimerParams
{
OSAsyncReference64 asyncRef;
uint32_t milliseconds;
OSObject* userClient;
};
IOReturn com_osxkernel_driver_IOKitTestUserClient::sInstallTimer (OSObject* target, void* reference, IOExternalMethodArguments* arguments)
{
TimerParams* timerParams;
thread_t newThread;
//分配一个结构体,存储定时器需要的参数
timerParams = (TimerParams*)IOMalloc(sizeof(TimerParams));
//取得asyncReference缓冲区的一个副本
bcopy(arguments->asyncReference, timerParams->asyncRef, sizeof(OSAsyncReference64));
//取得用户应用程序提供的milliseconds值的一个副本
timerParams->milliseconds = (uint32_t)arguments->scalarInput[0];
//取得userClient对象的一个引用
timerParams->userClient = target;
//异步操作执行时,保留该用户客户端
target->retain();
//启动一个后台线程,在返回给调用者之后继续执行操作
kernel_thread_start(DelayThreadFunc, timerParams, &newThread);
thread_deallocate(newThread);
//立即返回给调用应用程序
return kIOReturnSuccess;
}
void com_osxkernel_driver_IOKitTestUserClient::DelayThreadFunc (void *parameter, wait_result_t)
{
TimerParams* timerParams = (TimerParams*)parameter;
//按请求的时间睡眠
IOSleep(timerParams->milliseconds);
//向用户应用程序发送操作完成的通知
sendAsyncResult64(timerParams->asyncRef, kIOReturnSuccess, NULL, 0);
//后台操作已经完成
timerParams->userClient->release();
IOFree(timerParams, sizeof(TimerParams));
}
IOReturn com_osxkernel_driver_IOKitTestUserClient::startTimer ()
{
if (m_timerRunning == true)
return kIOReturnBusy;
m_timerRunning = true;
clock_get_uptime(&m_timerStartTime);
return kIOReturnSuccess;
}
IOReturn com_osxkernel_driver_IOKitTestUserClient::stopTimer ()
{
if (m_timerRunning == false)
return kIOReturnNotOpen;
m_timerRunning = false;
return kIOReturnSuccess;
}
IOReturn com_osxkernel_driver_IOKitTestUserClient::getElapsedTimerTime (uint32_t* timerTime)
{
uint64_t timeNow;
uint64_t elapsedTime;
if (m_timerRunning == false)
return kIOReturnNotOpen;
clock_get_uptime(&timeNow);
absolutetime_to_nanoseconds((timeNow - m_timerStartTime), &elapsedTime);
*timerTime = (uint32_t)(elapsedTime / 1000000);
return kIOReturnSuccess;
}
IOReturn com_osxkernel_driver_IOKitTestUserClient::getElapsedTimerValue (TimerValue* timerValue)
{
uint64_t timeNow;
uint64_t elapsedTime;
if (m_timerRunning == false)
return kIOReturnNotOpen;
clock_get_uptime(&timeNow);
absolutetime_to_nanoseconds((timeNow - m_timerStartTime), &elapsedTime);
timerValue->timebase = 1000000;
timerValue->time = (elapsedTime * timerValue->timebase) / 1000000000;
return kIOReturnSuccess;
}
IOReturn com_osxkernel_driver_IOKitTestUserClient::delayForMs (uint32_t milliseconds)
{
IOSleep(milliseconds);
return kIOReturnSuccess;
}
IOReturn com_osxkernel_driver_IOKitTestUserClient::delayForTime (const TimerValue* timerValue)
{
uint32_t milliseconds;
if (timerValue->timebase == 0)
return kIOReturnBadArgument;
milliseconds = (uint32_t)((timerValue->time * 1000) / timerValue->timebase);
IOSleep(milliseconds);
return kIOReturnSuccess;
}
//调试表
const IOExternalMethodDispatch com_osxkernel_driver_IOKitTestUserClient::sMethods[kTestUserClientMethodCount] =
{
// kTestUserClientStartTimer (void)
{ sStartTimer, 0, 0, 0, 0 },
// kTestUserClientStopTimer (void)
{ sStopTimer, 0, 0, 0, 0 },
// kTestUserClientGetElapsedTimerTime (uint32_t* timerValue)
{ sGetElapsedTimerTime, 0, 0, 1, 0 },
// kTestUserClientGetElapsedTimerValue (TimerValue* timerValue)
{ sGetElapsedTimerValue, 0, 0, 0, sizeof(TimerValue) },
// kTestUserClientDelayForMs (uint32_t milliseconds)
{ sDelayForMs, 1, 0, 0, 0 },
// kTestUserClientDelayForTime (const TimerValue* timerValue)
{ sDelayForTime, 0, sizeof(TimerValue), 0, 0 },
// kTestUserClientInstallTimer (uint32_t milliseconds)
{ sInstallTimer, 1, 0, 0, 0 }
};
//驱动程序的IOUserClient子类提供的实现
IOReturn com_osxkernel_driver_IOKitTestUserClient::externalMethod (uint32_t selector, IOExternalMethodArguments* arguments,
IOExternalMethodDispatch* dispatch, OSObject* target, void* reference)
{
//判断控制码 确保请求的控制码选择器在范围内
if (selector >= kTestUserClientMethodCount)
return kIOReturnUnsupported;
//选择相应的控制码(也就是调试表)
dispatch = (IOExternalMethodDispatch*)&sMethods[selector];
target = this;
reference = NULL;
//返回父类的
return super::externalMethod(selector, arguments, dispatch, target, reference);
//超类的实现
// externalMethod(selector, arguments, dispatch, target, reference){
// Dispatch->function(target,reference,args);
// }
}
//IOKitTest.h
#include <IOKit/IOService.h>
class com_osxkernel_driver_IOKitTest : public IOService
{
OSDeclareDefaultStructors(com_osxkernel_driver_IOKitTest)
public:
virtual bool init (OSDictionary* dictionary = NULL);
virtual void free (void);
virtual bool start (IOService* provider);
virtual void stop (IOService* provider);
};
//IOKitTest.cpp
#include "IOKitTest.h"
#include <IOKit/IOLib.h>
#define super IOService
OSDefineMetaClassAndStructors(com_osxkernel_driver_IOKitTest, IOService)
bool com_osxkernel_driver_IOKitTest::init (OSDictionary* dict)
{
bool res = super::init(dict);
return res;
}
void com_osxkernel_driver_IOKitTest::free (void)
{
super::free();
}
//启动时设置与之交互的IOUserClientClass
bool com_osxkernel_driver_IOKitTest::start (IOService *provider)
{
bool res = super::start(provider);
setProperty("IOUserClientClass", "com_osxkernel_driver_IOKitTestUserClient");
registerService();
return res;
}
void com_osxkernel_driver_IOKitTest::stop (IOService *provider)
{
super::stop(provider);
}
liuhailongdeMacBook-Air:~ liuhailong$ sudo chown -R root:wheel /Users/liuhailong/Library/Developer/Xcode/DerivedData/IOKitTest-dpmmsjcydkpiyxewowmltvnxxxgh/Build/Products/Debug/IOKitTest.kext
liuhailongdeMacBook-Air:~ liuhailong$ sudo kextload /Users/liuhailong/Library/Developer/Xcode/DerivedData/IOKitTest-dpmmsjcydkpiyxewowmltvnxxxgh/Build/Products/Debug/IOKitTest.kext
/Users/liuhailong/Library/Developer/Xcode/DerivedData/IOKitTest-dpmmsjcydkpiyxewowmltvnxxxgh/Build/Products/Debug/IOKitTest.kext failed to load - (libkern/kext) link error; check the system/kernel logs for errors or try kextutil(8).
liuhailongdeMacBook-Air:~ liuhailong$ sudo chown -R root:wheel /Users/liuhailong/Library/Developer/Xcode/DerivedData/IOKitTest-dpmmsjcydkpiyxewowmltvnxxxgh/Build/Products/Debug/IOKitTest.kext
liuhailongdeMacBook-Air:~ liuhailong$ sudo kextload /Users/liuhailong/Library/Developer/Xcode/DerivedData/IOKitTest-dpmmsjcydkpiyxewowmltvnxxxgh/Build/Products/Debug/IOKitTest.kext
liuhailongdeMacBook-Air:~ liuhailong$ /Users/liuhailong/Library/Developer/Xcode/DerivedData/UserSpaceClient-ffozqcnbwexasqelqqgpccijiicg/Build/Products/Debug/UserSpaceClient
StopTimer - e00002cd
StartTimer - 00000000
GetElapsedTimerTime - 00000000, time 0
DelayForMs - 00000000
GetElapsedTimerTime - 00000000, time 100
GetElapsedTimerValue - 00000000, time 100170 / 1000000
DelayForTime - e00002c2
DelayForTime - 00000000
GetElapsedTimerTime - 00000000, time 600
StopTimer - 00000000
InstallTimer - 00000000
InstallTimer - 00000000
InstallTimer - 00000000
InstallTimer - 00000000
DelayCallback - refcon dead0002 and result 00000000
DelayCallback - refcon dead0005 and result 00000000
DelayCallback - refcon dead0007 and result 00000000
DelayCallback - refcon deadbeef and result 00000000
liuhailongdeMacBook-Air:~ liuhailong$
转载于:https://blog.51cto.com/haidragon/2164506