wdk中ramdisk代码解读

入口函数,即驱动加载函数

NTSTATUS
DriverEntry(
    IN PDRIVER_OBJECT DriverObject,
    IN PUNICODE_STRING RegistryPath
    )

/*++

Routine Description:

    Installable driver initialization entry point.
    This entry point is called directly by the I/O system.

Arguments:

    DriverObject - pointer to the driver object

    RegistryPath - pointer to a unicode string representing the path
                   to driver-specific key in the registry

Return Value:

    STATUS_SUCCESS if successful.

--*/

{
    WDF_DRIVER_CONFIG config;

    KdPrint(("Windows Ramdisk Driver - Driver Framework Edition.\n"));
    KdPrint(("Built %s %s\n", __DATE__, __TIME__));

    WDF_DRIVER_CONFIG_INIT( &config, RamDiskEvtDeviceAdd );

    return WdfDriverCreate(DriverObject, RegistryPath, WDF_NO_OBJECT_ATTRIBUTES, &config, WDF_NO_HANDLE);
}

初始化可配置变量config,指定RamDiskEvtDeviceAdd的地址,这里的RamDiskEvtDeviceAdd相当于wdm中AddDevice回调函数,当即插即用管理器发现了一个新设备 ,则调用的函数。然后调用WdfDriverCreate返回,我的理解是WdfDriverCreate创建了WDF驱动框架。

接下来是RamDiskEvtDeviceAdd函数

NTSTATUS
RamDiskEvtDeviceAdd(
    IN WDFDRIVER Driver,
    IN PWDFDEVICE_INIT DeviceInit
    )
{
    //将要建立的设备对象的属性描述变量
    WDF_OBJECT_ATTRIBUTES   deviceAttributes;
    //将要调用的各种函数的状态返回值
    NTSTATUS                status;
    //将要建立的设备对象
    WDFDEVICE               device;
    //将要建立的队列对象的属性描述变量
    WDF_OBJECT_ATTRIBUTES   queueAttributes;
    //将要简历的队列配置变量
    WDF_IO_QUEUE_CONFIG     ioQueueConfig;
    //设备对象的扩展域的指针
    PDEVICE_EXTENSION       pDeviceExtension;
    //将要建立的队列扩展域指针
    PQUEUE_EXTENSION        pQueueContext = NULL;
    //将要建立的队列对象
    WDFQUEUE                queue;
    //初始化字符串
     DECLARE_CONST_UNICODE_STRING(ntDeviceName, NT_DEVICE_NAME);
    //保证这个函数可以操作paged内存
    PAGED_CODE();
    //防止产生警告
     UNREFERENCED_PARAMETER(Driver);
    //初始化设备的名字
    status = WdfDeviceInitAssignName(DeviceInit, &ntDeviceName);
    if (!NT_SUCCESS(status)) {
        return status;
    }
    //磁盘设备的类型  必须是FILE_DEVICE_DISK
    WdfDeviceInitSetDeviceType(DeviceInit, FILE_DEVICE_DISK);
    //设备的io类型   WdfDeviceIoBuffered 使用缓冲区接受数据  WdfDeviceIoDirect 直接接收数据 IRP所携带的缓冲区  可以直接使用
    //WdfDeviceIoBufferedOrDirect  前面两种方式   都可以使用
    WdfDeviceInitSetIoType(DeviceInit, WdfDeviceIoDirect);
    //Exclusive:独占    设置设备为非独占的
    WdfDeviceInitSetExclusive(DeviceInit, FALSE);
    //设置属性描述变量   就是说  设备对象的扩展  使用什么样的数据结构存储数据
    WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&deviceAttributes, DEVICE_EXTENSION);
    //制定设备的清理回调函数
    deviceAttributes.EvtCleanupCallback = RamDiskEvtDeviceContextCleanup;

    //建立这个设备
    status = WdfDeviceCreate(&DeviceInit, &deviceAttributes, &device);
    if (!NT_SUCCESS(status)) {
        return status;
    }
    //将指针指向新建立设备的设备扩展
    pDeviceExtension = DeviceGetExtension(device);
    //将队列的配置对象初始化为默认值
    WDF_IO_QUEUE_CONFIG_INIT_DEFAULT_QUEUE (
        &ioQueueConfig,
        WdfIoQueueDispatchSequential
        );
    //wdf中我们需要将发往自己创建的设备的请求处理函数  在队列对象的设置对象中设置

    //我们暂时只关心IoDeviceControl和读写事件
    ioQueueConfig.EvtIoDeviceControl = RamDiskEvtIoDeviceControl;
    ioQueueConfig.EvtIoRead          = RamDiskEvtIoRead;
    ioQueueConfig.EvtIoWrite         = RamDiskEvtIoWrite;
    //指定这个队列设备的属性描述对象
    WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&queueAttributes, QUEUE_EXTENSION);

    //创建这个队列对象  这里第一个参数是设备对象   说明这个队列在创建的时候   已经和设备绑定了
    status = WdfIoQueueCreate( device,
                               &ioQueueConfig,
                               &queueAttributes,
                               &queue );
    if (!NT_SUCCESS(status)) {
        return status;
    }
    //获取队列设备的扩展指针
    pQueueContext = QueueGetExtension(queue);
    //设置队列对象的扩展的设备对象扩展
    pQueueContext->DeviceExtension = pDeviceExtension;

    //设置进度条显示
    //status = SetForwardProgressOnQueue(queue);
    //if (!NT_SUCCESS(status)) {
    //    return status;
    //}

    //初始化设备扩展中一些变量
    pDeviceExtension->DiskRegInfo.DriveLetter.Buffer =
        (PWSTR) &pDeviceExtension->DriveLetterBuffer;
    pDeviceExtension->DiskRegInfo.DriveLetter.MaximumLength =
        sizeof(pDeviceExtension->DriveLetterBuffer);
    //从注册表中获取    这个注册表信息  其实是由inf文件指定
    RamDiskQueryDiskRegParameters(
        WdfDriverGetRegistryPath(WdfDeviceGetDriver(device)),
        &pDeviceExtension->DiskRegInfo
        );
    //分配非分页内存给这个磁盘  DiskImage是磁盘镜像的意思
    //分页内存和非分页内存   分页内存的存储介质有可能在内存  也有可能在硬盘
    //非分页内存的存储介质一定是内存    所以分配非分页内存  不会引起复杂的换页操作和一些缺页中断
    //RAMDISK_TAG 代表空间标识  便于调试
    pDeviceExtension->DiskImage = ExAllocatePoolWithTag(
        NonPagedPool,
        pDeviceExtension->DiskRegInfo.DiskSize,
        RAMDISK_TAG
        );

    if (pDeviceExtension->DiskImage) {

        UNICODE_STRING deviceName;
        UNICODE_STRING win32Name;
        //初始化磁盘空间
        RamDiskFormatDisk(pDeviceExtension);

        status = STATUS_SUCCESS;

        //    /DosDevice/xxx    windows下代表了磁盘设备
        RtlInitUnicodeString(&win32Name, DOS_DEVICE_NAME);
        RtlInitUnicodeString(&deviceName, NT_DEVICE_NAME);

        pDeviceExtension->SymbolicLink.Buffer = (PWSTR)
            &pDeviceExtension->DosDeviceNameBuffer;
        pDeviceExtension->SymbolicLink.MaximumLength =
            sizeof(pDeviceExtension->DosDeviceNameBuffer);
        pDeviceExtension->SymbolicLink.Length = win32Name.Length;

        RtlCopyUnicodeString(&pDeviceExtension->SymbolicLink, &win32Name);
        //初始化磁盘盘符
        RtlAppendUnicodeStringToString(&pDeviceExtension->SymbolicLink,
                                       &pDeviceExtension->DiskRegInfo.DriveLetter);

        status = WdfDeviceCreateSymbolicLink(device,
                                             &pDeviceExtension->SymbolicLink);
    }

    return status;

}

调用PAGED_CODE()保证代码可以调用非分页内存,这个宏其实是判断IRQL级别不处于DISPATCH_LEVEL级别及以上,如果是,则触发断言。

使用WdfDeviceInitAssignName初始化设备的名字,设备名和符号链接是不一样的,设备名只能在内核态被其他驱动识别。而符号链接则可以在用户态被应用程序识别。

使用WdfDeviceInitSetIoType指定设备的IO类型,一共有三种类型可选,1.WdfDeviceIoBuffered 使用缓冲区接受数据  2.WdfDeviceIoDirect,直接接收数据,IRP所携带的缓冲区 ,可以直接使用

3.WdfDeviceIoBufferedOrDirect 前面两种方式都会使用

使用函数WdfDeviceInitSetExclusive设置设备为非独占

WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE指定设备扩展使用什么样的数据结构存储数据,DEVICE_EXTENSION为自定义的结构体

deviceAttributes.EvtCleanupCallback这里指定设备的清理函数为RamDiskEvtDeviceContextCleanup

准备完毕之后使用WdfDeviceCreate创建磁盘设备,参数都是前面处理过,其中device为传出参数

下面是创建队列对象的代码

ioQueueConfig为队列对象的配置对象,首先通过WDF_IO_QUEUE_CONFIG_INIT_DEFAULT_QUEUE初始化为默认值,然后指定IoDeviceControl和读写事件,通过WdfIoQueueCreate创建这个队列对象

注意的是队列对象的扩展结构里面包含了设备对象的扩展结构指针

RamDiskQueryDiskRegParameters取得注册表数据,系统在驱动加载之前,会首先提供访问注册表的功能,这里的数据其实是安装驱动时ramdisk.inf指定

pDeviceExtension->DiskImage这个指针是磁盘镜像,即磁盘位置的起始,然后为这个指针分配非分页内存,调用ExAllocatePoolWithTag传入第一个参数NonPagedPool。使用非分页内存的原因是,非分页内存的存储介质一定是内存,所以分配非分页内存 ,不会引起复杂的换页操作和一些缺页中断。

如果分配内存成功,则使用RamDiskFormatDisk函数初始化磁盘空间。接下来使用了win32Name存储符号链接,最终的符号链接为/DosDevice/Z: ,在用户态会默认显示Z作为盘符

刚开始我找到WdfControlFinishInitializing这个函数,以为可以通知用户层更新,从而显示出新建的磁盘对象,后来发现这个函数不能达到目的。在这里也求大神指教,如何不用重启,即可显示出新建的磁盘设备。

//初始化磁盘结构的一些数据
NTSTATUS
RamDiskFormatDisk(
    IN PDEVICE_EXTENSION devExt
    )

{
    //将分配的非分页内存的首地址  转化为DBR结构的指针
    PBOOT_SECTOR bootSector = (PBOOT_SECTOR) devExt->DiskImage;
    //指向FAT表的指针
    PUCHAR       firstFatSector;
    //根目录入口点个数
    ULONG        rootDirEntries;
    //一个簇由多少扇区组成
    ULONG        sectorsPerCluster;
    //fat文件系统类型
    USHORT       fatType;        // Type FAT 12 or 16
    //FAT表里面有多少表项
    USHORT       fatEntries;     // Number of cluster entries in FAT
    //一个FAT表需要占多少簇存储
    USHORT       fatSectorCnt;   // Number of sectors for FAT
    //第一个根目录入口点
    PDIR_ENTRY   rootDir;        // Pointer to first entry in root dir
    //保证这个函数可以操作paged内存
    PAGED_CODE();

    ASSERT(sizeof(BOOT_SECTOR) == 512);
    ASSERT(devExt->DiskImage != NULL);
    //清空磁盘镜像
    RtlZeroMemory(devExt->DiskImage, devExt->DiskRegInfo.DiskSize);
    //每个扇区有512个字节
    devExt->DiskGeometry.BytesPerSector = 512;
    //每个磁道有32个扇区
    devExt->DiskGeometry.SectorsPerTrack = 32;     // Using Ramdisk value
    //每个柱面有两个磁道
    devExt->DiskGeometry.TracksPerCylinder = 2;    // Using Ramdisk value
    //计算得出磁柱面数
    devExt->DiskGeometry.Cylinders.QuadPart = devExt->DiskRegInfo.DiskSize / 512 / 32 / 2;
    devExt->DiskGeometry.MediaType = RAMDISK_MEDIA_TYPE;
    KdPrint((
        "Cylinders: %ld\n TracksPerCylinder: %ld\n SectorsPerTrack: %ld\n BytesPerSector: %ld\n",
        devExt->DiskGeometry.Cylinders.QuadPart, devExt->DiskGeometry.TracksPerCylinder,
        devExt->DiskGeometry.SectorsPerTrack, devExt->DiskGeometry.BytesPerSector
        ));
    //初始化根目录入口点个数
    rootDirEntries = devExt->DiskRegInfo.RootDirEntries;
    //一个簇由多少扇区组成
    sectorsPerCluster = devExt->DiskRegInfo.SectorsPerCluster;
    //这里  调整根目录数目的地方很疑惑
    if (rootDirEntries & (DIR_ENTRIES_PER_SECTOR - 1)) {

        rootDirEntries =
            (rootDirEntries + (DIR_ENTRIES_PER_SECTOR - 1)) &
                ~ (DIR_ENTRIES_PER_SECTOR - 1);
    }

    KdPrint((
        "Root dir entries: %ld\n Sectors/cluster: %ld\n",
        rootDirEntries, sectorsPerCluster
        ));
    //硬编码写入跳转指令
    bootSector->bsJump[0] = 0xeb;
    bootSector->bsJump[1] = 0x3c;
    bootSector->bsJump[2] = 0x90;
    //oem名字
        bootSector->bsOemName[0] = ‘R‘;
    bootSector->bsOemName[1] = ‘a‘;
    bootSector->bsOemName[2] = ‘j‘;
    bootSector->bsOemName[3] = ‘u‘;
    bootSector->bsOemName[4] = ‘R‘;
    bootSector->bsOemName[5] = ‘a‘;
    bootSector->bsOemName[6] = ‘m‘;
    bootSector->bsOemName[7] = ‘ ‘;
    //每个扇区有多少字节
    bootSector->bsBytesPerSec = (SHORT)devExt->DiskGeometry.BytesPerSector;
    //指定这个磁盘卷保留扇区   仅DBR这一个扇区为保留扇区
    bootSector->bsResSectors  = 1;
    //fat表一般一式两份  但这里就创建一份就可以
    bootSector->bsFATs        = 1;
    //指定根目录入口点个数
     bootSector->bsRootDirEnts = (USHORT)rootDirEntries;
     //磁盘总扇区数   磁盘大小除以扇区大小
    bootSector->bsSectors     = (USHORT)(devExt->DiskRegInfo.DiskSize /
                                         devExt->DiskGeometry.BytesPerSector);
    //磁盘介质类型
    bootSector->bsMedia       = (UCHAR)devExt->DiskGeometry.MediaType;
    //每个簇有多少个扇区
    bootSector->bsSecPerClus  = (UCHAR)sectorsPerCluster;
    //fat表项数  总扇区数-保留扇区数
    //这里很疑惑   如何得到FAT表项数
    fatEntries =
        (bootSector->bsSectors - bootSector->bsResSectors -
            bootSector->bsRootDirEnts / DIR_ENTRIES_PER_SECTOR) /
                bootSector->bsSecPerClus + 2;

    //如果表项数  大于2的12次方   则使用FAT16
    if (fatEntries > 4087) {
        fatType =  16;
        //这一步的调整  我很不理解
        fatSectorCnt = (fatEntries * 2 + 511) / 512;
        fatEntries   = fatEntries + fatSectorCnt;
        fatSectorCnt = (fatEntries * 2 + 511) / 512;
    }
    else {
        fatType =  12;
        fatSectorCnt = (((fatEntries * 3 + 1) / 2) + 511) / 512;
        fatEntries   = fatEntries + fatSectorCnt;
        fatSectorCnt = (((fatEntries * 3 + 1) / 2) + 511) / 512;
    }
    //初始化FAT表所占的扇区数
    bootSector->bsFATsecs       = fatSectorCnt;
    //初始化DBR每个磁道的扇区数
    bootSector->bsSecPerTrack   = (USHORT)devExt->DiskGeometry.SectorsPerTrack;
    //初始化每个柱面的磁道数
    bootSector->bsHeads         = (USHORT)devExt->DiskGeometry.TracksPerCylinder;
    //初始化启动签名  windows要求必须是0x29或者0x28
    bootSector->bsBootSignature = 0x29;
    //卷ID  随便填写
    bootSector->bsVolumeID      = 0x12345678;
    //初始化卷标
        bootSector->bsLabel[0]  = ‘R‘;
    bootSector->bsLabel[1]  = ‘a‘;
    bootSector->bsLabel[2]  = ‘m‘;
    bootSector->bsLabel[3]  = ‘D‘;
    bootSector->bsLabel[4]  = ‘i‘;
    bootSector->bsLabel[5]  = ‘s‘;
    bootSector->bsLabel[6]  = ‘k‘;
    bootSector->bsLabel[7]  = ‘ ‘;
    bootSector->bsLabel[8]  = ‘ ‘;
    bootSector->bsLabel[9]  = ‘ ‘;
    bootSector->bsLabel[10] = ‘ ‘;
    //设置磁盘文件类型
    bootSector->bsFileSystemType[0] = ‘F‘;
    bootSector->bsFileSystemType[1] = ‘A‘;
    bootSector->bsFileSystemType[2] = ‘T‘;
    bootSector->bsFileSystemType[3] = ‘1‘;
    bootSector->bsFileSystemType[4] = ‘?‘;
    bootSector->bsFileSystemType[5] = ‘ ‘;
    bootSector->bsFileSystemType[6] = ‘ ‘;
    bootSector->bsFileSystemType[7] = ‘ ‘;

    bootSector->bsFileSystemType[4] = ( fatType == 16 ) ? ‘6‘ : ‘2‘;
    //设置DBR结束标识
    bootSector->bsSig2[0] = 0x55;
    bootSector->bsSig2[1] = 0xAA;
    //初始化FAT表结构
    firstFatSector    = (PUCHAR)(bootSector + 1);
    firstFatSector[0] = (UCHAR)devExt->DiskGeometry.MediaType;
    firstFatSector[1] = 0xFF;
    firstFatSector[2] = 0xFF;

    if (fatType == 16) {
        firstFatSector[3] = 0xFF;
    }

     rootDir = (PDIR_ENTRY)(bootSector + 1 + fatSectorCnt);

    //接下来 初始化根目录入口点信息
    rootDir->deName[0] = ‘M‘;
    rootDir->deName[1] = ‘S‘;
    rootDir->deName[2] = ‘-‘;
    rootDir->deName[3] = ‘R‘;
    rootDir->deName[4] = ‘A‘;
    rootDir->deName[5] = ‘M‘;
    rootDir->deName[6] = ‘D‘;
    rootDir->deName[7] = ‘R‘;

    //
    // Set device extension name to "IVE"
    // NOTE: Fill all 3 characters, eg. sizeof(rootDir->deExtension);
    //
    rootDir->deExtension[0] = ‘I‘;
    rootDir->deExtension[1] = ‘V‘;
    rootDir->deExtension[2] = ‘E‘;

    rootDir->deAttributes = DIR_ATTR_VOLUME;

    return STATUS_SUCCESS;

}

RamDiskFormatDisk函数用来初始化硬盘空间,如果是引导盘,那么硬盘镜像的第一个结构是MBR(主引导扇区),但是ramdisk虚拟的硬盘没有引导系统的作用,所以,第一个结构是DBR,结构如下

typedef struct  _BOOT_SECTOR
{
    UCHAR       bsJump[3];          // x86 jmp instruction, checked by FS
    CCHAR       bsOemName[8];       // OEM name of formatter
    USHORT      bsBytesPerSec;      // Bytes per Sector
    UCHAR       bsSecPerClus;       // Sectors per Cluster
    USHORT      bsResSectors;       // Reserved Sectors
    UCHAR       bsFATs;             // Number of FATs - we always use 1
    USHORT      bsRootDirEnts;      // Number of Root Dir Entries
    USHORT      bsSectors;          // Number of Sectors
    UCHAR       bsMedia;            // Media type - we use RAMDISK_MEDIA_TYPE
    USHORT      bsFATsecs;          // Number of FAT sectors
    USHORT      bsSecPerTrack;      // Sectors per Track - we use 32
    USHORT      bsHeads;            // Number of Heads - we use 2
    ULONG       bsHiddenSecs;       // Hidden Sectors - we set to 0
    ULONG       bsHugeSectors;      // Number of Sectors if > 32 MB size
    UCHAR       bsDriveNumber;      // Drive Number - not used
    UCHAR       bsReserved1;        // Reserved
    UCHAR       bsBootSignature;    // New Format Boot Signature - 0x29
    ULONG       bsVolumeID;         // VolumeID - set to 0x12345678
    CCHAR       bsLabel[11];        // Label - set to RamDisk
    CCHAR       bsFileSystemType[8];// File System Type - FAT12 or FAT16
    CCHAR       bsReserved2[448];   // Reserved
    UCHAR       bsSig2[2];          // Originial Boot Signature - 0x55, 0xAA
}   BOOT_SECTOR, *PBOOT_SECTOR;

DBR结构如下图

时间: 2024-10-10 11:41:44

wdk中ramdisk代码解读的相关文章

Jsoup代码解读之六-防御XSS攻击

Jsoup代码解读之八-防御XSS攻击 防御XSS攻击的一般原理 cleaner是Jsoup的重要功能之一,我们常用它来进行富文本输入中的XSS防御. 我们知道,XSS攻击的一般方式是,通过在页面输入中嵌入一段恶意脚本,对输出时的DOM结构进行修改,从而达到执行这段脚本的目的.对于纯文本输入,过滤/转义HTML特殊字符<,>,",'是行之有效的办法,但是如果本身用户输入的就是一段HTML文本(例如博客文章),这种方式就不太有效了.这个时候,就是Jsoup大显身手的时候了. 在前面,我

Jsoup代码解读之五-实现一个CSS Selector

Jsoup代码解读之七-实现一个CSS Selector 当当当!终于来到了Jsoup的特色:CSS Selector部分.selector也是我写的爬虫框架webmagic开发的一个重点.附上一张street fighter的图,希望以后webmagic也能挑战Jsoup! select机制 Jsoup的select包里,类结构如下: 在最开始介绍Jsoup的时候,就已经说过NodeVisitor和Selector了.Selector是select部分的对外facade,而NodeVisito

Jsoup代码解读之四-parser

Jsoup代码解读之四-parser 作为Java世界最好的HTML 解析库,Jsoup的parser实现非常具有代表性.这部分也是Jsoup最复杂的部分,需要一些数据结构.状态机乃至编译器的知识.好在HTML语法不复杂,解析只是到DOM树为止,所以作为编译器入门倒是挺合适的.这一块不要指望囫囵吞枣,我们还是泡一杯咖啡,细细品味其中的奥妙吧. 基础知识 编译器 将计算机语言转化为另一种计算机语言(通常是更底层的语言,例如机器码.汇编.或者JVM字节码)的过程就叫做编译(compile).编译器(

Jsoup代码解读之三-Document的输出

Jsoup代码解读之三-Document的输出 Jsoup官方说明里,一个重要的功能就是output tidy HTML.这里我们看看Jsoup是如何输出HTML的. HTML相关知识 分析代码前,我们不妨先想想,“tidy HTML"到底包括哪些东西: 换行,块级标签习惯上都会独占一行 缩进,根据HTML标签嵌套层数,行首缩进会不同 严格的标签闭合,如果是可以自闭合的标签并且没有内容,则进行自闭合 HTML实体的转义 这里要补充一下HTML标签的知识.HTML Tag可以分为block和inl

Jsoup代码解读之二-DOM相关对象

Jsoup代码解读之二-DOM相关对象 之前在文章中说到,Jsoup使用了一套自己的DOM对象体系,和Java XML API互不兼容.这样做的好处是从XML的API里解脱出来,使得代码精炼了很多.这篇文章会说明Jsoup的DOM结构,DOM的遍历方式.在下一篇文章,我会并结合这两个基础,分析一下Jsoup的HTML输出功能. DOM结构相关类 我们先来看看nodes包的类图: 这里可以看到,核心无疑是Node类. Node类是一个抽象类,它代表DOM树中的一个节点,它包含: 父节点parent

【dlib代码解读】人脸检测器的训练【转】

转自:http://blog.csdn.net/elaine_bao/article/details/53046542 版权声明:本文为博主原创文章,转载请注明. 目录(?)[-] 综述 代码解读 step by step 1 预处理阶段 11 载入训练集测试集 12 图片上采样 13 镜像图片 2 训练阶段 21 定义scanner用于扫描图片并提取特征 22 设置scanner扫描窗口大小 23 定义trainer用于训练人脸检测器 24 训练生成人脸检测器 25 测试 3 tips 31

Hybrid----优秀开源代码解读之JS与iOS Native Code互调的优雅实现方案-备

本篇为大家介绍一个优秀的开源小项目:WebViewJavascriptBridge. 它优雅地实现了在使用UIWebView时JS与ios 的ObjC nativecode之间的互调,支持消息发送.接收.消息处理器的注册与调用以及设置消息处理的回调. 就像项目的名称一样,它是连接UIWebView和Javascript的bridge.在加入这个项目之后,他们之间的交互处理方式变得很友好. 在native code中跟UIWebView中的js交互的时候,像下面这样: [cpp] view pla

MySQL5.6.7-rc index condition pushdown 索引条件下推代码解读

MySQL5.6.7-rc index condition pushdown  索引条件下推代码解读 http://jishu.zol.com.cn/4505.html MySQL5.6.7-rc index condition pushdown代码解读 A-A+ 2013-08-07 11:10| 分享到: 对index condition pushdown很感兴趣,并且跟踪代码让自己受益良多,因此就来跟一下相关代码. 看的是mysql5.6.7-rc官方社区版. 先说说我对研究MySQL源码

directshow 虚拟摄像头 实例 代码解读

directshow 虚拟摄像头 实例 代码解读 本文只介绍这个源码的大致构成以及怎么修改,因为其他的我也不会啊哈哈哈,我就是用QQ调用虚拟摄像头读取我自己的视频或者图片播放给别人让别人以为这就是实时的而已. 1,示例代码:QQ可用 Directshow实现的虚拟摄像头(win10+vs2013,c++代码)    没有积分的又需要代码可以邮件找我[email protected] 2,源码说明 此源码包含了directshow的库文件,相关配置文件也保存在工程中,可以直接运行,不需要额外安装D