Windows的回调机制

Windows系统内核提供了一系列的事件通知(Notify)机制以及回调(Callback)机制。

事件通知机制:用于监控系统内某一事件的操作。

回调机制:用来反映系统内某一个部件的状态,也可以被用来实现多个内核模块之间的通信。

Windows的回调机制

回调机制为驱动程序提供了一种通用的方法来发送和接收某类通告,这些通告可以是系统某个部件的状态发生变化而产生的通告,也可以是开发者自定义的某个条件因为条件成立而产生的通告。

Windows操作系统内置了一部分回调,如电源状态变化回调(PowerState Callback)、系统时间变化回调(SetSystemTime Callback)等,用户也可以自定义回调。

回调对象(Callback Object)是回调机制中最重要的数据结构,类型为CALLBACK_OBJECT,用来唯一地描述一个回调。它属于一种命名内核对象。

当系统或开发者为驱动程序发送某一类通告时,需要创建一个回调对象A,如果驱动程序接受这类通告,则需要打开回调对象A,成功打开后,驱动程序还需要把一个回调例程注册到回调对象A上。这样,回调对象A和回调例程就被关联起来了,当系统或开发者发送通告时,就可以 通告回调对象A发起通告,最后回调例程就会被调用。

例如:系统再启动时创建了一个回调对象用于描述电源状态的变化,这个回调对象名称叫做\Callback\PowerState,驱动程序需要接收电源状态变化通告时,首先可以调用ExCreateCallback函数,打开\Callback\PowerState对象,得到PCALLBACK_OBJECT类型的指针,然后驱动程序调用ExRegisterCallback函数,把一个回调例程注册到这个回调对象上。当系统电源状态发生变化时,系统可以调用ExNotifyCallback函数来发送通告,驱动程序所注册的回调例程会被调用。

创建/打开回调对象

NTSTATUS
NTAPI
ExCreateCallback(
	OUT PCALLBACK_OBJECT *CallbackObject,	// 回调对象的指针,用完之后要通告ObDereferenceObject函数释放
	IN POBJECT_ATTRIBUTES ObjectAttributes,	// 属性,可以通过其指定回调对象名称
	IN BOOLEAN Create,	// 为TRUE表示若打开失败后会创建该回调对象,为FALSE表示若打开失败(即不存在),函数会返回失败
	IN BOOLEAN AllowMultipleCallbacks);	// 为TRUE表示允许多个回调例程注册到该回调对象上

注册回调对象,即把一个回调例程绑定到回调对象上,当发送通告时,系统会一次调用与该回调对象绑定的回调例程。

注册成功时返回一个注册句柄,注册失败返回NULL。

PVOID
NTAPI
ExRegisterCallback(
    IN PCALLBACK_OBJECT CallbackObject,		// 回调对象指针
    IN PCALLBACK_FUNCTION CallbackFunction,	// 回调例程
    IN PVOID CallbackContext);	// 用户自定义数据,传给回调例程的参数,可以为NULL

移除注册

VOID
NTAPI
ExUnregisterCallback(IN PVOID CallbackRegistrationHandle);

回调例程,注意其IRQL <= APC_LEVEL

VOID
NTAPI
CallbackFunction(
    IN PVOID CallbackContext,	// ExRegisterCallback的第三参数
    IN PVOID Argument1,		// 根据具体的回调对象决定
    IN PVOID Argument2);	// 根据具体的回调对象决定

发送通告,它内部会对CallbackObject进行分析,查找注册到其上的回调例程,按照注册顺序依次调用。

VOID
NTAPI
ExNotifyCallback(
	IN PCALLBACK_OBJECT CallbackObject,                 
    IN PVOID Argument1,                 
    IN PVOID Argument2);

测试代码

PCALLBACK_OBJECT CallbackObject = { 0 };

VOID
CallbackFunction(
    IN PVOID CallbackContext,
    IN PVOID Argument1,
    IN PVOID Argument2)
{
    PAGED_CODE();
    DbgPrint("CallbackFunction() is called");
}
void DriverUnload(PDRIVER_OBJECT DriverObject)
{
    UNREFERENCED_PARAMETER(DriverObject);
    ExUnregisterCallback(CallbackFunction);
    ObDereferenceObject(CallbackObject); 
}
NTSTATUS
DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegisterPath)
{
    //解引用
    UNREFERENCED_PARAMETER(RegisterPath);
    NTSTATUS Status = STATUS_SUCCESS;    
    OBJECT_ATTRIBUTES ObjectAttributes = { 0 };
    UNICODE_STRING ObjectName = { 0 };
    do
    {
        RtlInitUnicodeString(&ObjectName, L"\\Callback\\KeyBoard");
        InitializeObjectAttributes(&ObjectAttributes, &ObjectName, OBJ_CASE_INSENSITIVE, NULL, NULL);
        Status = ExCreateCallback(&CallbackObject, &ObjectAttributes, TRUE, TRUE);
        if (!NT_SUCCESS(Status))
        {
            break;
        }
        if (ExRegisterCallback(CallbackObject, CallbackFunction, NULL) == NULL)
        {
            break;
        }
        ExNotifyCallback(CallbackObject, NULL, NULL);
    } while (FALSE);

    //设置驱动卸载例程
    DriverObject->DriverUnload = DriverUnload;
    return Status;
}
  • 22
    点赞
  • 30
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
----------------------------------- Android 编程基础 1 封面----------------------------------- Android 编程基础 2 开放手机联盟 --Open --Open --Open --Open Handset Handset Handset Handset Alliance Alliance Alliance Alliance 什么是开放手机联盟? 开放手机联盟, Open Handset Alliance :是美国 Google 公司与 2007 年 11 月 5 日宣布组建的一个全球性的联 盟组织。这一联盟将会支持 Google 发布的 Android 手机操作系统或者应用软件,共同开发名为 Android 的 开 放源代码的移动系统。开放手机联盟包括手机制造商、手机芯片厂商和移动运营商几类。目前,联盟成员 数 量已经达到了 43 家。 移动手机联盟创始成员: Aplix 、 Ascender 、 Audience 、 Broadcom 、中国移动、 eBay 、 Esmertec 、谷歌、宏达电、英特尔、 KDDI 、 Living Image 、 LG 、 Marvell 、摩托罗拉、 NMS 、 NTT DoCoMo 、 Nuance 、 Nvidia 、 PacketVideo 、高通、三星 、 SiRF 、 SkyPop 、 Sonic Network 、 Sprint Nextel 、 Synaptics 、 TAT 、意大利电信、西班牙电信、德州仪器、 T-M obile 和 Wind River 。 Mobile Mobile Mobile Mobile Operators Operators Operators Operators 移动运营商类 China Mobile Communications Corporation 中国移动通信 KDDI CORPORATION 日本 KDDI 电信 NTT DoCoMo, Inc. 日本多科莫电信 SOFTBANK MOBILE Corp. 日本软银移动 Sprint Nextel( 美国 ) T-Mobile( 德国 ) Telecom Italia( 意大利 ) Telef ó nica( 西班牙 ) Vodafone 沃达丰电信 China Unicom 中国联通 Semiconductor Semiconductor Semiconductor Semiconductor Companies Companies Companies Companies 半导体制造公司 AKM Semiconductor Inc Audience ARM Atheros Communications Broadcom Corporation( 博通 ) Ericsson ( 爱立信公司 ) Intel Corporation ( 英特尔公司 ) Marvell Semiconductor, Inc. ( 收购了 intel 手机芯片部门的公司 )----------------------------------- Android 编程基础 3 NVIDIA Corporation ( 英伟达公司 ) Qualcomm Inc.( 高通公司 ) SiRF Technology Holdings, Inc.( 知名 GPS 芯片制造商 ) Synaptics, Inc. Texas Instruments Incorporated ( 德州仪器 ) Handset Handset Handset Handset Manufacturers Manufacturers Manufacturers Manufacturers 电话制造商 ASUSTeK Computer Inc. 华硕 Garmin International, Inc. HTC Corporation ( 多普达的母公司 ) 宏达电子 Huawei Technologies 华为科技 LG Electronics, Inc. 乐金电子 Motorola, Inc. 摩托罗拉 Samsung Electronics 三星电子 Sony Ericsson 索尼爱立信 Toshiba Corporation 东芝公司 lenovo 联想移动 联盟成员: Software Software Software Software Companies Companies Companies Companies 软件提供公司 Ascender Corp. eBay Inc. Esmertec Google Inc. LivingImage LTD. Nuance Communications, Inc. OMRON SOFTWARE Co, Ltd. 日本欧姆龙软件 有限公司 PacketVideo (PV) SkyPop SONiVOX ASUSTeK Computer Inc. 华硕 AKM Semiconductor AKM 半导体公司 ARM 公司 Atheros Communications Toshiba Corporation 东芝公司 lenovo 联想移动 软银移动 日本无线运营商软银 瑞典计算机咨询公司 Teleca AB Garmin International, Inc. 高明 HTC Corporation ( 多普达的母公司 ) 宏达电子 Huawei Technologies 华为科技 LG Electronics, Inc. 乐金电子 Motorola, Inc. 摩托罗拉 Samsung Electronics 三星电子 Sony Ericsson 索尼爱立信 沃达丰 Teleca Borqs 播思通讯 联盟目的 将会支持 Google 可能发布的手机操作系统或者应用软件,共同开发名为 Android 的开放源代码的移动 系 统。 谷歌早在 2002 年就进入了移动领域,可是由于目前的手机操作系统企业和手机企业相对封闭,提高了 行业的进入门槛,移动互联网的发展远没有拥有统一标准的传统互联网发展迅速,此次推出的开源手机操 作 系统平台就是出于这个目的。 也有分析认为,谷歌并不想做一个简单的手机终端制造商或者软件平台开发商,而意在一统传统互联网和 移 动互联网。----------------------------------- Android 编程基础 4 Android Android Android Android 手机新概念 操作系统的选择 -------- 定制和长尾 � 重构 � MVC 和 Web APP 架构 Android Android Android Android 开发背景 � 计算技术、无线接入技术的发展,使嵌入式系统逐渐有能力对桌面系统常规业务进行支持。 � 谷歌长期以来奉行的移动发展战略:通过与全球各地的手机制造商和移动运营商结成合作伙伴,开发 既 有用又有吸引力的移动服务,并推广这些产品。 Android 进一步推进了 " 随时随地为每个人提供信息 " 这一企 业 目标的实现。 � Open Handset Alliance 汇集了多家业界巨头。运营商如: China Mobile 、 NTT DoCoMo 、 Vodafone 、 T-M obile 等;设备制造商如 ASUS 、 HTC 、 Huawei 、 LG 、 Motorola 、 Samsung 、 Sony Ericsson 、 Toshiba 等;芯片厂商 如 ARM 、 Broadcom 、 Intel 、 Marvell 、 NVIDIA 、 Qualcomm 等。软件厂商如 Ascender 、 eBay 、 Esmertec 、 Li vingImage 等。 � Android 更像一款桌面环境为 Java 的 Linux 操作系统。有助于 Google 实现其 " 随时随地为每个人提供信 息 " 的企业战略。 HTC HTC HTC HTC Dream/G1 Dream/G1 Dream/G1 Dream/G1 具体配置 硬件 3.17 英寸 HVGA (480 x 320) ; 1150mAh 电池 ;高通 528Mhz 7201 处理器 ; 64MB RAM 、 128MB ROM ; 1GB MicroSD 卡 ; QWERTY 全键盘; 310 万像素摄像头。 流媒体 支持视频格式: H.264 、流媒体、 3GPP 、 MPEG4 和 Codec 3GP ;支持音频格式: MP3 、 AAC 、 AAC+ 、 W MA 、 MPEG4 、 WAV 、 MIDI 、 REAL 、 AUDIO 和 OGG ;支持墙纸格式: JPG 、 BMP 、 PNG 和 GIF ;铃声 (MP3 、 AAC 、 AAC+ 和 WMA) 。 接入技术 蓝牙 (class 1) ;四频 (850 , 900 , 1800 , 1900) ;支持 3G , 802.11b 和 802.11g 。----------------------------------- Android 编程基础 5 互联网 支持 HTTP 、 WAP Push 和 xHTML ;支持 POP 、 IMAP 、 SMTP ,以及 AOL 和 GMAIL 电子邮件服务;支持 AIM 、 MSN 、雅虎通和 GTALK ;与谷歌日历同步;与 Android Market 联机;支持谷歌 “ 街景 ” 服务;包装盒内附 数据工具包。 更多信息 https://sites.google.com/a/android.com/opensource/release-features Android Android Android Android 盈利模式 Android 的 App Market 模式,软件开发者获得 7 成收入, 3 成用于系统维护。难点在于位置营销。 设备商通过卖设备、内置特色应用来获得盈利。也可以兼职专业软件开发者进行赢利。 Google 自身通过基于统一平台为用户提供信息来盈利。 Android Android Android Android 的优势 � 源代码完全开放,便于开发人员更清楚的把握实现细节,便于提高开发人员的技术水平,有利于开发 出 更具差异性的应用。 � 采用了对有限内存、电池和 CPU 优化过的虚拟机 Dalvik , Android 的运行速度比想象的要快很多。 � 运营商(中国移动等)的大力支持,产业链条的热捧。 � 良好的盈利模式( 3/7 开),产业链条的各方:运营商、制造商、独立软件生产商都可以获得不错的利 益 。 将移动终端的评价标准从硬件向软件转变,极大的激发了软件开发者的热情。 � Android 的源代码遵循 Apache V2 软件许可,而不是通常的 GPL v2 许可。有利于商业开发。 � 具有强大的 Linux 社区的支持。 Android Android Android Android 的不足 � 由于采用了 Java 作为应用开发语言,目前可用的传统第三方应用还很少,但由于 Android 是一款完全 开 源的移动计算平台,相信第三方应用会很快的丰富起来。 � Google 提供了一套 Java 核心包 (J2SE 5,J2SE 6) 的有限子集,尚不承诺遵守 Java 任何 Java 规范 , 可能会造 成J ava 阵营的进一步分裂。 � 现有应用完善度不太够,需要的开发工作量较大。----------------------------------- Android 编程基础 6 � 基于 QEMU 开发的模拟器调试手段不十分丰富,只支持通话、SMS等,速度慢。 � 暂不具备 Push Mail 和 Office(DataViz 、 QuickOffice 计划近期推出 ) 功能,目前主要面向的是普通消费 者 用户,对商业用户支持尚弱。 Android Android Android Android 带来的影响 ANDROID 的推出后可能影响的产业包括移动电信业,软件开发业,手机制造业,在以消费者为核心的状 态 。 对消费者的影响 � 高档手机选择面增加。 � A ndroid 在设计初期就考虑了与现其有业务的融合,改变以往从计算机为主改成从手机使用为导向。新 生应用如:G oogle 地图及其衍生应用、 GMail 、 GTalk 等。 � GPS 卫星导航功能,手机照相, MP3 ,蓝芽等均被列为 Android 所提供支持的基本选项。 � Android 的平台基本上是免费的,虽然有部份原生链接库会要求费用,但大部份是免权利金; Android 的 程序可以采用 JAVA 开发,但是因为它的虚拟机 (Virtual Machine) Dalvik ,是将 JAVA 的 bytecode 转成 自 己的格式,回避掉需要付给 SUN 有关 JAVA 的授权费用。 对手机制造者的影响 � Android 是款开源的移动计算软件平台,组建了 google 主导的拥有众多产业界巨头的产业联盟,有利于 高效开发、降低成本。 � 由于是源代码开放的产品,对非主导厂商而言,可以避开与主导厂商在核心技术上面的差距,开发出 更 具竞争力和差异化的产品。 对运营商的影响 � 丰富的数据业务,将导致数据流量的显著增加 。 � 手机来源增加,价格更为低廉。 对软件开发者的影响 � 因为 Android 移动软件平台抱持开放互通的观念,势必吸引不少自由软件的拥护者。 � 开发方向有三个重点 :----------------------------------- Android 编程基础 7 � 应用软件的开发 � 特殊功能的原生链接库 � 专属应用程序框架 � 由于 Android 的A pp Market 性质,可能催生出专门的应用软件开发商。 Android Android Android Android 应用现状 � 设备商: lenovo 、琦基、戴尔、三星、摩托罗拉、华为、英特尔、 Kogan 、索爱、华硕、多普达、爱可 视 、 Archos 等。 � 制造商: HTC 、 Telstra 等。 � 手机设计公司:播思、德信无线等。 � 运营商:中国移动、 Sprint 、 T-Mobile 、 Teleca AB 等。 � 芯片商: Qualcomm 、 Marvell 、 TI 、 Boardcom 等。----------------------------------- Android 编程基础 8 Android Android Android Android 开发入门 System System System System Requirements Requirements Requirements Requirements The sections below describe the system and software requirements for developing Android applications using the Android SDK tools included in Android 1.1 SDK, Release 1. Supported Supported Supported Supported Operating Operating Operating Operating Systems Systems Systems Systems • Windows XP (32-bit) or Vista (32- or 64-bit) • Mac OS X 10.4.8 or later (x86 only) • Linux (tested on Linux Ubuntu Dapper Drake) Supported Supported Supported Supported Development Development Development Development Environments Environments Environments Environments Eclipse IDE o Eclipse 3.3 (Europa), 3.4 (Ganymede) � Eclipse JDT plugin (included in most Eclipse IDE packages) � WST (optional, but needed for the Android Editors feature; included in most Eclipse IDE packages ) o JDK 5 or JDK 6 (JRE alone is not sufficient) o Android Development Tools plugin (optional) o Not Not Not Not compatible with Gnu Compiler for Java (gcj) Other development environments or IDEs o JDK 5 or JDK 6 (JRE alone is not sufficient) o Apache Ant 1.6.5 or later for Linux and Mac, 1.7 or later for Windows o Not Not Not Not compatible with Gnu Compiler for Java (gcj) Note: Note: Note: Note: If JDK is already installed on your development computer, please take a moment to make sure that it meets the version requirements listed above. In particular, note that some Linux distributions may include JDK 1.4 or Gnu Compiler for Java, both of which are not supported for Android development----------------------------------- Android 编程基础 9 什么是 Android? Android? Android? Android? Android 是一个专门针对移动设备的软件集,它包括一个操作系统,中间件和一些重要的应用程序。 Beta 版 的 Android SDK 提供了在 Android 平台上使用 JaVa 语言进行 Android 应用开发必须的工具和 API 接口。 特性 • 应用程序框架 支持组件的重用与替换 • Dalvik Dalvik Dalvik Dalvik 虚拟机 专为移动设备优化 • 集成的浏览器 基于开源的 WebKit 引擎 • 优化的图形库 包括定制的 2D 图形库, 3D 图形库基于 OpenGL ES 1.0 (硬件加速可选) • SQLite SQLite SQLite SQLite 用作结构化的数据存储 • 多媒体支持 包括常见的音频、视频和静态图像格式 ( 如 MPEG4, H.264, MP3, AAC, AMR, JPG, PNG , GIF ) • GSM GSM GSM GSM 电话技术 (依赖于硬件) • 蓝牙 Bluetooth, Bluetooth, Bluetooth, Bluetooth, EDGE, EDGE, EDGE, EDGE, 3G, 3G, 3G, 3G, 和 WiFi WiFi WiFi WiFi (依赖于硬件) • 照相机, GPS GPS GPS GPS ,指南针,和加速度计( accelerometer accelerometer accelerometer accelerometer ) (依赖于硬件) • 丰富的开发环境 包括设备模拟器,调试工具,内存及性能分析图表,和 Eclipse 集成开发环境插件 应用程序 Android 会同一系列核心应用程序包一起发布,该应用程序包包括 email 客户端, SMS 短消息程序,日历, 地图,浏览器,联系人管理程序等。所有的应用程序都是使用 JAVA 语言编写的。 应用程序框架 开发人员也可以完全访问核心应用程序所使用的 API 框架。该应用程序的架构设计简化了组件的重用;任 何 一个应用程序都可以发布它的功能块并且任何其它的应用程序都可以使用其所发布的功能块(不过得遵循 框 架的安全性限制)。同样,该应用程序重用机制也使用户可以方便的替换程序组件。 隐藏在每个应用后面的是一系列的服务和系统 , 其中包括; • 丰富而又可扩展的视图( Views ),可以用来构建应用程序, 它包括列表( lists ),网格( grids ),文 本框( text boxes ),按钮( buttons ), 甚至可嵌入的 web 浏览器。 • 内容提供器( Content Providers )使得应用程序可以访问另一个应用程序的数据(如联系人数据库), 或 者共享它们自己的数据 • 资源管理器( Resource Manager )提供 非代码资源的访问,如本地字符串,图形,和布局文件( la yout files )。 • 通知管理器 ( Notification Manager ) 使得应用程序可以在状态栏中显示自定义的提示信息。 • 活动管理器( Activity Manager ) 用来管理应用程序生命周期并提供常用的导航回退功能。----------------------------------- Android 编程基础 10 程序库 Android 包含一些 C/C++ 库,这些库能被 Android 系统中不同的组件使用。它们通过 Android 应用程序框架 为开发者提供服务。以下是一些核心库: • 系统 C C C C 库 - 一个从 BSD 继承来的标准 C 系统函数库( libc ), 它是专门为基于 embedded linu x 的设备定制的。 • 媒体库 - 基于 PacketVideo OpenCORE ;该库支持多种常用的音频、视频格式回放和录制,同时支 持 静态图像文件。编码格式包括 MPEG4, H.264, MP3, AAC, AMR, JPG, PNG 。 • Surface Surface Surface Surface Manager Manager Manager Manager - 对显示子系统的管理,并且为多个应用程序提 供了 2D 和 3D 图层的无缝融合。 • LibWebCore LibWebCore LibWebCore LibWebCore - 一个最新的 web 浏览器引擎用,支持 Android 浏览器和一个可嵌入的 web 视图。 • SGL SGL SGL SGL - 底层的 2D 图形引擎 • 3D 3D 3D 3D libraries libraries libraries libraries - 基于 OpenGL ES 1.0 APIs 实现;该库可以使用硬件 3D 加速(如果可用)或者使用高 度优化的 3D 软加速。 • FreeType FreeType FreeType FreeType - 位图( bitmap )和矢量( vector )字体显示。 • SQLite SQLite SQLite SQLite - 一个对于所有应用程序可用,功能强劲的轻型关系型数据库引擎。 Android Android Android Android 运行库 Android 包括了一个核心库,该核心库提供了 JAVA 编程语言核心库的大多数功能。 每一个 Android 应用程序都在它自己的进程中运行,都拥有一个独立的 Dalvik 虚拟 机实例。 Dalvik 被设计 成一个设备可以同时高效地运行多个虚拟系统。 Dalvik 虚拟机执行( .dex )的 Dalvik 可执行文件,该格式 文 件针对小内存使用做了 优化。同时虚拟机是基于寄存器的,所有的类都经由 JAVA 编译器编译,然后通过 SDK 中 的 "dx" 工具转化成 .dex 格式由虚拟机执行。 Dalvik 虚拟机依赖于 linux 内核的一些功能,比如线程机制和底层内存管理机制。 Linux Linux Linux Linux 内核 Android 的核心系统服务依赖于 Linux 2.6 内核,如安全性,内存管理,进程管理, 网络协议栈和驱动模 型 。 Linux 内核也同时作为硬件和软件栈之间的抽象层。----------------------------------- Android 编程基础 11 Android Android Android Android 的系统架构 系统构架 Android Android Android Android 内核 � Linux 内核版本 2.6 � 位于硬件和软件堆之间的抽象层 � 核心服务:安全机制、内存管理、进程管理、网络、硬件驱动。 Android 依赖 Linux 内核 2.6 提供核心服务,比如安全、内存管理、进程管理、网络、硬件驱动。在这里, L inux 内核扮演的是硬件层和系统其它层次之间的一个抽象层的概念。这个操作系统并非类 GNU/Linux 的,因为 其 系统库,系统初始化和编程接口都和标准的 Linux 系统是有所不同的。----------------------------------- Android 编程基础 12 从 Google 目前 release 的 Linux 系统来看,其没有虚拟内存文件系统,系统所用的是 yaffs2 文件系统,具体 的映像也都位于 SDK 安装目录下。通过 emulator -console 命令,我们可以在 host 中断下得到一个简单的可 以 控制 Android 的 shell ,这 个 系 统 包 含 了 一 个 Toolbox ,提 供 一 些 基 本 的 命 令 工 具 , 集 中 在 /sbin,/system/sbin,/system/bin 中,但是很简陋,命令种类也很少。 目前 Android 的程序安装模式是靠 Eclipse 自动进行的,通过对底层的分析可知,大致步骤就是在 /data/app 和 data/data 下存放 android 底层和普通内核没有什么大的区别,我们可以将其作为一个 Linux 来进行开发和 hacking 。 Lib Lib Lib Lib 和运行环境 lib � C/C++ 库:被各种 Android 组件使用 � 通过应用程序框架开发者可以使用其功能 � 包括: � 媒体库: MPEG4 H.264 MP3 JPG PNG ..... � WebKit/LibWebCore : Web 浏览引擎 � SQLite 关系数据库引擎 � 2D , 3D 图形库、引擎 丰富的类库支持: 2D 和 3D 图像库 OpenGL ES 、数据库 SQLite 、对象数据库 db4o 类库、媒体库、基于 Lin ux 底层系统 C 库等等,让应用开发更简单多样。 Google 使用 Apache 的 Harmony 类库, Harmony 某些方面速 度 快于 Sun 的 VM 。 Runtime 在 Dalvik Java VM 上, Dalvik 采用简练、高效的 byte code 格式运行,它能够在 低 资耗和没有应用相互干扰的情况下并行执行多个应用。 运行时环境 � 核心库提供的 Java 功能 � Dalvik 虚拟机依赖于 Linux 内核,例如线程或底层内存管理 � 设备可以运行多个 Dalvik 虚拟机,每一个 Android 应用程序在它自己的 Dalvik VM 实例中运行 � VM 执行优化的 Dalvik 可执行文件 (.dex) � Dx- 工具把编译过的 Java 文件转换为 dex 文件----------------------------------- Android 编程基础 13 应用和框架 � 核心应用,例如联系人,电子邮件,电话,浏览器,日历,地图, ... � 充分访问所有核心应用框架 API � 简化组件的重用 � 用 Java 编写应用程序----------------------------------- Android 编程基础 14 支持的功能 + Application framework: 可重用的和可替换的组件部分,在这个层面上,所有的软件都是平等的。 + Dalvik virtul machine: 一个基于 Linux 的虚拟机。 + Integrated browser: 一个基于开源的 WebKit 引擎的浏览器,在应用程序层。 + Optimized graphics: 包含一个自定义的 2D 图形库和基于 OpenGL ES 1.0 标准的 3D 实现。 + SQLite: 数据库 + Media support: 通用的音频,视频和对各种图片格式的支持 (MPEG4, H.264, MP3, AAC, AMR, JPG, PNG, GI F) + GSM Telephony: GSM 移动网络 , 硬件支持。 + Bluetooth, EDGE, 3G, and WiFi: 都依赖于硬件支持。 + Camera, GPS, compass, and accelerometer: 都依赖于硬件支持。 + Rich development environment: 包含一套完整的开发工具集,方便跟踪调试,内存检测和性能测试,而且 提供了 Eclipse 的插件。最底层的是一个 Linux Kernel ,加载了几个移动设备必要的系统驱动(这么说来 Android 基 础系统是要以 GPL 发布了?不知道 34 家厂商的硬件开发商们是怎么样想的);上面是类库和 Runtime ,绿 色 的类库部分可以看到大名鼎鼎的 SQLite ,这个软件甚至声称自己属于公共领域(比 MIT License 还要强 @ @ ) , 字体 FreeType 是 BSD-style License 的,图形库 OpenGL ES 只需通过产品测试,无偿使用于产品。再向上看 是应用层的东西了,这里可以做的事情就非常多了 ,各个社区,各个厂家都可以参与进来。难怪 Android 的 sdk 可以 Apache License 发布了 , 对企业和开发人员友好啊。 那么 Google 自己的东西在哪里呢?没错,就是 右 边那个 runtime ,最吸引技术人员的就是这个 runtime (注意,这个才是 Android 的核心)。 Google 为它准备 了 一个虚拟机,叫做 Dalvik 。这个让人摸不着头脑的东西的到底是什么?从开发平台上我们清清楚楚地得到 了 答案: Java----------------------------------- Android 编程基础 15 封面----------------------------------- Android 编程基础 1 封面----------------------------------- Android 编程基础 2 7 7 7 7 个 Linux Linux Linux Linux 手机平台 � Maemo � Android � LIMO � OpenMOKO � GPE^2 � ALP � QTopia Phone Edition Maemo Maemo Maemo Maemo 架构----------------------------------- Android 编程基础 3 Android Android Android Android 架构----------------------------------- Android 编程基础 4 LIMO LIMO LIMO LIMO 架构----------------------------------- Android 编程基础 5 OpneMOKO OpneMOKO OpneMOKO OpneMOKO 架构----------------------------------- Android 编程基础 6 GPE^2 GPE^2 GPE^2 GPE^2 架构----------------------------------- Android 编程基础 7 ALP ALP ALP ALP 架构----------------------------------- Android 编程基础 8 QTopia QTopia QTopia QTopia Phone Phone Phone Phone Edition Edition Edition Edition 架构----------------------------------- Android 编程基础 9 进程间的通信 Linux 手机平台进程间通信 � Maemo 采用 D-BUS � Android 采用 OpenBinder � LiMO 采用 D-BUS � OpenMoko 采用 D-BUS � GPE Phone Edition 采用 D-BUS � ALC 采用 OpenBinder � Qtopia Phone Edition 采用 D-BUS 进程间通信种类 � D-BUS � Openbinder � CORBA/Corbit � IVY � GNET D-BUS----------------------------------- Android 编程基础 10 Android Android Android Android 学习方法 ① 了解什么是 Androi ② 建立开发环境 ③ 阅读 SDK 文档 ④ 背景知识 � Java � 面向对象 � 设计模式 � J2ME 、 Brew 、 Symbian 建立 Android Android Android Android 开发环境 ① 下载 JDK 5 or JDK 6 (JRE alone is not sufficient) -> 安装 -> 设置环境变量 JAVA_HOME CLASSPATH path ② 下载 Eclipse 3.3 (Europa), 3.4 (Ganymede) IDE for JAVA-> 解压 ③ 下载 Android SDK 解压 -> path 里加入 SDK 包中的 tools 目录全路径 ④ 下载 ADT 0.8.0 解压 ⑤ 打开 Eclipse 安装 ADT 插件----------------------------------- Android 编程基础 11 封面----------------------------------- Android 编程基础 1 封面----------------------------------- Android 编程基础 2 Android Android Android Android 开发环境搭建 ADV ADV ADV ADV 的创建 ADT0.9.1 版本 ① 在 Eclipse 中创建----------------------------------- Android 编程基础 3 ② 在命令行中创建 打开 CMD 命令行,进入到 Android SDK tools 目录 使用 android 命令列出 target 值 使用 android create avd 命令来创建 AVD cd E:\Mobile DEV\Android_SDK1.5\tools android list targets 行为: "create avd": 创建一个新的 Android 虚拟设备。 选项: -t --target 新的 AVD 的 Target ID( 必须 ) -c --sdcard 指向一个共享的 SD 存储卡的路径或是为新的 AVD 定制的新 SD 存储卡的容量大小 -p --path 新 AVD 将被创建的位置路径 -n --name 新 AVD 的名称 ( 必须 ) -f --force 强制创建 ( 覆盖已存在的 AVD) -s --skin 新 AVD 的皮肤----------------------------------- Android 编程基础 4 例子 : 将建一个名叫 GPhone 的 AVD , Target ID=2 、 SD 存储卡容量 52M 、路径 C:\AVD\ 、皮肤 SUSE-HVGA- P 查看自己新创建的 ADV : list avd 命令 ADT0.9.0 版本 只能在命令行中创建 开启命令行进入 Android SDK tools 目录 列出 Target ID 创建一个新的 AVD 查看新创建的 AVD 运行指定的 AVD 运行新创建的 AVD:GPhone android create avd -n GPhone -t 2 -c 52M -p C:\AVD\ -s SUSE-HVGA-P android list avd cd E:\Mobile DEV\Android_SDK1.5\tools andriod list target android create avd -n GPhone -t 2 -c 52M -p C:\AVD\ -s SUSE-HVGA-P android list avd emulator -avd GPhone----------------------------------- Android 编程基础 5 Windows Windows Windows Windows 平台: Eclipse IDE 版本 ------------JDK+Eclipse+Android SDK+ADT 1. 必须软件 2. 安装过程 ① 安装 JAVA JDK SE 1.6 � 设置环境变量 � JAVA_HOME � JAVA_JRE_HOME � JRE_HOME � Android_SDK_HOME � CLASSPATH � Path ① JAVA JDK SE 1.6 jdk-6u13-windows-i586-p.exe ② Eclipse 3.4.2 eclipse-java-ganymede-SR2-win32.zip ③ Google Android SDK android-sdk-windows-1.5_r1.zip ④ ADT-0.9.0 ADT-0.9.0.zip JAVA_HOME=C:\Program Files\Java\jdk1.6.0_13 JAVA_JRE_HOME=C:\Program Files\Java\jdk1.6.0_13\jre JRE_HOME=C:\Program Files\Java\jre6 Android_SDK_HOME =C:\Mobile Phone DEV\Android SDK CLASSPATH=.;%JAVA_HOME%\lib;%JAVA_HOME%\lib\tools.jar;%JAVA_HOME%\lib\dt. jar;%JRE_HOME%\lib;%JRE_HOME%\lib\rt.jar;%JAVA_JRE_HOME%\lib;%JAVA_JRE_HOME% \lib\rt.jar Path= %Android_SDK_HOME%\tools ;%JAVA_HOME%\bin;%JRE_HOME%\bin;%JAVA_JRE _HOME%\bin; 要使用命令行工具必须配置----------------------------------- Android 编程基础 6 ② 解压 Eclipse 3.4.2 ③ 解压 Google Android SDK ④ Eclipse 下安装 ADT 0.9.0 ⑤ 设置 Google Android SDK 路径 解压 eclipse-java-ganymede-SR2-win32.zip 到 C:\Eclipse For Android\ 解压 android-sdk-windows-1.5_r1.zip 到 C:\Mobile Phone DEV\Android SDK 复制 ADT-0.9.0.zip 到 C:\ 打开 C:\Eclipse For Android\eclipse.exe 设置工作路径为 C:\WorkSpace Help->SoftWare Update->find and install ->Search for new features to install ->Next->New Archived site-> 选中 C:\ ADT-0.9.0.zip->OK->Finish->ADT-0.9.0.zip 选勾 ->Next->Accept->Next->Finish- >Install All->Restart “ YES ” Window->preferences-> 选中 Android->SDK Location 中选择 Google Android SDK 的安装路 径 C:\Mobile Phone DEV\Android SDK->OK----------------------------------- Android 编程基础 7 3. HelloWorld 程序实例 ① 新建一个 Android Project � Project name 设置工程名 Hello Google Android � Package name 设置包名 zyf.android.test.hello � Activity name 设置活动名 Hello � Application name 设置应用程序名 Hello � Build Target 设置 AVD API 的版本 3 Android1.5----------------------------------- Android 编程基础 8----------------------------------- Android 编程基础 9 ② 修改 Hello.java 文件 内容如下: ③ 运行 as Android package package package package zyf.android.test.hello; import import import import android.app.Activity; import import import import android.os.Bundle; import import import import android.widget.TextView; public public public public class class class class Hello extends extends extends extends Activity { /** Called when the activity is first created. */ @Override public public public public void void void void onCreate(Bundle savedInstanceState) { super super super super .onCreate(savedInstanceState); // setContentView (R.layout.main); TextView tv = new new new new TextView( this this this this ); tv.setText( " 这是一个测试 Android 的 helloWorld" ); setContentView(tv); } }----------------------------------- Android 编程基础 10 ④ 代码分析: 在 Android 中,用户界面控件被封装成了各种 Class 叫做 Views 。一个 View 是一个可以显示的控件对 象,比如 RadioButton , Animation , TextLable 等。其中的一个简单的控件是 TextView: 传入 TextView 构造函数的参数是一个 Context 对象,通过这个对象可以使用系统提供的功能接口,比 如加载资源,访问数据库和共享数据等等。 Activity 类从 Context 类继承而来,所以 Activity 本身 是 一个 Context ( Java 中的继承概念)。 TextView 对象构建以后就可以设置要显示的数据了。 tv.setText(" 这是一个测试 Android 的 helloWorld"); 最后是连接 TextView 到屏幕 , 类似这样 : setContentView() 方法可以控制具体哪一个控件和系统的 UI 联系起来(我的理 解是设置为主显示 View )。如果没有设置,屏幕中将会显示空白。 ⑤ 结果 TextView tv = new new new new TextView( this this this this ); setContentView(tv);----------------------------------- Android 编程基础 11----------------------------------- Android 编程基础 12 Apache Ant IDE 版本 ------------JDK+Android SDK +Ant 1. 必须软件 2. 安装过程 ① 安装 JAVA JDK SE 1.6 � 设置环境变量 � JAVA_HOME � JAVA_JRE_HOME � JRE_HOME � Android_SDK_HOME � ANT_HOME � CLASSPATH � Path ① JAVA JDK SE 1.6 jdk-6u13-windows-i586-p.exe ② Google Android SDK android-sdk-windows-1.5_r1.zip ③ Apache Ant apache-ant-1.7.1-bin.zip JAVA_HOME=C:\Program Files\Java\jdk1.6.0_13 JAVA_JRE_HOME=C:\Program Files\Java\jdk1.6.0_13\jre JRE_HOME=C:\Program Files\Java\jre6 Android_SDK_HOME =C:\Mobile Phone DEV\Android SDK ANT_HOME=C:\Mobile Phone DEV\Apache Ant\apache-ant-1.7.1 CLASSPATH=.;%ANT_HOME%\lib;%ANT_HOME%\lib\ant.jar;%JAVA_HOME%\lib;%JAV A_HOME%\lib\tools.jar;%JAVA_HOME%\lib\dt.jar;%JRE_HOME%\lib;%JRE_HOME%\lib\r t.jar;%JAVA_JRE_HOME%\lib;%JAVA_JRE_HOME%\lib\rt.jar Path=%ANT_HOME%\bin;%Android_SDK_HOME%\tools;%JAVA_HOME%\bin;%JRE_HO ME%\bin;%JAVA_JRE_HOME%\bin;----------------------------------- Android 编程基础 13 ② 解压 Google Android SDK ③ 解压 apache-ant-1.7.1.zip 3. HelloWorld 程序实例 结果 解压 android-sdk-windows-1.5_r1.zip 到 C:\Mobile Phone DEV\Android SDK 解压 Apache Ant apache-ant-1.7.1.zip 到 C:\Mobile Phone DEV\Apache Ant\apache-ant-1.7.1 ① 开始 -> 运行 ->cmd ② cd C:\Mobile Phone DEV\WorkSpace ③ 使用命令行工具来创建一个新工程 ④ cd Hello ⑤ ant debug ⑥ cd bin ⑦ emulator -avd Android_SDK1.5 ⑧ adb install ./hello-debug.apk ⑨ 在模拟器中运行 hello 程序 android create project -k zyf.hello -n HelloAndroid -t 2 -a AntActivity -p ./Hello----------------------------------- Android 编程基础 14 Linux Linux Linux Linux 平台: JDK+Eclipse+Android SDK+ADT JDK+Android SDK +Ant----------------------------------- Android 编程基础 15 应用解析 Activity Activity Activity Activity : : : : 活动是最基本的 Android 应用程序组件,应用程序中,一个活动通常就是一个单独的屏幕。每一个活动 都被实现为一个独立的类,并且从活动基类中继承而来,活动类将会显示由视图控件组成的用户接口,并 对 事件做出响应。大多数的应用是由多个屏幕显示组成。例如 : 一个文本信息的应用也许有一个显示发送消息 的 联系人列表屏幕,第二个屏幕用来写文本消息和选择收件人,再来一个屏幕查看消息历史或者消息设置操 作 等。这里每一个这样的屏幕就是一个活动,很容易实现从一个屏幕到一个新的屏幕并且完成新的活动。在 某 些情况下当前的屏幕也许需要向上一个屏 幕活动提供返回值 -- 比如让用户从手机中挑选一张照片返回通讯录 做为电话拨入者的头像。 当一个新的屏幕打开后,前一个屏幕将会暂停,并保存在历史堆栈中。用户可以返回到历史堆栈中的 前 一个屏幕。当屏幕不再使用时,还可以从历史堆栈中删除。默认情况下, Android 将会保留从主屏幕到每一 个应用的运行屏幕。 简单理解 Activity 代表一个用户所能看到的屏幕, Activity 主要是处理一个应用的整体性工作,例如, 监 听系统事件 ( 按键事件、触摸屏事件等 ) 、为用户显示指定的 View ,启动其他 Activity 等。所有应用的 Activit y 都继承于 android.app.Activity 类,该类是 Android 提供的基层类,其他的 Activity 继承该父类后,通过 Over ride 父类的方法来实现各种功能,这种设计在其他领域也较为常见。 Intent Intent Intent Intent : : : : 调用 Android 专有类 Intent 进行架构屏幕之间的切换。 Intent 是描述应用想要做什么。 Intent 数据结构两 个最重要的部分是动作和动作对应的数据。典型的动作类型有 :MAIN (活动的门户)、 VIEW 、 PICK 、 EDIT 等。而动作对应的数据则以 URI 的形式进行表示。例如 : 要查看某个人的联系方式,你需要创建一个动作类 型为 VIEW 的 Intent ,以及一个表示这个人的 URI 。 Android 使用了 Intent 这个特殊类,实现在屏幕与屏幕之间移动。 Intent 类用于描述一个应用将会做什 么 事。在 Intent 的描述结构中,有两个最重要的部分:动作和动作对应的数据。典型的动作类型有: MAIN ( a ctivity 的门户)、 VIEW 、 PICK 、 EDIT 等。而动作对应的数据则以 URI 的形式进行表示。例如:要查看一个人的 联 系方式,你需要创建一个动作类型为 VIEW 的 intent ,以及一个表示这个人的 URI 。 与之有关系的一个类叫 IntentFilter 。相对于 intent 是一个有效的做某事的请求,一个 intentfilter 则用于 描 述一个 activity (或者 IntentReceiver )能够操作哪些 intent 。一个 activity 如果要显示一个人的联系方式时, 需 要声明一个 IntentFilter ,这个 IntentFilter 要知道怎么去处理 VIEW 动作和表示一个人的 URI 。 IntentFilter 需 要在 AndroidManifest.xml 中定义。 通过解析各种 intent ,从一个屏幕导航到另一个屏幕是很简单的。当向前导航时, activity 将会调用 startActivity(IntentmyIntent) 方法。然后,系统会在所有安装的应用程序中定义的 IntentFilter 中查找,找到最 匹配 myIntent 的 Intent 对应的 activity 。新的 activity 接收到 myIntent 的通知后,开始运行。当 startActivity 方 法被调用将触发解析 myIntent 的动作,这个机制提供了两个关键好处:----------------------------------- Android 编程基础 16 A 、 Activities 能够重复利用从其它组件中以 Intent 的形式产生的一个请求; B 、 Activities 可以在任何时候被一个具有相同 IntentFilter 的新的 Activity 取代。 IntentReceiver: IntentReceiver: IntentReceiver: IntentReceiver: 当你希望你的应用能够对一个外部的事件 ( 如当电话呼入时,或者数据网络可用时,或者到了晚上时 ) 做出响 应,你可以使用一个 IntentReceiver 。虽然 IntentReceiver 在感兴趣的事件发生时,会使用 NotificationManage r 通知用户,但它并不能生成一个 UI 。 IntentReceiver 在 AndroidManifest.xml 中注册,但也可以在代码中使用 Context.registerReceiver() 进行注册。当一个 intentreceiver 被触发时,你的应用不必对请求调用 inten treceiver , 系统会在需要的时候启动你的应用。各种应用还可以通过使用 Context.broadcastIntent() 将它们自己的 intentreceiver 广播给其它应用程序。 Service Service Service Service : : : : 一个 Service 是一段长生命周期的,没有用户界面的程序。比较好的一个例子就是一个正在从播放列表中 播放歌曲的媒体播放器。在一个媒体播放器的应用中,应该会有多个 activity ,让使用者可以选择歌曲并播 放 歌曲。然而,音乐重放这个功能并没有对应的 activity ,因为使用者当然会认为在导航到其它屏幕时音乐应 该 还在播放的。在这个例子中,媒体播放器这个 activity 会使用 Context.startService() 来启动一个 service ,从而 可以在后台保持音乐的播放。同时,系统也将保持这个 service 一直执行,直到这个 service 运行结束。另外 , 我们还可以通过使用 Context.bindService() 方法,连接到一个 service 上(如果这个 service 还没有运行将启动 它)。当连接到一个 service 之后,我们还可以 service 提供的接口与它进行通讯。拿媒体播放器这个例子来 说 , 我们还可以进行暂停、重播等操作。 Content Content Content Content Provider Provider Provider Provider : : : : Android 应用程序能够将它们的数据保存到文件、 SQLite 数据库中,甚至是任何有效的设备中。当你想 将你的应用数据与其它的应用共享时,内容提供器就可以发挥作用了。因为内容提供器类实现了一组标准 的 方法,从而能够让其它的应用保存或读取此内容提供器处理的各种数据类型。 数据是应用的核心。在 Android 中,默认使用鼎鼎大名的 SQLite 作为系统 DB 。但是在 Android 中,使用方 法有点小小的不一样。在 Android 中每一个应用都运行在各自的进程中,当你的应用需要访问其他应用的数 据时,也就需要数据在不同的虚拟机之间传递,这样的情况操作起来可能有些困难 ( 正常情况下,你不能读 取 其他的应用的 db 文件 ) , ContentProvider 正是用来解决在不同的应用包之间共享数据的工具。 � 所有被一个 Android 应用程序创建的偏好设置,文件和数据库都是私有的。 � 为了和其他应用程序共享数据,应用程序不得不创建一个 Content Provider � 要回索其他应用程序的数据,它自己的 Content Provider 必须被调用 � Android 本地 Content Provider 包括: � CallLog :地址和接收到的电话信息 � Contact.People.Phones :存储电话号码 � Setting.System :系统设置和偏好设置 � 等等----------------------------------- Android 编程基础 17 封面----------------------------------- Android 编程基础 1 封面----------------------------------- Android 编程基础 2 Android Android Android Android 虚拟机 Dalvik Dalvik Dalvik Dalvik Dalvik Dalvik Dalvik Dalvik 冲击 随着 Google 的 AndroidSDK 的发布,关于它的 API 以及在移动电话领域所带来的预 期影响这些方面的讨论不胜枚举。不过,其中的一个话题在 Java 社区是一石激起千层浪, 这就是 Android 平台的基础 —— Dalvik 虚拟机。 Dalvik Dalvik Dalvik Dalvik 和标准 Java Java Java Java 虚拟机 (JVM) (JVM) (JVM) (JVM) 首要差别 Dalvik 基于寄存器,而 JVM 基于栈。,基于寄存器的虚拟机对于更大的程序来说,在它们编译的时候,花 费 的时间更短。 Dalvik Dalvik Dalvik Dalvik 和 Java Java Java Java 运行环境的区别 Dalvik 经过优化,允许在有限的内存中同时运行多个虚拟机的实例,并且每一个 Dalvik 应用作为一个独立 的 Linux 进程执行。独立的进程可以防止在虚拟机崩溃的时候所有程序都被关闭 . Dalvik Dalvik Dalvik Dalvik 形势 Dalvik 的诞生也导致人们开始忧虑 Java 平台的第一次大规模的分道扬镳或许已经是进行时了 —— 有人已经 把 Davlik 和微软的 JVM 以及 Sun 对微软的诉讼联系起来,等着看 Google 身上是否也会发生类似事情;另外 一 些人则指出, Google 并没有宣称 Dalvik 是一个 Java 实现,而微软却是这样做的。 Sun 也对可能带来的阵营 分裂表达了忧虑情绪,并提出和 Google 合作来保证 Dalvik 和 JVM 之间的兼容性 —— Google 对此的解释是, Dalvik 是对解决目前 JavaME 平台上分裂的一次尝试,也是为了提供一个拥 有较少限制许可证的平台。甚至 还有人怀疑这是否是 Sun 和 Google 两大阵营对 Java 之未来的一次大规模较量。----------------------------------- Android 编程基础 3 Android Android Android Android 中各种 JAVA JAVA JAVA JAVA 包的功能描述 在 Android 的应用程序开发中,通常使用的是 JAVA 语言,除了需要熟悉 JAVA 语 言的基础知识之外,还需要了解 Android 提供的扩展的 JAVA 功能。 在一般的 JAVA 应用中,如果需用引用基础类库,通常需要使用如下的方式: import javax.swing.*; 以上代码表示了引用 JAVA 的 GUI 组件 Swing,javax.swing 即 JAVA 中的一个包。 android 提供一些扩展的 JAVA 类库,类库分为若干个包,每个包中包含若干个类。 重要包的描述: android.app :提供高层的程序模型、提供基本的运行环境 android.content :包含各种的对设备上的数据进行访问和发布的类 android.database :通过内容提供者浏览和操作数据库 android.graphics :底层的图形库,包含画布,颜色过滤,点,矩形,可以将他们直接绘制到屏幕上 . android.location :定位和相关服务的类 android.media :提供一些类管理多种音频、视频的媒体接口 android.net :提供帮助网络访问的类,超过通常的 java.net.* 接口 android.os :提供了系统服务、消息传输、 IPC 机制 android.opengl :提供 OpenGL 的工具 android.provider :提供类访问 Android 的内容提供者 android.telephony :提供与拨打电话相关的 API 交互 android.view :提供基础的用户界面接口框架 android.util :涉及工具性的方法,例如时间日期的操作 android.webkit :默认浏览器操作接口 android.widget :包含各种 UI 元素(大部分是可见的)在应用程序的屏幕中使用----------------------------------- Android 编程基础 4 Android Android Android Android 的相关文件类型 Java Java Java Java 文件 ----- ----- ----- ----- 应用程序源文件 android 本身相当一部分都是用 java 编写而成 ( 基本上架构图里头蓝色的部份都是用 Java 开发的 ) , android 的 应用必须使用 java 来开发。 Class Class Class Class 文件 ------Java ------Java ------Java ------Java 编译后的目标文件 不像 J2se , java 编译成 class 就可以直接运行, android 平台上 class 文件不能直接在 android 上运行。由于 G oogle 使用了自己的 Dalvik 来运行应用,所以这里的 class 也肯定不能在 AndroidDalvik 的 java 环境中运行, androi d 的 class 文件实际上只是编译过程中的中间目标文件,需要链接成 dex 文件后才能在 dalvik 上运行。 Dex Dex Dex Dex 文件 -----Android -----Android -----Android -----Android 平台上的可执行文件 Android 虚拟机 Dalvik 支持的字节码文件格式 Google 在新发布的 Android 平台上使用了自己的 Dalvik 虚拟 机 来定义,这种虚拟机执行的并非 Java 字节码,而是另一种字节码: dex 格式的字节码。在编译 Java 代码之 后 , 通过 Android 平台上的工具可以将 Java 字节码转换成 Dex 字节码。虽然 Google 称 Dalvik 是为了移动设备定 做的,但是业界很多人认为这是为了规避向 sun 申请 Javalicense 。这个 DalvikVM 针对手机程式 /CPU 做过 最 佳化,可以同时执行许多 VM 而不会占用太多 Res ource 。 Apk Apk Apk Apk 文件 -------Android -------Android -------Android -------Android 上的安装文件 Apk 是 Android 安装包的扩展名,一个 Android 安装包包含了与某个 Android 应用程序相关的所有文件。 apk 文件将 AndroidManifest.xml 文件、应用程序代码 (.dex 文件 ) 、资源文件和其他文件打成一个压缩包。一个工 程只能打进一个 .apk 文件。----------------------------------- Android 编程基础 5 Android Android Android Android 的应用程序结构分析: HelloActivity 本例以一个简单的 HelloActivity 程序为例,简单介绍 Android 应用程序的源代码结构。事实 上, Android 应用程序虽然不是很复杂,但是通常涉及了 JAVA 程序 ,XML 文件, Makefile 多方面的内容。 HelloActivity 虽然简单,但是麻雀虽小,五脏俱全,是学习 Android 应用程 序的最好示例。 第一部分: HelloActivity HelloActivity HelloActivity HelloActivity 的源代码 HelloActivity 工程的源代码在 Android 目录的 development/samples/HelloActivity/ 中,代码的 结构如下所示: 其中 tests 是一个独立的项目,可以暂时不考虑。其他部分看作一个 Android 的一应用程序 的工程。这个工程主要的组成部分如下所示: AndroidManifest.xml :工程的描述文件,在运行时有用处 Android.mk :整个工程的 Makefile development/samples/HelloActivity/ |-- Android.mk |-- AndroidManifest.xml |-- res | |-- layout | | `-- hello_activity.xml | `-- values | `-- strings.xml |-- src | `-- com | `-- example | `-- android | `-- helloactivity | `-- HelloActivity.java `-- tests |-- Android.mk |-- AndroidManifest.xml `-- src `-- com `-- android `-- helloactivity `-- HelloActivityTest.java----------------------------------- Android 编程基础 6 res :放置资源文件的目录 src/com/example/android/helloactivity/HelloActivity.java :这是 JAVA 类文件,这个文件的路径 表示在 Andorid 的 JAVA 包的结构中的位置, 这个包的使用方式为 com.example.android.helloactivity 。 第二部分: 编译的中间结果 这个 HelloActivity 工程经过编译后将生成 out/target/common/obj/APPS/He lloActivity_intermediates/ 目录, 这个目录中的内容都是 HelloActivity 工程相关的, 更具体地说都与 development/samples/HelloActivity/ 中的 Android.mk 文件相关。 classes.dex 是一个最重要的文件,它是给 Android 的 JAVA 虚拟机 Dalvik 运行的字节码文 件。 classes.jar 是一个 JAR 文件, JAR 的含义为 Java ARchive ,也就是 Java 归档,是一种与平台 无关的文件格式,可将多个文件合成一个文件。解压缩之后的目录结构: (JAVA 标准编译得 到的类 ) out/target/common/obj/APPS/He lloActivity_intermediates/ |-- classes.dex (字节码) |-- classes.jar ( JAR 文件 ) |-- public_resources.xml (根据 resources 结构生成的 xml ) `-- src |-- R.stamp `-- com `-- example `-- android `-- helloactivity `-- R.java ( resources 生成的文件)----------------------------------- Android 编程基础 7 各个以 class 为扩展名的文件,事实上是 JAVA 程序经过编译后的各个类的字节码。 第三部分: 目标 apk apk apk apk 文件 目标 apk 文件是 Android 的 JAVA 虚拟机 Dalvik 安装和运行的文件,事实上这个 apk 文件将 由编译的中间结果和原始文件生成。 apk 文件的本质是一个 zip 包。这个 APK 包解压缩后的 目录结构如下所示: 值得注意的是,这里的 xml 文件经过了处理,和原始的文件不太一样,不能按照文本文件 的方式阅读。 classes |-- META-INF | `-- MANIFEST.MF `-- com `-- example `-- android `-- helloactivity |-- HelloActivity.class |-- R$attr.class |-- R$id.class |-- R$layout.class |-- R$string.class `-- R.class out/target/product/generic/obj/APPS/HelloActivity_intermediates/package.apk_FILES/ |-- AndroidManifest.xml |-- META-INF | |-- CERT.RSA | |-- CERT.SF | `-- MANIFEST.MF |-- classes.dex |-- res | `-- layout | `-- hello_activity.xml `-- resources.arsc----------------------------------- Android 编程基础 8 第四部分: 源代码的各个文件 Android.mk 是整个工程的 “ Makefile ” ,其内容如下所示: � LOCAL_PATH:= $(call my-dir) � include $(CLEAR_VARS) � LOCAL_MODULE_TAGS := samples � # Only compile source java files in this apk. � LOCAL_SRC_FILES := $(call all-java-files-under, src) � LOCAL_PACKAGE_NAME := HelloActivity � LOCAL_SDK_VERSION := current � include $(BUILD_PACKAGE) � # Use the following include to make our test apk. � include $(call all-makefiles-under,$(LOCAL_PATH)) 这个文件在各个 Android 的工程中都是类似的,其中 LOCAL_PACKAGE_NAME 表示了这 个包的名字。 LOCAL_MODULE_TAGS 表示了模块的标,在这里使用的是 samples ,正式的应用程序( packages 目录中的应用)中多使用 eng development 。 AndroidManifest.xml 是这个 HelloActivity 工程的描述文件,其内容如下所示: 其中 package 用于说明这个包的名称, android:labeapplication 中的内容是表示这个应用程序 在界面上显示的标题, activity 中的 android:name 表示这个 Android 的活动的名称。 ----------------------------------- Android 编程基础 9 文件 src/com/example/android/helloactivity/HelloActivity.java 是程序主要文件,由 JAVA 语言 写成 com.example.android.helloactivity 表示的是这个包的名称 , 在文件的头部引入了两个包 android.app.Activity 是一个 Android 活动( Activity )包,每一个 Android 活动都需要继承 Activity 类。 包 android.os.Bundle 用于映射字符串的值。 onCreate() 是一个重载的函数,在这个函数中实现应用程序创建的所执行的过程。其中 setContentView() 设置当前的视图( View )。 设置的方法是使用一个文件,这个文件因此决定了视图中包含的内容。这里使用的是 R.layout.hello_activity ,表示从 res/layout/ 目录中使用 hello_activity.xml 文件。 res/layout/hello_activity.xml 文件的内容如下所示: 其中定义了一个可编辑的文本( EditText ),下面的各项其实是它的各种属性, android:text 表示这个文本 的 内 容 ,string/hello_activity_text_text 表 示 找 到 相 应 的 文 件 , 也 就 是 res/value/string.xml 文 件 中 的 hello_activity_text_text 文本。 res/value/string.xml 的内容如下所示: hello_activity_text_text 文本被 res/layout/hello_activity.xml 文件引用,正是应用程序运行时在 屏幕显示的文本。 package package package package com.example.android.helloactivity; import import import import android.app.Activity; import import import import android.os.Bundle; public public public public class class class class HelloActivity extends extends extends extends Activity { public public public public HelloActivity() { } @ Override public public public public void void void void onCreate(Bundle savedInstanceState) { super super super super .onCreate(savedInstanceState); setContentView(R.layout.hello_activity); } } He llo , World! ----------------------------------- Android 编程基础 10 Android Android Android Android ADB ADB ADB ADB 工具使用 adb(Android Debug Bridge) 是 Android 提供的一个通用调试工具,借助这个工具,我妈可以管理设备或手机 模 拟器的状态。 adb adb adb adb 功能操作: � 快速更新设备或手机模拟器中的代码,如应用或 Android 系统升级 � 在设备上运行 shell 命令 � 管理设备或手机模拟器上预定端口 � 在设备或手机模拟器上复制、粘贴文件 adb adb adb adb 常用操作: 安装应用到模拟器 Android 没有提供一个卸载应用的命令,只能手动删除: 进入设备或模拟器的 Shell 通过以上命令,可以进入设备或模拟器的 shell 环境中,在这个 Linux Shell 中,你可以执行各种 Linux 的命 令 , 另外如果只想执行一条 shell 命令,可以采用以下方式: 如: 会打印出内核的调试信息 发布端口 可以设置任意的端口号,做为主机向模拟器或设备的请求端口。如 : adb install app.apk adb shell cd data/app rm app.apk adb shell adb shell [command] adb shell dmesg adb forward tcp:5555 tcp:8000----------------------------------- Android 编程基础 11 复制文件 可向一个设备或从一个设备中复制文件 � 复制一个文件或目录到设备或模拟器上: 如: � 从设备或模拟器上复制一个文件或目录 如: 搜索 / 等待模拟器、设备实例 取得当前运行的模拟器、设备的实例列表及每个实例的状态 | 等待正在运行的设备 查看 Bug 报告 记录无线通讯日志 无线通讯记录日志非常多,在运行时没必要记录,可以通过命令设置记录 获取设备 ID 和序列号 访问数据库 SQLite3 adb push adb push test.txt /tmp/test.txt adb pull adb pull /android/lib/libwebcore.os adb devices adb wait-for-device adb bugreport adb shell logcat -b radio adb get-product adb get-serialno adb shell sqlite3----------------------------------- Android 编程基础 12 封面----------------------------------- Android 编程基础 1 封面----------------------------------- Android 编程基础 2 Android Android Android Android 模拟器 模拟器参数 参数格式 option 选项 emulator [option] [-qemu args] -sysdir 为模拟器在 目录中搜索系统硬盘镜像 -system 为模拟器从 文件中读取初始化系统镜像 -datadir 设置用户数据写入的目录 -kernel 为模拟器设置使用指定的模拟器内核 -ramdisk 设置内存 RAM 镜像文件 ( 默认为 /ramdisk.img) -image 废弃,使用 -system 替代 -init-data 设置初始化数据镜像 ( 默认为 /userdata.img) -initdata 和 "-init-data " 使用方法一致 -data 设置数据镜像 ( 默认为 /userdata-qemu.img) -partition-size system/data 分区容量大小 (MB) -cache 设置模拟器缓存分区镜像 ( 默认为 零时文件 ) -no-cache 禁用缓存分区 -nocache 与 "-no-cache" 使用方法相同 -sdcard 指定模拟器 SDCard 镜像文件 ( 默认为 /sdcard.img) -wipe-data 清除并重置用户数据镜像 ( 从 initdata 拷贝 ) -avd 指定模拟器使用 Android 虚拟设备 -skindir 设置模拟器皮肤 在 目录中搜索皮肤 ( 默认为 /skins 目录 ) -skin 选择使用给定的皮肤 -no-skin 不适用任何模拟器皮肤 -noskin 使用方法与 "-no-skin" 相同 -memory 物理 RAM 内存大小 (MB) -netspeed 设置最大网络下载、上传速度 -netdelay 网络时延模拟 -netfast 禁用网络形态 -tarce 代码配置可用 -show-kernel 显示内核信息 -shell 在当前终端中使用根 Shell 命令 -no-jni Dalvik 运行时禁用 JNI 检测 -nojni 使用方法与 "-no-jni" 相同 -logcat 输出给定 tag 的 Logcat 信息----------------------------------- Android 编程基础 3 -no-audio 禁用音频支持 -noaudio 与 "-no-audio" 用法相同 -audio 使用指定的音频 backend -audio-in 使用指定的输入音频 backend -audoi-out 使用指定的输出音频 backend -raw-keys 禁用 Unicode 键盘翻转图 -radio 重定向无线模式接口到个性化设备 -port 设置控制台使用的 TCP 端口 -ports , 设置控制台使用的 TCP 端口和 ADB 调试桥使用的 TCP 端口 -onion 在屏幕上层使用覆盖 PNG 图片 -onion-alpha 指定上层皮肤半透明度 -onion-rotation 0|1|2|3 指定上层皮肤旋转 -scale 调节模拟器窗口尺寸 ( 三种: 1.0-3.0 、 dpi 、 auto) -dpi-device 设置设备的 resolution (dpi 单位 ) ( 默认 165) -http-proxy 通过一个 HTTP 或 HTTPS 代理来创建 TCP 连接 -timezone 使用给定的时区,而不是主机默认的 -dns-server 在模拟系统上使用给定的 DNS 服务 -cpu-delay 调节 CUP 模拟 -no-boot-anim 禁用动画来快速启动 -no-window 禁用图形化窗口显示 -version 显示模拟器版本号 -report-console 向远程 socket 报告控制台端口 -gps 重定向 GPS 导航到个性化设备 -keyset 指定按键设置文件名 -shell-serial 根 shell 的个性化设备 -old-system 支持旧版本 (pre 1.4) 系统镜像 -tcpdump 把网络数据包捕获到文件中 -bootchart bootcharting 可用 -qemu args.... 向 qemu 传递参数 -qemu -h 显示 qemu 帮助 -verbose 和 "-debug-init" 相同 -debug 可用、禁用调试信息 -debug- 使指定的调试信息可用 -debug-no- 禁用指定的调试信息 -help 打印出该帮助文档 -help- 打印出指定 option 的帮助文档 -help-disk-images 关于硬盘镜像帮助 -help-keys 支持按钮捆绑 ( 手机快捷键 ) -help-debug-tags 显示出 -debug 命令中的 tag 可选值 -help-char-devices 个性化设备说明 -help-environment 环境变量 -help-keyset-file 指定按键绑定设置文件 -help-virtula-device 虚拟设备管理----------------------------------- Android 编程基础 4 -help-sdk-images 当使用 SDK 时关于硬盘镜像的信息 -help-build-images 当构建 Android 时,关于硬盘镜像的信息 -help-all 打印出所有帮助----------------------------------- Android 编程基础 5 进程: 在 Android 中,进程完全是应用程序的实现细节,不是用户一般想象的那样。 它们的用途很简单: � 通过把不信任或是不稳定的代码放到其他进程中来提高稳定性或是安全性 � 通过在相同的进程中运行多个 .apk 代码来减少消耗 � 通过把重量级代码放入一个分开的进程中来帮助系统管理资源。该分开的进程可以被应用程序的其他 部 分单独地杀死 � 如果两个没有共享相同的用户 ID 的 .apk 试图在相同的进程中运行,这将不被允许,并且系统会为每一 个 apk 程序创建不同的进程会 线程 � Android 让一个应用程序在单独的线程中,指导它创建自己的线程 � 应用程序组件( Activity 、 service 、 broadcast receiver )所有都在理想的主线程中实例化 � 没有一个组件应该执行长时间或是阻塞操作 ( 例如网络呼叫或是计算循环 ) 当被系统调用时,这将中断所 有在该进程的其他组件 � 你可以创建一个新的线程来执行长期操作----------------------------------- Android 编程基础 6 Android Android Android Android 释放手机资源,进程释放优先级 当系统资源消耗, Android 将会杀死一些进程来释放资源。 进程优先级顺序: ① 前台进程: 包含一个前台 Activity 、包含一个正在运行的广播接收器、正在运行的服务(当前用户所需的 Activity 、 正在屏幕顶层运行的 Activity ) ② 可视进程: 包含一个可视化的 Activity ( Activity 可视的,但是不是在前台的( onPause ))、例如显示在一个前台对 话框之后的以前的 Activity ) ③ 服务进程: 包含一个被开启的服务 ( 处理服务,不是直接可视,例如媒体播放器,网络上传、下载 ) ④ 后台进程: 包含一个不可视的 Activity( 带有一个当前不可视的 Activity 、可以在任意时刻杀死该进程来回收内存 ) ⑤ 空进程 没有持有任何应用程序组件----------------------------------- Android 编程基础 7 Android Android Android Android 应用开发 1 1 1 1 分析 Hello Hello Hello Hello Android Android Android Android 打开 Hello Android 工程 Main.xml src 文件夹 HelloAndroid.java R.java Android Library Assets 文件夹 源文件 主程序文件 资源文件 Java 库 静态文件 打包 res 文件夹 drawable 文件夹 layout 文件夹 values 文件夹 程序图标 (ico.png) 布局 UI (main.xml) 程序用到的 String 、颜色 **(string.xml) AndroidMainfest.xml 描述应用程序、构成、组件、权限 bin 文件夹 classes.dex HelloAndroid.apk 自定义的包文件夹 编译的 java 二进制 码 Android 安装包 (APK 包 ) 存放编译后的字节码文件 整体布局 表示线性布局 xmlns:android = "http://schemas.android.com/apk/res/android" 名字空间 android:orientation = "vertical" 控件布局 垂直往下布局 android:layout_width = "fill_parent" android:layout_height = "fill_parent" 上层控件填充满 图形空间 派生于 View ----------------------------------- Android 编程基础 8 R.java 通过 res 文件夹下的 xml 文件定义自动生成的, main.xml ico.png string.xml 是配套的关联,进行修改后 R.java 自动重新生成 AndroidManifest.xml 有关版本,程序信息, java 包,程序图标,程序记录信息等。 Manifest.xml 文件轮廓 ----------------------------------- Android 编程基础 9 添加编辑框与按钮 package package package package zyf.Study.AndroidSturdyByMyself; import import import import android.app.Activity; import import import import android.os.Bundle; import import import import android.view.View; import import import import android.view.View.OnClickListener; import import import import android.widget.Button; import import import import android.widget.EditText; import import import import android.widget.TextView; public public public public class class class class AndroidSturdyByMyself extends extends extends extends Activity { private private private private EditText getNameEditText ; private private private private Button button_Login ; private private private private TextView show_Login_TextView ; /** Called when the activity is first created. */ @Override public public public public void void void void onCreate(Bundle savedInstanceState) { super super super super .onCreate(savedInstanceState); setContentView(R.layout. main ); getNameEditText =(EditText)findViewById(R.id. widget29_getName_EditText ); button_Login =(Button)findViewById(R.id. widget30_Login_Button ); show_Login_TextView =(TextView)findViewById(R.id. widget31_showLogin_TextView ); button_Login .setOnClickListener( new new new new OnClickListener(){ @Override public public public public void void void void onClick(View v) { // TODO TODO TODO TODO Auto-generated method stub show_Login_TextView .setText( getNameEditText .getText()+ " 欢迎您进入 " ); } }); } }----------------------------------- Android 编程基础 10 使用 Intent Intent Intent Intent 启动另一个 Activity Activity Activity Activity 在多个 Activity Activity Activity Activity 之间切换时候,注意每个 Activity Activity Activity Activity 都应在 AndroidManifest.xml AndroidManifest.xml AndroidManifest.xml AndroidManifest.xml 中有所声 明定义(如下) 在不同 Task Task Task Task 中启动 Activity Activity Activity Activity Intent.FLAG_ACTIVITY_NEW_TASK Intent showNextPage_Intent= new new new new Intent(); showNextPage_Intent.setClass(UsingBundel. this this th
这是书的光盘。共分为两部分,这是第二部分。 本书由浅入深、循序渐进地介绍了Windows驱动程序的开发方法与调试技巧。本书共分23章,内容涵盖了Windows操作系统的基本原理、NT驱动程序与WDM驱动程序的构造、驱动程序中的同步异步处理方法、驱动程序中即插即用功能、驱动程序的各种调试技巧等。同时,还针对流行的PCI驱动程序、USB驱动程序、虚拟串口驱动程序、摄像头驱动程序、SDIO驱动程序进行了详细的介绍,本书最大的特色在于每一节的例子都是经过精挑细选的,具有很强的针对性。力求让读者通过亲自动手实验,掌握各类Windows驱动程序的开发技巧,学习尽可能多的Windows底层知识。   本书适用于中、高级系统程序员,同时也可用做高校计算机专业操作系统实验课的补充教材。 原创经典,威盛一线工程师倾力打造。深入驱动核心,剖析操作系统底层运行机制,通过实例引导,快速学习编译、安装、调试的方法。   从Windows最基本的两类驱动程序的编译、安装、调试入手讲解,非常容易上手,用实例详细讲解PCI、USB、虚拟串口、虚拟摄像头、SDIO等驱动程序的开发,归纳了多种调试驱动程序的高级技巧,如用WinDBG和VMWARE软件对驱动进行源码级调试,深入Windows操作系统的底层和内核,透析Windows驱动开发的本质。 本书是作者结合教学和科研实践经验编写而成的,不仅详细介绍了Windows内核原理,而且介绍了编程技巧和应用实例,兼顾了在校研究生和工程技术人员的实际需求,对教学、生产和科研有现实的指导意义,是一本值得推荐的专著。              ——中国工程院院士   院士推荐   目前,电子系统设计广泛采用通用操作系统,达到降低系统的设计难度和缩短研发周期。实现操作系统与硬件快速信息交换是电子系统设计的关键。   通用操作系统硬件驱动程序的开发,编写者不仅需要精通硬件设备、计算机总线,而且需要Windows操作系统知识以及调试技巧。学习和掌握Windows硬件驱动程序的开发是电子系统设计人员必备的能力。   本书是作者结合教学和科研实践经验编写而成的,不仅详细介绍了Windows内核原理,并且介绍了编程技巧和应用实例,兼顾了在校研究生和工程技术人员的实际需求,对教学、生产和科研有现实的指导意义,是一本值得推荐的专著。 第1篇 入门篇 第1章 从两个最简单的驱动谈起 本章向读者呈现两个最简单的Windows驱动程序,一个是NT式的驱动程序,另一个是WDM式的驱动程序。这两个驱动程序没有操作具体的硬件设备,只是在系统里创建了虚拟设备。在随后的章节中,它们会作为基本驱动程序框架,被本书其他章节的驱动程序开发所复用。笔者将带领读者编写代码、编译、安装和调试程序。   1.1 DDK的安装   1.2 第一个驱动程序HelloDDK的代码分析    1.2.1 HelloDDK的头文件    1.2.2 HelloDDK的入口函数    1.2.3 创建设备例程    1.2.4 卸载驱动例程    1.2.5 默认派遣例程   1.3 HelloDDK的编译和安装    1.3.1 用DDK环境编译HelloDDK    1.3.2 用VC集成开发环境编译HelloDDK    1.3.3 HelloDDK的安装   1.4 第二个驱动程序HelloWDM的代码分析    1.4.1 HelloWDM的头文件    1.4.2 HelloWDM的入口函数    1.4.3 HelloWDM的AddDevice例程    1.4.4 HelloWDM处理PNP的回调函数    1.4.5 HelloWDM对PNP的默认处理    1.4.6 HelloWDM对IRP_MN_REMOVE_DEVICE的处理    1.4.7 HelloWDM对其他IRP的回调函数    1.4.8 HelloWDM的卸载例程   1.5 HelloWDM的编译和安装    1.5.1 用DDK编译环境编译HelloWDM    1.5.2 HelloWDM的编译过程    1.5.3 安装HelloWDM   1.6 小结  第2章 Windows操作驱动的基本概念  驱动程序被操作系统加载在内核模式下,它与Windows操作系统内核的其他组件进行密切交互。本章主要介绍Windows操作系统内核的基本概念,同时还介绍应用程序和驱动程序之间的通信方法。   2.1 Windows操作系统概述    2.1.1 Windows家族    2.1.2 Windows特性    2.1.3 用户模式和内核模式    2.1.4 操作系统与应用程序   2.2 操作系统分层    2.2.1 Windows操作系统总体架构    2.2.2 应用程序与Win32子系统    2.2.3 其他环境子系统    2.2.4 Native API    2.2.5 系统服务    2.2.6 执行程序组件    2.2.7 驱动程序    2.2.8 内核    2.2.9 硬件抽象层    2.2.10 Windows与微内核   2.3 从应用程序到驱动程序   2.4 小结  第3章 Windows驱动编译环境配置、安装及调试  本章将带领读者一步步对驱动程序进行编译、安装和简单的调试工作。这些步骤虽然简单,但往往困惑着初次接触驱动程序的开发者。  3.1 用C语言还是用C++语言    3.1.1 调用约定    3.1.2 函数的导出名    3.1.3 运行时函数的调用   3.2 用DDK编译环境编译驱动程序    3.2.1 编译版本    3.2.2 nmake工具    3.2.3 build工具    3.2.4 makefile文件    3.2.5 dirs文件    3.2.6 sources文件    3.2.7 makefile.inc文件    3.2.8 build工具的环境变量    3.2.9 build工具的命令行参数   3.3 用VC编译驱动程序    3.3.1 建立驱动程序工程    3.3.2 修改编译选项    3.3.3 修改链接选项    3.3.4 其他修改    3.3.5 VC编译小结   3.4 查看调试信息    3.4.1 打印调试语句    3.4.2 查看调试语句   3.5 手动加载NT式驱动   3.6 编写程序加载NT式驱动    3.6.1 SCM组件和Windows服务    3.6.2 加载NT驱动的代码    3.6.3 卸载NT驱动的代码    3.6.4 实验   3.7 WDM式驱动的加载    3.7.1 WDM的手动安装    3.7.2 简单的INF文件剖析   3.8 WDM设备安装在注册表中的变化    3.8.1 硬件子键    3.8.2 类子键    3.8.3 服务子键   3.9 小结  第4章 驱动程序的基本结构  本章首先对Windows驱动程序的两个重要数据结构进行介绍,分别是驱动对象和设备对象数据结构。另外还要介绍NT驱动程序和WDM驱动程序的入口函数、卸载例程、各种IRP派遣上函数等。   4.1 Windows驱动程序中重要的数据结构    4.1.1 驱动对象(DRIVER_OBJECT)    4.1.2 设备对象(DEVICE_OBJECT)    4.1.3 设备扩展   4.2 NT式驱动的基本结构    4.2.1 驱动加载过程与驱动入口函数(DriverEntry)    4.2.2 创建设备对象    4.2.3 DriverUnload例程    4.2.4 用WinObj观察驱动对象和设备对象    4.2.5 用DeviceTree观察驱动对象和设备对象   4.3 WDM式驱动的基本结构    4.3.1 物理设备对象与功能设备对象    4.3.2 WDM驱动的入口程序    4.3.3 WDM驱动的AddDevice例程    4.3.4 DriverUnload例程    4.3.5 对IRP_MN_REMOVE_DEVICE IRP的处理    4.3.6 用Device Tree查看WDM设备对象栈   4.4 设备的层次结构    4.4.1 驱动程序的垂直层次结构    4.4.2 驱动程序的水平层次结构    4.4.3 驱动程序的复杂层次结构   4.5 实验    4.5.1 改写HelloDDK查看驱动结构    4.5.2 改写HelloWDM查看驱动结构   4.6 小结  第5章 Windows内存管理   本章围绕着驱动程序中的内存操作进行了介绍。在驱动程序开发中,首先要注意分页内存和非分页内存的使用。同时,还需要区分物理内存地址和虚拟内存地址这两个概念。   5.1 内存管理概念    5.1.1 物理内存概念(Physical Memory Address)    5.1.2 虚拟内存地址概念(Virtual Memory Address)    5.1.3 用户模式地址和内核模式地址    5.1.4 Windows驱动程序和进程的关系    5.1.5 分页与非分页内存    5.1.6 分配内核内存   5.2 在驱动中使用链表    5.2.1 链表结构    5.2.2 链表初始化    5.2.3 从首部插入链表    5.2.4 从尾部插入链表    5.2.5 从链表删除    5.2.6 实验   5.3 Lookaside结构    5.3.1 频繁申请内存的弊端    5.3.2 使用Lookaside    5.3.3 实验   5.4 运行时函数    5.4.1 内存间复制(非重叠)    5.4.2 内存间复制(可重叠)    5.4.3 填充内存    5.4.4 内存比较    5.4.5 关于运行时函数使用的注意事项    5.4.6 实验   5.5 使用C++特性分配内存   5.6 其他    5.6.1 数据类型    5.6.2 返回状态值    5.6.3 检查内存可用性    5.6.4 结构化异常处理(try-except块)    5.6.5 结构化异常处理(try-finally块)    5.6.6 使用宏需要注意的地方    5.6.7 断言   5.7 小结 第6章 Windows内核函数  本章介绍了Windows内核模式下的一些常用内核函数,这些函数在驱动程序的开发中将会经常用到。   6.1 内核模式下的字符串操作    6.1.1 ASCII字符串和宽字符串    6.1.2 ANSI_STRING字符串与UNICODE_STRING字符串    6.1.3 字符初始化与销毁    6.1.4 字符串复制    6.1.5 字符串比较    6.1.6 字符串转化成大写    6.1.7 字符串与整型数字相互转换    6.1.8 ANSI_STRING字符串与UNICODE_STRING字符串相互转换   6.2 内核模式下的文件操作    6.2.1 文件的创建    6.2.2 文件的打开    6.2.3 获取或修改文件属性    6.2.4 文件的写操作    6.2.5 文件的读操作   6.3 内核模式下的注册表操作    6.3.1 创建关闭注册表    6.3.2 打开注册表    6.3.3 添加、修改注册表键值    6.3.4 查询注册表    6.3.5 枚举子项    6.3.6 枚举子键    6.3.7 删除子项    6.3.8 其他   6.4 小结  第7章 派遣函数   本章重点介绍了驱动程序中的处理IRP请求的派遣函数。所有对设备的操作最终将转化为IRP请求,这些IRP请求会被传送到派遣函数处理。   7.1 IRP与派遣函数    7.1.1 IRP    7.1.2 IRP类型    7.1.3 对派遣函数的简单处理    7.1.4 通过设备链接打开设备    7.1.5 编写一个更通用的派遣函数    7.1.6 跟踪IRP的利器IRPTrace   7.2 缓冲区方式读写操作    7.2.1 缓冲区设备    7.2.2 缓冲区设备读写    7.2.3 缓冲区设备模拟文件读写   7.3 直接方式读写操作    7.3.1 直接读取设备    7.3.2 直接读取设备的读写   7.4 其他方式读写操作    7.4.1 其他方式设备    7.4.2 其他方式读写   7.5 IO设备控制操作    7.5.1 DeviceIoControl与驱动交互    7.5.2 缓冲内存模式IOCTL    7.5.3 直接内存模式IOCTL    7.5.4 其他内存模式IOCTL   7.6 小结 第2篇 进阶篇  第8章 驱动程序的同步处理  本章介绍了驱动程序中常用的同步处理办法,并且将内核模式下的同步处理方法和用户模式下的同步处理方法做了比较。另外,本章还介绍了中断请求级、自旋锁等同步处理机制。   8.1 基本概念    8.1.1 问题的引出    8.1.2 同步与异步   8.2 中断请求级    8.2.1 中断请求(IRQ)与可编程中断控制器(PIC)    8.2.2 高级可编程控制器(APIC)    8.2.3 中断请求级(IRQL)    8.2.4 线程调度与线程优先级    8.2.5 IRQL的变化    8.2.6 IRQL与内存分页    8.2.7 控制IRQL提升与降低   8.3 自旋锁    8.3.1 原理    8.3.2 使用方法   8.4 用户模式下的同步对象    8.4.1 用户模式的等待    8.4.2 用户模式开启多线程    8.4.3 用户模式的事件    8.4.4 用户模式的信号灯    8.4.5 用户模式的互斥体    8.4.6 等待线程完成   8.5 内核模式下的同步对象    8.5.1 内核模式下的等待    8.5.2 内核模式下开启多线程    8.5.3 内核模式下的事件对象    8.5.4 驱动程序与应用程序交互事件对象    8.5.5 驱动程序与驱动程序交互事件对象    8.5.6 内核模式下的信号灯    8.5.7 内核模式下的互斥体    8.5.8 快速互斥体   8.6 其他同步方法    8.6.1 使用自旋锁进行同步    8.6.2 使用互锁操作进行同步    8.7 小结  第9章 IRP的同步  本章详细地介绍了IRP的同步处理方法和异步处理方法。另外,本章还介绍了StartIO例程、中断服务例程、DPC服务例程。   9.1 应用程序对设备的同步异步操作    9.1.1 同步操作与异步操作原理    9.1.2 同步操作设备    9.1.3 异步操作设备(方式一)    9.1.4 异步操作设备(方式二)   9.2 IRP的同步完成与异步完成    9.2.1 IRP的同步完成    9.2.2 IRP的异步完成    9.2.3 取消IRP   9.3 StartIO例程    9.3.1 并行执行与串行执行    9.3.2 StartIO例程    9.3.3 示例   9.4 自定义的StartIO    9.4.1 多个串行化队列    9.4.2 示例   9.5 中断服务例程    9.5.1 中断操作的必要性    9.5.2 中断优先级    9.5.3 中断服务例程(ISR)   9.6 DPC例程    9.6.1 延迟过程调用例程(DPC)    9.6.2 DpcForISR   9.7 小结  第10章 定时器  本章总结了在内核模式下的四种等待方法,读者可以利用这些方法灵活地用在自己的驱动程序中。最后本章还介绍了如何对IRP的超时情况进行处理。   10.1 定时器实现方式一    10.1.1 I/O定时器    10.1.2 示例代码   10.2 定时器实现方式二    10.2.1 DPC定时器    10.2.2 示例代码   10.3 等待    10.3.1 第一种方法:使用KeWaitForSingleObject    10.3.2 第二种方法:使用KeDelayExecutionThread    10.3.3 第三种方法:使用KeStallExecutionProcessor    10.3.4 第四种方法:使用定时器   10.4 时间相关的其他内核函数    10.4.1 时间相关函数    10.4.2 示例代码   10.5 IRP的超时处理    10.5.1 原理    10.5.2 示例代码   10.6 小结  第11章 驱动程序调用驱动程序 本章主要介绍了如何在驱动程序中调用其他驱动程序。比较简单的方法是将被调用的驱动程序以文件的方式操作。比较高级的方法是构造各种IRP,并将这些IRP传送到被调用的驱动程序中。   11.1 以文件句柄形式调用其他驱动程序    11.1.1 准备一个标准驱动    11.1.2 获得设备句柄    11.1.3 同步调用    11.1.4 异步调用方法一    11.1.5 异步调用方法二    11.1.6 通过符号链接打开设备   11.2 通过设备指针调用其他驱动程序    11.2.1 用IoGetDeviceObjectPointer获得设备指针    11.2.2 创建IRP传递给驱动的派遣函数    11.2.3 用IoBuildSynchronousFsdRequest创建IRP    11.2.4 用IoBuildAsynchronousFsdRequest创建IRP    11.2.5 用IoAllocateIrp创建IRP   11.3 其他方法获得设备指针    11.3.1 用ObReferenceObjectByName获得设备指针    11.3.2 剖析IoGetDeviceObjectPointer    11.4 小结  第12章 分层驱动程序   本章主要介绍了分层驱动的概念。分层驱动可以将功能复杂的驱动程序分解为多个功能简单的驱动程序。多个分层的驱动程序形成一个设备堆栈,IRP请求首先发送到设备堆栈的顶层,然后依次穿越每层的设备堆栈,最终完成IRP请求。   12.1 分层驱动程序概念    12.1.1 分层驱动程序的概念    12.1.2 设备堆栈与挂载    12.1.3 I/O堆栈    12.1.4 向下转发IRP    12.1.5 挂载设备对象示例    12.1.6 转发IRP示例    12.1.7 分析    12.1.8 遍历设备栈   12.2 完成例程    12.2.1 完成例程概念    12.2.2 传播Pending位    12.2.3 完成例程返回STATUS_SUCCESS    12.2.4 完成例程返回STATUS_MORE_PROCESSING_REQUIRED   12.3 将IRP分解成多个IRP    12.3.1 原理    12.3.2 准备底层驱动    12.3.3 读派遣函数    12.3.4 完成例程    12.3.5 分析   12.4 WDM驱动程序架构    12.4.1 WDM与分层驱动程序    12.4.2 WDM的加载方式    12.4.3 功能设备对象    12.4.4 物理设备对象    12.4.5 物理设备对象与即插即用   12.5 小结  第13章 让设备实现即插即用  本章首先介绍即插即用的概念和驱动程序支持即插即用功能的必要性。另外,本章还介绍如何利用WDM驱动程序开发框架设计支持即插即用功能的驱动程序。   13.1 即插即用概念    13.1.1 历史原因    13.1.2 即插即用的目标    13.1.3 Windows中即插即用相关组件    13.1.4 遗留驱动程序   13.2 即插即用IRP    13.2.1 即插即用IRP的功能代码    13.2.2 处理即插即用IRP的派遣函数   13.3 通过设备接口寻找设备    13.3.1 设备接口    13.3.2 WDM驱动中设置接口    13.3.3 应用程序寻找接口    13.3.4 查看接口设备   13.4 启动和停止设备    13.4.1 为一个实际硬件安装HelloWDM    13.4.2 启动设备    13.4.3 转发并等待    13.4.4 获得设备相关资源    13.4.5 枚举设备资源    13.4.6 停止设备   13.5 即插即用的状态转换    13.5.1 状态转换图    13.5.2 IRP_MN_QUERY_STOP_DEVICE    13.5.3 IRP_MN_QUERY_REMOVE_DEVICE   13.6 其他即插即用IRP    13.6.1 IRP_MN_FILTER_RESOURCE_REQUIREMENTS    13.6.2 IRP_MN_QUERY_CAPABILITIES   13.7 小结  第14章 电源管理  本章主要介绍了如何在WDM驱动程序中进行电源处理。电源处理主要是处理好电源状态和设备状态。   14.1 WDM电源管理模型    14.1.1 概述    14.1.2 热插拔    14.1.3 电源状态    14.1.4 设备状态    14.1.5 状态转换   14.2 处理IRP_MJ_POWER   14.3 处理IRP_MN_QUERY_CAPABILITIES    14.3.1 DEVICE_CAPABILITIES    14.3.2 一个试验   14.4 小结 第3篇 实用篇  第15章 I/O端口操作  本章总结了多种I/O端口操作的方法。这些方法本质上是一样的,都是将端口输入输出的汇编指令运行在内核模式中。   15.1 概述    15.1.1 从DOS说起    15.1.2 汇编实现    15.1.3 DDK实现   15.2 工具软件WinIO    15.2.1 WinIO简介    15.2.2 使用方法   15.3 端口操作实现方法一    15.3.1 驱动端程序    15.3.2 应用程序端程序   15.4 端口操作实现方法二    15.4.1 驱动端程序    15.4.2 应用程序端程序   15.5 端口操作实现方法三    15.5.1 驱动端程序    15.5.2 应用程序端程序   15.6 端口操作实现方法四    15.6.1 原理    15.6.2 驱动端程序    15.6.3 应用程序端程序   15.7 驱动PC喇叭    15.7.1 可编程定时器    15.7.2 PC喇叭    15.7.3 操作代码   15.8 操作并口设备    15.8.1 并口设备简介    15.8.2 并口寄存器    15.8.3 并口设备操作   15.9 小结 第16章 PCI设备驱动 本章主要介绍PCI设备的驱动开发。首先介绍了PCI总线协议。作为驱动程序员,开发PCI驱动程序首先要了解PCI配置空间。根据读取PCI配置空间,可以得到PCI设备的所有资源。另外,本章还总结了四种获取PCI配置空间的方法。   16.1 PCI总线协议    16.1.1 PCI总线简介    16.1.2 PCI配置空间简介   16.2 访问PCI配置空间方法一    16.2.1 两个重要寄存器    16.2.2 示例   16.3 访问PCI配置空间方法二    16.3.1 DDK函数读取配置空间    16.3.2 示例   16.4 访问PCI配置空间方法三    16.4.1 通过即插即用IRP获得PCI配置空间    16.4.2 示例   16.5 访问PCI配置空间方法四    16.5.1 创建IRP_MN_READ_CONFIG    16.5.2 示例   16.6 PCI设备驱动开发示例    16.6.1 开发步骤    16.6.2 中断操作    16.6.3 操作设备物理内存    16.6.4 示例   16.7 小结 第17章 USB设备驱动  本章首先介绍了USB总线协议的基本框架,其中包括USB总线的拓扑结构,USB通信的流程,还有USB的四种传输模式。另外,本章介绍了如何编写USB总线设备的驱动程序。   17.1 USB总线协议    17.1.1 USB设备简介    17.1.2 USB连接拓扑结构    17.1.3 USB通信的流程    17.1.4 USB四种传输模式   17.2 Windows下的USB驱动    17.2.1 观察USB设备的工具    17.2.2 USB设备请求    17.2.3 设备描述符    17.2.4 配置描述符    17.2.5 接口描述符    17.2.6 端点描述符   17.3 USB驱动开发实例    17.3.1 功能驱动与物理总线驱动    17.3.2 构造USB请求包    17.3.3 发送USB请求包    17.3.4 USB设备初始化    17.3.5 USB设备的插拔    17.3.6 USB设备的读写   17.4 小结  第18章 SDIO设备驱动 本章首先介绍了SDIO协议,讲述了SD内存卡和SDIO卡的兼容问题。然后介绍了SDIO协议中的发送命令、回应命令、传送数据等相关协议。随后,本章又介绍了Windows中,DDK提供的对SDIO卡设备的支持。然后介绍了如何利用总线驱动,使SDIO设备初始化,接收中断,发送和接收数据等操作。   18.1 SDIO协议    18.1.1 SD内存卡概念    18.1.2 SDIO卡概念    18.1.3 SDIO总线    18.1.4 SDIO令牌    18.1.5 SDIO令牌格式    18.1.6 SDIO的寄存器    18.1.7 CMD52命令    18.1.8 CMD53命令   18.2 SDIO卡驱动开发框架    18.2.1 SDIO Host Controller驱动    18.2.2 SDIO卡的初始化    18.2.3 中断回调函数    18.2.4 获得和设置属性    18.2.5 CMD52    18.2.6 CMD53   18.3 SDIO开发实例   18.4 小结  第19章 虚拟串口设备驱动  本章介绍了串口开发的框架模型,在串口的AddDevice例程中需要暴露出一个串口的符号连接,另外在相应的注册表中需要进行设置。在串口与应用程序的通信中,主要是一组DDK定义的IO控制码,这些IO控制码负责由应用程序向驱动发出请求。   19.1 串口简介   19.2 DDK串口开发框架    19.2.1 串口驱动的入口函数    19.2.2 应用程序与串口驱动的通信    19.2.3 写的实现    19.2.4 读的实现   19.3 小结  第20章 摄像头设备驱动程序  本章主要介绍了微软提供的摄像头驱动框架。在该框架中,微软提供了类驱动和小驱动的概念。对于驱动程序员的任务就是编写小驱动程序。   20.1 WDM摄像头驱动框架    20.1.1 类驱动与小驱动    20.1.2 摄像头的类驱动与小驱动    20.1.3 编写小驱动程序    20.1.4 小驱动的流控制   20.2 虚拟摄像头开发实例    20.2.1 编译和安装    20.2.2 虚拟摄像头入口函数    20.2.3 对STREAM_REQUEST_BLOCK的处理函数    20.2.4 打开视频流    20.2.5 对视频流的读取   20.3 小结 第4篇 提高篇  第21章 再论IRP  本章将相关IRP的操作做了进一步的总结。首先是转发IRP,归纳了几种不同的方式。其次总结了创建IRP的几种不同方法。创建IRP总的来说分为创建同步IRP和创建异步IRP。对于创建同步IRP,操作比较简单,I/O管理器会负责回收IRP的相关内存,但是使用不够灵活。对于创建异步IRP,操作比较复杂,程序员需要自己负责对IRP及相关内存回收,但使用十分灵活。   21.1 转发IRP    21.1.1 直接转发    21.1.2 转发并且等待    21.1.3 转发并且设置完成例程    21.1.4 暂时挂起当前IRP    21.1.5 不转发IRP   21.2 创建IRP    21.2.1 IoBuildDeviceIoControlRequest    21.2.2 创建有超时的IOCTL IRP    21.2.3 用IoBuildSynchronousFsdRequest创建IRP    21.2.4 关于IoBuildAsynchronousFsdRequest    21.2.5 关于IoAllocateIrp   21.3 小结 第22章 过滤驱动程序  本章主要介绍WDM和NT式过滤驱动程序开发。过滤驱动程序开发十分灵活,可以修改已有驱动程序的功能,也可以对数据进行过滤加密。另外,利用过滤驱动程序还能编写出很多具有相当功能强大的程序来。  22.1 文件过滤驱动程序   22.1.1 过滤驱动程序概念   22.1.2 过滤驱动程序的入口函数   22.1.3 U盘过滤驱动程序   22.1.4 过滤驱动程序加载方法一   22.1.5 过滤驱动程序加载方法二   22.1.6 过滤驱动程序的AddDevice例程   22.1.7 磁盘命令过滤  22.2 NT式过滤驱动程序   22.2.1 NT式过滤驱动程序   22.2.2 NT过滤驱动的入口函数   22.2.3 挂载过滤驱动   22.2.4 过滤键盘读操作  22.3 小结  第23章 高级调试技巧  本章将介绍一些Windows开发驱动的高级调试技巧。有一些高级驱动程序调试技巧,可以帮助程序员找出驱动程序中的Bug。另外,利用一些第三方工具软件,也可以帮助程序员找到驱动程序中的漏洞,从而提高开发效率。  23.1 一般性调试技巧   23.1.1 打印调试信息   23.1.2 存储dump信息   23.1.3 使用WinDbg调试工具  23.2 高级内核调试技巧   23.2.1 安装VMWare   23.2.2 在虚拟机上加载驱动程序   23.2.3 VMWare和WinDbg联合调试驱动程序  23.3 用IRPTrace调试驱动程序  23.4 小结
第1章 内核上机指导 2 1.1 下载和使用WDK 2 1.1.1 下载并安装WDK 2 1.1.2 编写第一个C文件 4 1.1.3 编译一个工程 5 1.2 安装与运行 6 1.2.1 下载一个安装工具 6 1.2.2 运行与查看输出信息 7 1.2.3 在虚拟机中运行 8 1.3 调试内核模块 9 1.3.1 下载和安装WinDbg 9 1.3.2 设置Windows XP调试执行 9 1.3.3 设置Vista调试执行 10 1.3.4 设置VMware的管道虚拟串口 11 1.3.5 设置Windows内核符号表 12 1.3.6 实战调试first 13 第2章 内核编程环境及其特殊性 16 2.1 内核编程的环境 16 2.1.1 隔离的应用程序 16 2.1.2 共享的内核空间 17 2.1.3 无处不在的内核模块 18 2.2 数据类型 19 2.2.1 基本数据类型 19 2.2.2 返回状态 19 2.2.3 字符串 20 2.3 重要的数据结构 21 2.3.1 驱动对象 21 2.3.2 设备对象 22 2.3.3 请求 24 2.4 函数调用 25 2.4.1 查阅帮助 25 2.4.2 帮助中有的几类函数 26 2.4.3 帮助中没有的函数 28 2.5 Windows驱动开发模型 29 2.6 WDK编程中的特殊点 30 2.6.1 内核编程的主要调用源 30 2.6.2 函数的多线程安全性 30 2.6.3 代码的中断级 32 2.6.4 WDK中出现的特殊代码 32 第3章 字符串与链表 35 3.1 字符串操作 35 3.1.1 使用字符串结构 35 3.1.2 字符串的初始化 36 3.1.3 字符串的拷贝 37 3.1.4 字符串的连接 38 3.1.5 字符串的打印 38 3.2 内存与链表 40 3.2.1 内存的分配与释放 40 3.2.2 使用LIST_ENTRY 41 3.2.3 使用长长整型数据 43 3.3 自旋锁 44 3.3.1 使用自旋锁 44 3.3.2 在双向链表中使用自旋锁 45 3.3.3 使用队列自旋锁提高性能 46 第4章 文件、注册表、线程 47 4.1 文件操作 47 4.1.1 使用OBJECT_ATTRIBUTES 47 4.1.2 打开和关闭文件 48 4.1.3 文件读/写操作 51 4.2 注册表操作 53 4.2.1 注册表键的打开 53 4.2.2 注册表键值的读 55 4.2.3 注册表键值的写 57 4.3 时间与定时器 58 4.3.1 获得当前“滴答”数 58 4.3.2 获得当前系统时间 58 4.3.3 使用定时器 59 4.4 线程与事件 62 4.4.1 使用系统线程 62 4.4.2 在线程中睡眠 63 4.4.3 使用同步事件 64 第5章 应用与内核通信 67 5.1 内核方面的编程 68 5.1.1 生成控制设备 68 5.1.2 控制设备的名字和符号链接 70 5.1.3 控制设备的删除 71 5.1.4 分发函数 72 5.1.5 请求的处理 73 5.2 应用方面的编程 74 5.2.1 基本的功能需求 74 5.2.2 在应用程序中打开与关闭设备 75 5.2.3 设备控制请求 75 5.2.4 内核中的对应处理 77 5.2.5 结合测试的效果 79 5.3 阻塞、等待与安全设计 80 5.3.1 驱动主动通知应用 80 5.3.2 通信接口的测试 81 5.3.3 内核中的缓冲区链表结构 83 5.3.4 输入:内核中的请求处理中的安全检查 84 5.3.5 输出处理与卸载清理 85 第6章 64位和32位内核开发差异 88 6.1 64位系统新增机制 88 6.1.1 WOW64子系统 88 6.1.2 PatchGuard技术 91 6.1.3 64位驱动的编译、安装与运行 91 6.2 编程差异 92 6.2.1 汇编嵌入变化 92 6.2.2 预处理与条件编译 93 6.2.3 数据结构调整 93 开 发 篇 第7章 串口的过滤 96 7.1 过滤的概念 96 7.1.1 设备绑定的内核API之一 97 7.1.2 设备绑定的内核API之二 98 7.1.3 生成过滤设备并绑定 98 7.1.4 从名字获得设备对象 100 7.1.5 绑定所有串口 101 7.2 获得实际数据 102 7.2.1 请求的区分 102 7.2.2 请求的结局 103 7.2.3 写请求的数据 104 7.3 完整的代码 105 7.3.1 完整的分发函数 105 7.3.2 如何动态卸载 106 7.3.3 代码的编译与运行 107 第8章 键盘的过滤 109 8.1 技术原理 110 8.1.1 预备知识 110 8.1.2 Windows中从击键到内核 110 8.1.3 键盘硬件原理 112 8.2 键盘过滤的框架 112 8.2.1 找到所有的键盘设备 112 8.2.2 应用设备扩展 115 8.2.3 键盘过滤模块的DriverEntry 117 8.2.4 键盘过滤模块的动态卸载 117 8.3 键盘过滤的请求处理 119 8.3.1 通常的处理 119 8.3.2 PNP的处理 120 8.3.3 读的处理 121 8.3.4 读完成的处理 122 8.4 从请求中打印出按键信息 123 8.4.1 从缓冲区中获得KEYBOARD_INPUT_DATA 123 8.4.2 从KEYBOARD_INPUT_DATA中得到键 124 8.4.3 从MakeCode到实际字符 124 8.5 Hook分发函数 126 8.5.1 获得类驱动对象 126 8.5.2 修改类驱动的分发函数指针 127 8.5.3 类驱动之下的端口驱动 128 8.5.4 端口驱动和类驱动之间的协作机制 129 8.5.5 找到关键的回调函数的条件 129 8.5.6 定义常数和数据结构 130 8.5.7 打开两种键盘端口驱动寻找设备 131 8.5.8 搜索在KbdClass类驱动中的地址 133 8.6 Hook键盘中断反过滤 135 8.6.1 中断:IRQ和INT 136 8.6.2 如何修改IDT 136 8.6.3 替换IDT中的跳转地址 137 8.6.4 QQ的PS/2反过滤措施 139 8.7 直接用端口操作键盘 139 8.7.1 读取键盘数据和命令端口 139 8.7.2 p2cUserFilter的最终实现 140 第9章 磁盘的虚拟 143 9.1 虚拟的磁盘 143 9.2 一个具体的例子 143 9.3 入口函数 144 9.3.1 入口函数的定义 144 9.3.2 Ramdisk驱动的入口函数 145 9.4 EvtDriverDeviceAdd函数 146 9.4.1 EvtDriverDeviceAdd的定义 146 9.4.2 局部变量的声明 146 9.4.3 磁盘设备的创建 147 9.4.4 如何处理发往设备的请求 148 9.4.5 用户配置的初始化 149 9.4.6 链接给应用程序 151 9.4.7 小结 152 9.5 FAT12/16磁盘卷初始化 152 9.5.1 磁盘卷结构简介 152 9.5.2 Ramdisk对磁盘的初始化 154 9.6 驱动中的请求处理 160 9.6.1 请求的处理 160 9.6.2 读/写请求 160 9.6.3 DeviceIoControl请求 162 9.7 Ramdisk的编译和安装 164 9.7.1 编译 164 9.7.2 安装 164 9.7.3 对安装的深入探究 165 第10章 磁盘的过滤 167 10.1 磁盘过滤驱动的概念 167 10.1.1 设备过滤和类过滤 167 10.1.2 磁盘设备和磁盘卷设备过滤驱动 167 10.1.3 注册表和磁盘卷设备过滤驱动 168 10.2 具有还原功能的磁盘卷过滤驱动 168 10.2.1 简介 168 10.2.2 基本思想 169 10.3 驱动分析 169 10.3.1 DriverEntry函数 169 10.3.2 AddDevice函数 170 10.3.3 PnP请求的处理 174 10.3.4 Power请求的处理 178 10.3.5 DeviceIoControl请求的处理 178 10.3.6 bitmap的作用和分析 182 10.3.7 boot驱动完成回调函数和稀疏文件 187 10.3.8 读/写请求的处理 190 第11章 文件系统的过滤与监控 199 11.1 文件系统的设备对象 200 11.1.1 控制设备与卷设备 200 11.1.2 生成自己的一个控制设备 201 11.2 文件系统的分发函数 202 11.2.1 普通的分发函数 202 11.2.2 文件过滤的快速IO分发函数 203 11.2.3 快速IO分发函数的一个实现 205 11.2.4 快速IO分发函数逐个简介 206 11.3 设备的绑定前期工作 207 11.3.1 动态地选择绑定函数 207 11.3.2 注册文件系统变动回调 208 11.3.3 文件系统变动回调的一个实现 209 11.3.4 文件系统识别器 211 11.4 文件系统控制设备的绑定 212 11.4.1 生成文件系统控制设备的过滤设备 212 11.4.2 绑定文件系统控制设备 213 11.4.3 利用文件系统控制请求 215 11.5 文件系统卷设备的绑定 217 11.5.1 从IRP中获得VPB指针 217 11.5.2 设置完成函数并等待IRP完成 218 11.5.3 卷挂载IRP完成后的工作 221 11.5.4 完成函数的相应实现 223 11.5.5 绑定卷的实现 224 11.6 读/写操作的过滤 226 11.6.1 设置一个读处理函数 226 11.6.2 设备对象的区分处理 227 11.6.3 解析读请求中的文件信息 228 11.6.4 读请求的完成 230 11.7 其他操作的过滤 234 11.7.1 文件对象的生存周期 234 11.7.2 文件的打开与关闭 235 11.7.3 文件的删除 237 11.8 路径过滤的实现 238 11.8.1 取得文件路径的三种情况 238 11.8.2 打开成功后获取路径 238 11.8.3 在其他时刻获得文件路径 240 11.8.4 在打开请求完成之前获得路径名 240 11.8.5 把短名转换为长名 242 11.9 把sfilter编译成静态库 243 11.9.1 如何方便地使用sfilter 243 11.9.2 初始化回调、卸载回调和绑定回调 244 11.9.3 绑定与回调 245 11.9.4 插入请求回调 246 11.9.5 如何利用sfilter.lib 249 第12章 文件系统透明加密 252 12.1 文件透明加密的应用 252 12.1.1 防止企业信息泄密 252 12.1.2 文件透明加密防止企业信息泄密 253 12.1.3 文件透明加密软件的例子 253 12.2 区分进程 254 12.2.1 机密进程与普通进程 254 12.2.2 找到进程名字的位置 255 12.2.3 得到当前进程的名字 256 12.3 内存映射与文件缓冲 257 12.3.1 记事本的内存映射文件 257 12.3.2 Windows的文件缓冲 258 12.3.3 文件缓冲:明文还是密文的选择 259 12.3.4 清除文件缓冲 260 12.4 加密标识 263 12.4.1 保存在文件外、文件头还是文件尾 263 12.4.2 隐藏文件头的大小 264 12.4.3 隐藏文件头的设置偏移 266 12.4.4 隐藏文件头的读/写偏移 267 12.5 文件加密表 267 12.5.1 何时进行加密操作 267 12.5.2 文件控制块与文件对象 268 12.5.3 文件加密表的数据结构与初始化 269 12.5.4 文件加密表的操作:查询 270 12.5.5 文件加密表的操作:添加 271 12.5.6 文件加密表的操作:删除 272 12.6 文件打开处理 273 12.6.1 直接发送IRP进行查询与设置操作 274 12.6.2 直接发送IRP进行读/写操作 276 12.6.3 文件的非重入打开 277 12.6.4 文件的打开预处理 280 12.7 读/写加密和解密 285 12.7.1 在读取时进行解密 285 12.7.2 分配与释放MDL 286 12.7.3 写请求加密 287 12.8 crypt_file的组装 289 12.8.1 crypt_file的初始化 289 12.8.2 crypt_file的IRP预处理 290 12.8.3 crypt_file的IRP后处理 293 第13章 文件系统微过滤驱动 297 13.1 文件系统微过滤驱动简介 297 13.1.1 文件系统微过滤驱动的由来 297 13.1.2 Minifilter的优点与不足 298 13.2 Minifilter的编程框架 298 13.2.1 微文件系统过滤的注册 299 13.2.2 微过滤器的数据结构 300 13.2.3 卸载回调函数 303 13.2.4 预操作回调函数 303 13.2.5 后操作回调函数 306 13.2.6 其他回调函数 307 13.3 Minifilter如何与应用程序通信 309 13.3.1 建立通信端口的方法 310 13.3.2 在用户态通过DLL使用通信端口的范例 311 13.4 Minifilter的安装与加载 314 13.4.1 安装Minifilter的INF文件 314 13.4.2 启动安装完成的Minifilter 316 第14章 网络传输层过滤 317 14.1 TDI概要 317 14.1.1 为何选择TDI 317 14.1.2 从socket到Windows内核 318 14.1.3 TDI过滤的代码例子 319 14.2 TDI的过滤框架 319 14.2.1 绑定TDI的设备 319 14.2.2 唯一的分发函数 320 14.2.3 过滤框架的实现 322 14.2.4 主要过滤的请求类型 323 14.3 生成请求:获取地址 324 14.3.1 过滤生成请求 324 14.3.2 准备解析IP地址与端口 326 14.3.3 获取生成的IP地址和端口 327 14.3.4 连接终端的生成与相关信息的保存 329 14.4 控制请求 330 14.4.1 TDI_ASSOCIATE_ADDRESS的过滤 330 14.4.2 TDI_CONNECT的过滤 332 14.4.3 其他的次功能号 333 14.4.4 设置事件的过滤 334 14.4.5 TDI_EVENT_CONNECT类型的设置事件的过滤 336 14.4.6 直接获取发送函数的过滤 337 14.4.7 清理请求的过滤 339 14.5 本书例子tdifw.lib的应用 341 14.5.1 tdifw库的回调接口 341 14.5.2 tdifw库的使用例子 342 第15章 Windows过滤平台 345 15.1 WFP简介 345 15.2 WFP框架 345 15.3 基本对象模型 347 15.3.1 过滤引擎 347 15.3.2 垫片 347 15.3.3 呼出接口 347 15.3.4 分层 348 15.3.5 子层 349 15.3.6 过滤器 350 15.3.7 呼出接口回调函数 354 15.4 WFP操作 359 15.4.1 呼出接口的注册与卸载 360 15.4.2 呼出接口的添加与移除 360 15.4.3 子层的添加与移除 361 15.4.4 过滤器的添加 362 15.5 WFP过滤例子 362 第16章 NDIS协议驱动 370 16.1 以太网包和网络驱动架构 370 16.1.1 以太网包和协议驱动 370 16.1.2 NDIS网络驱动 371 16.2 协议驱动的DriverEntry 372 16.2.1 生成控制设备 372 16.2.2 注册协议 374 16.3 协议与网卡的绑定 375 16.3.1 协议与网卡的绑定概念 375 16.3.2 绑定回调处理的实现 376 16.3.3 协议绑定网卡的API 378 16.3.4 解决绑定竞争问题 379 16.3.5 分配接收和发送的包池与缓冲池 380 16.3.6 OID请求的发送和请求完成回调 381 16.3.7 ndisprotCreateBinding的最终实现 385 16.4 绑定的解除 390 16.4.1 解除绑定使用的API 390 16.4.2 ndisprotShutdownBinding的实现 392 16.5 在用户态操作协议驱动 395 16.5.1 协议的收包与发包 395 16.5.2 在用户态编程打开设备 396 16.5.3 用DeviceIoControl发送控制请求 397 16.5.4 用WriteFile发送数据包 399 16.5.5 用ReadFile发送数据包 400 16.6 在内核态完成功能的实现 402 16.6.1 请求的分发与实现 402 16.6.2 等待设备绑定完成与指定设备名 402 16.6.3 指派设备的完成 403 16.6.4 处理读请求 406 16.6.5 处理写请求 408 16.7 协议驱动的接收回调 412 16.7.1 和接收包有关的回调函数 412 16.7.2 ReceiveHandler的实现 413 16.7.3 TransferDataCompleteHandler的实现 417 16.7.4 ReceivePacketHandler的实现 418 16.7.5 接收数据包的入队 420 16.7.6 接收数据包的出队和读请求的完成 422 第17章 NDIS小端口驱动 427 17.1 小端口驱动的应用与概述 427 17.1.1 小端口驱动的应用 427 17.1.2 小端口驱动示例 428 17.1.3 小端口驱动的运作与编程概述 429 17.2 小端口驱动的初始化 429 17.2.1 小端口驱动的DriverEntry 429 17.2.2 小端口驱动的适配器结构 431 17.2.3 配置信息的读取 433 17.2.4 设置小端口适配器上下文 433 17.2.5 MPInitialize的实现 434 17.2.6 MPHalt的实现 437 17.3 打开ndisprot设备 438 17.3.1 IO目标 438 17.3.2 给IO目标发送DeviceIoControl请求 439 17.3.3 打开ndisprot接口并完成配置设备 441 17.4 使用ndisprot发送包 443 17.4.1 小端口驱动的发包接口 443 17.4.2 发送控制块(TCB) 444 17.4.3 遍历包组并填写TCB 446 17.4.4 写请求的构建与发送 449 17.5 使用ndisprot接收包 451 17.5.1 提交数据包的内核API 451 17.5.2 从接收控制块(RCB)提交包 452 17.5.3 对ndisprot读请求的完成函数 454 17.5.4 读请求的发送 456 17.5.5 用于读包的WDF工作任务 457 17.5.6 ndisedge读工作任务的生成与入列 459 17.6 其他的特征回调函数的实现 461 17.6.1 包的归还 461 17.6.2 OID查询处理的直接完成 462 17.6.3 OID设置处理 465 第18章 NDIS中间层驱动 467 18.1 NDIS中间层驱动概述 467 18.1.1 Windows网络架构总结 467 18.1.2 NDIS中间层驱动简介 468 18.1.3 NDIS中间层驱动的应用 469 18.1.4 NDIS包描述符结构深究 470 18.2 中间层驱动的入口与绑定 473 18.2.1 中间层驱动的入口函数 473 18.2.2 动态绑定NIC设备 474 18.2.3 小端口初始化(MpInitialize) 475 18.3 中间层驱动发送数据包 477 18.3.1 发送数据包原理 477 18.3.2 包描述符“重利用” 478 18.3.3 包描述符“重申请” 481 18.3.4 发送数据包的异步完成 482 18.4 中间层驱动接收数据包 484 18.4.1 接收数据包概述 484 18.4.2 用PtReceive接收数据包 485 18.4.3 用PtReceivePacket接收 490 18.4.4 对包进行过滤 491 18.5 中间层驱动程序查询和设置 494 18.5.1 查询请求的处理 494 18.5.2 设置请求的处理 496 18.6 NDIS句柄 498 18.6.1 不可见的结构指针 498 18.6.2 常见的NDIS句柄 499 18.6.3 NDIS句柄误用问题 500 18.6.4 一种解决方案 502 18.7 生成普通控制设备 503 18.7.1 在中间层驱动中添加普通设备 503 18.7.2 使用传统方法来生成控制设备 505 第19章 IA-32汇编基础 511 19.1 x86内存、寄存器与堆栈 511 19.1.1 _asm关键字 511 19.1.2 x86中的mov指令 512 19.1.3 x86中的寄存器与内存 512 19.1.4 赋值语句的实现 513 19.2 x86中函数的实现 514 19.2.1 一个函数的例子 514 19.2.2 堆栈的介绍 515 19.2.3 寄存器的备份和恢复 516 19.2.4 内部变量与返回值 518 19.3 x86中函数的调用与返回 521 19.3.1 函数的调用指令call 521 19.3.2 通过堆栈传递参数 521 19.3.3 从函数返回 523 19.3.4 三种常见的调用协议 524 19.4 从32位汇编到64位汇编 526 19.4.1 Intel 64与IA-32体系架构简介 526 19.4.2 64位指令与32位指令 526 19.4.3 通用寄存器 527 19.5 64位下的函数实现 528 19.5.1 函数概览 528 19.5.2 32位参数的传递 529 19.5.3 64位参数与返回值 530 19.5.4 栈空间的开辟与恢复 531 第20章 IA-32体系中的内存地址 534 20.1 内存的虚拟地址 534 20.1.1 C语言中的内存地址 534 20.1.2 虚拟地址的构成 535 20.1.3 段的选择 536 20.2 全局描述符表和段描述符 538 20.2.1 全局描述符表 538 20.2.2 段类型 539 20.2.3 段寄存器与段选择子 540 20.2.4 64位模式下的段 541 20.3 分段编程实践 542 20.3.1 系统表寄存器的结构 542 20.3.2 在汇编语言中获取全局描述表的位置 543 20.3.3 调试范例:sgdt指令的错误使用 545 20.3.4 在64位下获得全局描述符表 547 20.4 线性地址基础 549 20.4.1 分页控制机制 550 20.4.2 线性地址的转换 551 20.4.3 混合页面大小 552 20.4.4 32位物理地址的页目录和页表项 552 20.5 各种特殊分页方式 555 20.5.1 PAE分页方式 555 20.5.2 PSE-36分页机制 558 20.5.3 IA-32e模式下的线性地址 559 20.6 分页编程实践 562 20.6.1 页目录和页目录指针表的获取 562 20.6.2 页表的获取 564 20.6.3 线性地址的结构 567 第21章 处理器权限级别切换 571 21.1 Ring0和Ring3权限级别 571 21.2 保护模式下的分页内存保护 572 21.3 分页内存不可执行保护 574 21.3.1 不可执行保护原理 574 21.3.2 不可执行保护的漏洞 575 21.3.3 上机实践 577 21.4 权限级别的切换 579 21.4.1 调用门及其漏洞 579 21.4.2 sysenter和sysexit指令 581 21.4.3 上机实践 583 第22章 IA-32体系结构中的中断 585 22.1 中断基础知识 585 22.1.1 中断描述符表 585 22.1.2 中断处理过程 587 22.1.3 64位模式下的中断处理机制 589 22.1.4 多核下的中断 589 22.2 Windows中断机制 593 22.3 中断编程实践 596 22.3.1 IDT Hook 596 22.3.2 巧用IDT Hook实现安全防护 598 第23章 Windows内核挂钩 601 23.1 系统服务描述符表挂钩 602 23.1.1 系统服务描述符表(SSDT) 602 23.1.2 系统服务描述符表挂钩的意图 603 23.1.3 寻找要挂钩的函数的地址 604 23.1.4 函数被挂钩的过程 605 23.1.5 具体实现的代码 606 23.2 函数导出表挂钩 608 23.2.1 内核函数的种类 608 23.2.2 挂钩IoCallDriver 610 23.2.3 对跳转地址进行修改 611 23.3 Windows 7系统下IofCallDriver的跟踪 612 23.4 Windows 7系统下内联挂钩 615 23.4.1 写入跳转指令并拷贝代码 615 23.4.2 实现中继函数 617 高 级 篇 第24章 Windows通知与回调 620 24.1 Windows的事件通知与回调 620 24.2 常用的事件通知 620 24.2.1 创建进程通知 621 24.2.2 创建线程通知 625 24.2.3 加载模块通知 626 24.2.4 注册表操作通知 629 24.3 Windows回调机制 636 24.3.1 回调对象 636 24.3.2 回调对象的创建 637 24.3.3 回调对象的注册 637 24.3.4 回调的通告 638 24.4 安全的死角,回调的应用 639 第25章 保护进程 640 25.1 内核对象简介 640 25.2 内核对象的结构 641 25.3 保护内核对象 642 25.3.1 处理对象的打开 643 25.3.2 处理句柄的复制 644 25.3.3 处理句柄的继承 646 25.4 进程的保护 652 25.4.1 保护原理 652 25.4.2 Vista以后的进程对象保护 654 25.4.3 进程的其他保护 655 附录A 如何使用本书的源码光盘 656 附录B 练习题 659
这是书的光盘。共分为两个部分,这是第一部分。 本书由浅入深、循序渐进地介绍了Windows驱动程序的开发方法与调试技巧。本书共分23章,内容涵盖了 Windows操作系统的基本原理、NT驱动程序与WDM驱动程序的构造、驱动程序中的同步异步处理方法、驱 动程序中即插即用功能、驱动程序的各种调试技巧等。同时,还针对流行的PCI驱动程序、USB驱动程序 、虚拟串口驱动程序、摄像头驱动程序、SDIO驱动程序进行了详细的介绍,本书最大的特色在于每一节 的例子都是经过精挑细选的,具有很强的针对性。力求让读者通过亲自动手实验,掌握各类Windows驱动 程序的开发技巧,学习尽可能多的Windows底层知识。   本书适用于中、高级系统程序员,同时也可用做高校计算机专业操作系统实验课的补充教材。 原创经典,威盛一线工程师倾力打造。深入驱动核心,剖析操作系统底层运行机制,通过实例引导,快 速学习编译、安装、调试的方法。   从Windows最基本的两类驱动程序的编译、安装、调试入手讲解,非常容易上手,用实例详细讲解 PCI、USB、虚拟串口、虚拟摄像头、SDIO等驱动程序的开发,归纳了多种调试驱动程序的高级技巧,如 用WinDBG和VMWARE软件对驱动进行源码级调试,深入Windows操作系统的底层和内核,透析Windows驱动 开发的本质。 本书是作者结合教学和科研实践经验编写而成的,不仅详细介绍了Windows内核原理,而且介绍了编程技 巧和应用实例,兼顾了在校研究生和工程技术人员的实际需求,对教学、生产和科研有现实的指导意义 ,是一本值得推荐的专著。              ——中国工程院院士   院士推荐   目前,电子系统设计广泛采用通用操作系统,达到降低系统的设计难度和缩短研发周期。实现操作 系统与硬件快速信息交换是电子系统设计的关键。   通用操作系统硬件驱动程序的开发,编写者不仅需要精通硬件设备、计算机总线,而且需要Windows 操作系统知识以及调试技巧。学习和掌握Windows硬件驱动程序的开发是电子系统设计人员必备的能力。   本书是作者结合教学和科研实践经验编写而成的,不仅详细介绍了Windows内核原理,并且介绍了编 程技巧和应用实例,兼顾了在校研究生和工程技术人员的实际需求,对教学、生产和科研有现实的指导 意义,是一本值得推荐的专著。 第1篇 入门篇 第1章 从两个最简单的驱动谈起 本章向读者呈现两个最简单的Windows驱动程序,一个是NT式的驱动程序,另一个是WDM式的驱动程序。 这两个驱动程序没有操作具体的硬件设备,只是在系统里创建了虚拟设备。在随后的章节中,它们会作 为基本驱动程序框架,被本书其他章节的驱动程序开发所复用。笔者将带领读者编写代码、编译、安装 和调试程序。   1.1 DDK的安装   1.2 第一个驱动程序HelloDDK的代码分析    1.2.1 HelloDDK的头文件    1.2.2 HelloDDK的入口函数    1.2.3 创建设备例程    1.2.4 卸载驱动例程    1.2.5 默认派遣例程   1.3 HelloDDK的编译和安装    1.3.1 用DDK环境编译HelloDDK    1.3.2 用VC集成开发环境编译HelloDDK    1.3.3 HelloDDK的安装   1.4 第二个驱动程序HelloWDM的代码分析    1.4.1 HelloWDM的头文件    1.4.2 HelloWDM的入口函数    1.4.3 HelloWDM的AddDevice例程    1.4.4 HelloWDM处理PNP的回调函数    1.4.5 HelloWDM对PNP的默认处理    1.4.6 HelloWDM对IRP_MN_REMOVE_DEVICE的处理    1.4.7 HelloWDM对其他IRP的回调函数    1.4.8 HelloWDM的卸载例程   1.5 HelloWDM的编译和安装    1.5.1 用DDK编译环境编译HelloWDM    1.5.2 HelloWDM的编译过程    1.5.3 安装HelloWDM   1.6 小结  第2章 Windows操作驱动的基本概念  驱动程序被操作系统加载在内核模式下,它与Windows操作系统内核的其他组件进行密切交互。本章主 要介绍Windows操作系统内核的基本概念,同时还介绍应用程序和驱动程序之间的通信方法。   2.1 Windows操作系统概述    2.1.1 Windows家族    2.1.2 Windows特性    2.1.3 用户模式和内核模式    
第1章 内核上机指导 2 1.1 下载和使用WDK 2 1.1.1 下载并安装WDK 2 1.1.2 编写第一个C文件 4 1.1.3 编译一个工程 5 1.2 安装与运行 6 1.2.1 下载一个安装工具 6 1.2.2 运行与查看输出信息 7 1.2.3 在虚拟机中运行 8 1.3 调试内核模块 9 1.3.1 下载和安装WinDbg 9 1.3.2 设置Windows XP调试执行 9 1.3.3 设置Vista调试执行 10 1.3.4 设置VMware的管道虚拟串口 11 1.3.5 设置Windows内核符号表 12 1.3.6 实战调试first 13 第2章 内核编程环境及其特殊性 16 2.1 内核编程的环境 16 2.1.1 隔离的应用程序 16 2.1.2 共享的内核空间 17 2.1.3 无处不在的内核模块 18 2.2 数据类型 19 2.2.1 基本数据类型 19 2.2.2 返回状态 19 2.2.3 字符串 20 2.3 重要的数据结构 21 2.3.1 驱动对象 21 2.3.2 设备对象 22 2.3.3 请求 24 2.4 函数调用 25 2.4.1 查阅帮助 25 2.4.2 帮助中有的几类函数 26 2.4.3 帮助中没有的函数 28 2.5 Windows驱动开发模型 29 2.6 WDK编程中的特殊点 30 2.6.1 内核编程的主要调用源 30 2.6.2 函数的多线程安全性 30 2.6.3 代码的中断级 32 2.6.4 WDK中出现的特殊代码 32 第3章 字符串与链表 35 3.1 字符串操作 35 3.1.1 使用字符串结构 35 3.1.2 字符串的初始化 36 3.1.3 字符串的拷贝 37 3.1.4 字符串的连接 38 3.1.5 字符串的打印 38 3.2 内存与链表 40 3.2.1 内存的分配与释放 40 3.2.2 使用LIST_ENTRY 41 3.2.3 使用长长整型数据 43 3.3 自旋锁 44 3.3.1 使用自旋锁 44 3.3.2 在双向链表中使用自旋锁 45 3.3.3 使用队列自旋锁提高性能 46 第4章 文件、注册表、线程 47 4.1 文件操作 47 4.1.1 使用OBJECT_ATTRIBUTES 47 4.1.2 打开和关闭文件 48 4.1.3 文件读/写操作 51 4.2 注册表操作 53 4.2.1 注册表键的打开 53 4.2.2 注册表键值的读 55 4.2.3 注册表键值的写 57 4.3 时间与定时器 58 4.3.1 获得当前“滴答”数 58 4.3.2 获得当前系统时间 58 4.3.3 使用定时器 59 4.4 线程与事件 62 4.4.1 使用系统线程 62 4.4.2 在线程中睡眠 63 4.4.3 使用同步事件 64 第5章 应用与内核通信 67 5.1 内核方面的编程 68 5.1.1 生成控制设备 68 5.1.2 控制设备的名字和符号链接 70 5.1.3 控制设备的删除 71 5.1.4 分发函数 72 5.1.5 请求的处理 73 5.2 应用方面的编程 74 5.2.1 基本的功能需求 74 5.2.2 在应用程序中打开与关闭设备 75 5.2.3 设备控制请求 75 5.2.4 内核中的对应处理 77 5.2.5 结合测试的效果 79 5.3 阻塞、等待与安全设计 80 5.3.1 驱动主动通知应用 80 5.3.2 通信接口的测试 81 5.3.3 内核中的缓冲区链表结构 83 5.3.4 输入:内核中的请求处理中的安全检查 84 5.3.5 输出处理与卸载清理 85 第6章 64位和32位内核开发差异 88 6.1 64位系统新增机制 88 6.1.1 WOW64子系统 88 6.1.2 PatchGuard技术 91 6.1.3 64位驱动的编译、安装与运行 91 6.2 编程差异 92 6.2.1 汇编嵌入变化 92 6.2.2 预处理与条件编译 93 6.2.3 数据结构调整 93 开 发 篇 第7章 串口的过滤 96 7.1 过滤的概念 96 7.1.1 设备绑定的内核API之一 97 7.1.2 设备绑定的内核API之二 98 7.1.3 生成过滤设备并绑定 98 7.1.4 从名字获得设备对象 100 7.1.5 绑定所有串口 101 7.2 获得实际数据 102 7.2.1 请求的区分 102 7.2.2 请求的结局 103 7.2.3 写请求的数据 104 7.3 完整的代码 105 7.3.1 完整的分发函数 105 7.3.2 如何动态卸载 106 7.3.3 代码的编译与运行 107 第8章 键盘的过滤 109 8.1 技术原理 110 8.1.1 预备知识 110 8.1.2 Windows中从击键到内核 110 8.1.3 键盘硬件原理 112 8.2 键盘过滤的框架 112 8.2.1 找到所有的键盘设备 112 8.2.2 应用设备扩展 115 8.2.3 键盘过滤模块的DriverEntry 117 8.2.4 键盘过滤模块的动态卸载 117 8.3 键盘过滤的请求处理 119 8.3.1 通常的处理 119 8.3.2 PNP的处理 120 8.3.3 读的处理 121 8.3.4 读完成的处理 122 8.4 从请求中打印出按键信息 123 8.4.1 从缓冲区中获得KEYBOARD_INPUT_DATA 123 8.4.2 从KEYBOARD_INPUT_DATA中得到键 124 8.4.3 从MakeCode到实际字符 124 8.5 Hook分发函数 126 8.5.1 获得类驱动对象 126 8.5.2 修改类驱动的分发函数指针 127 8.5.3 类驱动之下的端口驱动 128 8.5.4 端口驱动和类驱动之间的协作机制 129 8.5.5 找到关键的回调函数的条件 129 8.5.6 定义常数和数据结构 130 8.5.7 打开两种键盘端口驱动寻找设备 131 8.5.8 搜索在KbdClass类驱动中的地址 133 8.6 Hook键盘中断反过滤 135 8.6.1 中断:IRQ和INT 136 8.6.2 如何修改IDT 136 8.6.3 替换IDT中的跳转地址 137 8.6.4 QQ的PS/2反过滤措施 139 8.7 直接用端口操作键盘 139 8.7.1 读取键盘数据和命令端口 139 8.7.2 p2cUserFilter的最终实现 140 第9章 磁盘的虚拟 143 9.1 虚拟的磁盘 143 9.2 一个具体的例子 143 9.3 入口函数 144 9.3.1 入口函数的定义 144 9.3.2 Ramdisk驱动的入口函数 145 9.4 EvtDriverDeviceAdd函数 146 9.4.1 EvtDriverDeviceAdd的定义 146 9.4.2 局部变量的声明 146 9.4.3 磁盘设备的创建 147 9.4.4 如何处理发往设备的请求 148 9.4.5 用户配置的初始化 149 9.4.6 链接给应用程序 151 9.4.7 小结 152 9.5 FAT12/16磁盘卷初始化 152 9.5.1 磁盘卷结构简介 152 9.5.2 Ramdisk对磁盘的初始化 154 9.6 驱动中的请求处理 160 9.6.1 请求的处理 160 9.6.2 读/写请求 160 9.6.3 DeviceIoControl请求 162 9.7 Ramdisk的编译和安装 164 9.7.1 编译 164 9.7.2 安装 164 9.7.3 对安装的深入探究 165 第10章 磁盘的过滤 167 10.1 磁盘过滤驱动的概念 167 10.1.1 设备过滤和类过滤 167 10.1.2 磁盘设备和磁盘卷设备过滤驱动 167 10.1.3 注册表和磁盘卷设备过滤驱动 168 10.2 具有还原功能的磁盘卷过滤驱动 168 10.2.1 简介 168 10.2.2 基本思想 169 10.3 驱动分析 169 10.3.1 DriverEntry函数 169 10.3.2 AddDevice函数 170 10.3.3 PnP请求的处理 174 10.3.4 Power请求的处理 178 10.3.5 DeviceIoControl请求的处理 178 10.3.6 bitmap的作用和分析 182 10.3.7 boot驱动完成回调函数和稀疏文件 187 10.3.8 读/写请求的处理 190 第11章 文件系统的过滤与监控 199 11.1 文件系统的设备对象 200 11.1.1 控制设备与卷设备 200 11.1.2 生成自己的一个控制设备 201 11.2 文件系统的分发函数 202 11.2.1 普通的分发函数 202 11.2.2 文件过滤的快速IO分发函数 203 11.2.3 快速IO分发函数的一个实现 205 11.2.4 快速IO分发函数逐个简介 206 11.3 设备的绑定前期工作 207 11.3.1 动态地选择绑定函数 207 11.3.2 注册文件系统变动回调 208 11.3.3 文件系统变动回调的一个实现 209 11.3.4 文件系统识别器 211 11.4 文件系统控制设备的绑定 212 11.4.1 生成文件系统控制设备的过滤设备 212 11.4.2 绑定文件系统控制设备 213 11.4.3 利用文件系统控制请求 215 11.5 文件系统卷设备的绑定 217 11.5.1 从IRP中获得VPB指针 217 11.5.2 设置完成函数并等待IRP完成 218 11.5.3 卷挂载IRP完成后的工作 221 11.5.4 完成函数的相应实现 223 11.5.5 绑定卷的实现 224 11.6 读/写操作的过滤 226 11.6.1 设置一个读处理函数 226 11.6.2 设备对象的区分处理 227 11.6.3 解析读请求中的文件信息 228 11.6.4 读请求的完成 230 11.7 其他操作的过滤 234 11.7.1 文件对象的生存周期 234 11.7.2 文件的打开与关闭 235 11.7.3 文件的删除 237 11.8 路径过滤的实现 238 11.8.1 取得文件路径的三种情况 238 11.8.2 打开成功后获取路径 238 11.8.3 在其他时刻获得文件路径 240 11.8.4 在打开请求完成之前获得路径名 240 11.8.5 把短名转换为长名 242 11.9 把sfilter编译成静态库 243 11.9.1 如何方便地使用sfilter 243 11.9.2 初始化回调、卸载回调和绑定回调 244 11.9.3 绑定与回调 245 11.9.4 插入请求回调 246 11.9.5 如何利用sfilter.lib 249 第12章 文件系统透明加密 252 12.1 文件透明加密的应用 252 12.1.1 防止企业信息泄密 252 12.1.2 文件透明加密防止企业信息泄密 253 12.1.3 文件透明加密软件的例子 253 12.2 区分进程 254 12.2.1 机密进程与普通进程 254 12.2.2 找到进程名字的位置 255 12.2.3 得到当前进程的名字 256 12.3 内存映射与文件缓冲 257 12.3.1 记事本的内存映射文件 257 12.3.2 Windows的文件缓冲 258 12.3.3 文件缓冲:明文还是密文的选择 259 12.3.4 清除文件缓冲 260 12.4 加密标识 263 12.4.1 保存在文件外、文件头还是文件尾 263 12.4.2 隐藏文件头的大小 264 12.4.3 隐藏文件头的设置偏移 266 12.4.4 隐藏文件头的读/写偏移 267 12.5 文件加密表 267 12.5.1 何时进行加密操作 267 12.5.2 文件控制块与文件对象 268 12.5.3 文件加密表的数据结构与初始化 269 12.5.4 文件加密表的操作:查询 270 12.5.5 文件加密表的操作:添加 271 12.5.6 文件加密表的操作:删除 272 12.6 文件打开处理 273 12.6.1 直接发送IRP进行查询与设置操作 274 12.6.2 直接发送IRP进行读/写操作 276 12.6.3 文件的非重入打开 277 12.6.4 文件的打开预处理 280 12.7 读/写加密和解密 285 12.7.1 在读取时进行解密 285 12.7.2 分配与释放MDL 286 12.7.3 写请求加密 287 12.8 crypt_file的组装 289 12.8.1 crypt_file的初始化 289 12.8.2 crypt_file的IRP预处理 290 12.8.3 crypt_file的IRP后处理 293 第13章 文件系统微过滤驱动 297 13.1 文件系统微过滤驱动简介 297 13.1.1 文件系统微过滤驱动的由来 297 13.1.2 Minifilter的优点与不足 298 13.2 Minifilter的编程框架 298 13.2.1 微文件系统过滤的注册 299 13.2.2 微过滤器的数据结构 300 13.2.3 卸载回调函数 303 13.2.4 预操作回调函数 303 13.2.5 后操作回调函数 306 13.2.6 其他回调函数 307 13.3 Minifilter如何与应用程序通信 309 13.3.1 建立通信端口的方法 310 13.3.2 在用户态通过DLL使用通信端口的范例 311 13.4 Minifilter的安装与加载 314 13.4.1 安装Minifilter的INF文件 314 13.4.2 启动安装完成的Minifilter 316 第14章 网络传输层过滤 317 14.1 TDI概要 317 14.1.1 为何选择TDI 317 14.1.2 从socket到Windows内核 318 14.1.3 TDI过滤的代码例子 319 14.2 TDI的过滤框架 319 14.2.1 绑定TDI的设备 319 14.2.2 唯一的分发函数 320 14.2.3 过滤框架的实现 322 14.2.4 主要过滤的请求类型 323 14.3 生成请求:获取地址 324 14.3.1 过滤生成请求 324 14.3.2 准备解析IP地址与端口 326 14.3.3 获取生成的IP地址和端口 327 14.3.4 连接终端的生成与相关信息的保存 329 14.4 控制请求 330 14.4.1 TDI_ASSOCIATE_ADDRESS的过滤 330 14.4.2 TDI_CONNECT的过滤 332 14.4.3 其他的次功能号 333 14.4.4 设置事件的过滤 334 14.4.5 TDI_EVENT_CONNECT类型的设置事件的过滤 336 14.4.6 直接获取发送函数的过滤 337 14.4.7 清理请求的过滤 339 14.5 本书例子tdifw.lib的应用 341 14.5.1 tdifw库的回调接口 341 14.5.2 tdifw库的使用例子 342 第15章 Windows过滤平台 345 15.1 WFP简介 345 15.2 WFP框架 345 15.3 基本对象模型 347 15.3.1 过滤引擎 347 15.3.2 垫片 347 15.3.3 呼出接口 347 15.3.4 分层 348 15.3.5 子层 349 15.3.6 过滤器 350 15.3.7 呼出接口回调函数 354 15.4 WFP操作 359 15.4.1 呼出接口的注册与卸载 360 15.4.2 呼出接口的添加与移除 360 15.4.3 子层的添加与移除 361 15.4.4 过滤器的添加 362 15.5 WFP过滤例子 362 第16章 NDIS协议驱动 370 16.1 以太网包和网络驱动架构 370 16.1.1 以太网包和协议驱动 370 16.1.2 NDIS网络驱动 371 16.2 协议驱动的DriverEntry 372 16.2.1 生成控制设备 372 16.2.2 注册协议 374 16.3 协议与网卡的绑定 375 16.3.1 协议与网卡的绑定概念 375 16.3.2 绑定回调处理的实现 376 16.3.3 协议绑定网卡的API 378 16.3.4 解决绑定竞争问题 379 16.3.5 分配接收和发送的包池与缓冲池 380 16.3.6 OID请求的发送和请求完成回调 381 16.3.7 ndisprotCreateBinding的最终实现 385 16.4 绑定的解除 390 16.4.1 解除绑定使用的API 390 16.4.2 ndisprotShutdownBinding的实现 392 16.5 在用户态操作协议驱动 395 16.5.1 协议的收包与发包 395 16.5.2 在用户态编程打开设备 396 16.5.3 用DeviceIoControl发送控制请求 397 16.5.4 用WriteFile发送数据包 399 16.5.5 用ReadFile发送数据包 400 16.6 在内核态完成功能的实现 402 16.6.1 请求的分发与实现 402 16.6.2 等待设备绑定完成与指定设备名 402 16.6.3 指派设备的完成 403 16.6.4 处理读请求 406 16.6.5 处理写请求 408 16.7 协议驱动的接收回调 412 16.7.1 和接收包有关的回调函数 412 16.7.2 ReceiveHandler的实现 413 16.7.3 TransferDataCompleteHandler的实现 417 16.7.4 ReceivePacketHandler的实现 418 16.7.5 接收数据包的入队 420 16.7.6 接收数据包的出队和读请求的完成 422 第17章 NDIS小端口驱动 427 17.1 小端口驱动的应用与概述 427 17.1.1 小端口驱动的应用 427 17.1.2 小端口驱动示例 428 17.1.3 小端口驱动的运作与编程概述 429 17.2 小端口驱动的初始化 429 17.2.1 小端口驱动的DriverEntry 429 17.2.2 小端口驱动的适配器结构 431 17.2.3 配置信息的读取 433 17.2.4 设置小端口适配器上下文 433 17.2.5 MPInitialize的实现 434 17.2.6 MPHalt的实现 437 17.3 打开ndisprot设备 438 17.3.1 IO目标 438 17.3.2 给IO目标发送DeviceIoControl请求 439 17.3.3 打开ndisprot接口并完成配置设备 441 17.4 使用ndisprot发送包 443 17.4.1 小端口驱动的发包接口 443 17.4.2 发送控制块(TCB) 444 17.4.3 遍历包组并填写TCB 446 17.4.4 写请求的构建与发送 449 17.5 使用ndisprot接收包 451 17.5.1 提交数据包的内核API 451 17.5.2 从接收控制块(RCB)提交包 452 17.5.3 对ndisprot读请求的完成函数 454 17.5.4 读请求的发送 456 17.5.5 用于读包的WDF工作任务 457 17.5.6 ndisedge读工作任务的生成与入列 459 17.6 其他的特征回调函数的实现 461 17.6.1 包的归还 461 17.6.2 OID查询处理的直接完成 462 17.6.3 OID设置处理 465 第18章 NDIS中间层驱动 467 18.1 NDIS中间层驱动概述 467 18.1.1 Windows网络架构总结 467 18.1.2 NDIS中间层驱动简介 468 18.1.3 NDIS中间层驱动的应用 469 18.1.4 NDIS包描述符结构深究 470 18.2 中间层驱动的入口与绑定 473 18.2.1 中间层驱动的入口函数 473 18.2.2 动态绑定NIC设备 474 18.2.3 小端口初始化(MpInitialize) 475 18.3 中间层驱动发送数据包 477 18.3.1 发送数据包原理 477 18.3.2 包描述符“重利用” 478 18.3.3 包描述符“重申请” 481 18.3.4 发送数据包的异步完成 482 18.4 中间层驱动接收数据包 484 18.4.1 接收数据包概述 484 18.4.2 用PtReceive接收数据包 485 18.4.3 用PtReceivePacket接收 490 18.4.4 对包进行过滤 491 18.5 中间层驱动程序查询和设置 494 18.5.1 查询请求的处理 494 18.5.2 设置请求的处理 496 18.6 NDIS句柄 498 18.6.1 不可见的结构指针 498 18.6.2 常见的NDIS句柄 499 18.6.3 NDIS句柄误用问题 500 18.6.4 一种解决方案 502 18.7 生成普通控制设备 503 18.7.1 在中间层驱动中添加普通设备 503 18.7.2 使用传统方法来生成控制设备 505 第19章 IA-32汇编基础 511 19.1 x86内存、寄存器与堆栈 511 19.1.1 _asm关键字 511 19.1.2 x86中的mov指令 512 19.1.3 x86中的寄存器与内存 512 19.1.4 赋值语句的实现 513 19.2 x86中函数的实现 514 19.2.1 一个函数的例子 514 19.2.2 堆栈的介绍 515 19.2.3 寄存器的备份和恢复 516 19.2.4 内部变量与返回值 518 19.3 x86中函数的调用与返回 521 19.3.1 函数的调用指令call 521 19.3.2 通过堆栈传递参数 521 19.3.3 从函数返回 523 19.3.4 三种常见的调用协议 524 19.4 从32位汇编到64位汇编 526 19.4.1 Intel 64与IA-32体系架构简介 526 19.4.2 64位指令与32位指令 526 19.4.3 通用寄存器 527 19.5 64位下的函数实现 528 19.5.1 函数概览 528 19.5.2 32位参数的传递 529 19.5.3 64位参数与返回值 530 19.5.4 栈空间的开辟与恢复 531 第20章 IA-32体系中的内存地址 534 20.1 内存的虚拟地址 534 20.1.1 C语言中的内存地址 534 20.1.2 虚拟地址的构成 535 20.1.3 段的选择 536 20.2 全局描述符表和段描述符 538 20.2.1 全局描述符表 538 20.2.2 段类型 539 20.2.3 段寄存器与段选择子 540 20.2.4 64位模式下的段 541 20.3 分段编程实践 542 20.3.1 系统表寄存器的结构 542 20.3.2 在汇编语言中获取全局描述表的位置 543 20.3.3 调试范例:sgdt指令的错误使用 545 20.3.4 在64位下获得全局描述符表 547 20.4 线性地址基础 549 20.4.1 分页控制机制 550 20.4.2 线性地址的转换 551 20.4.3 混合页面大小 552 20.4.4 32位物理地址的页目录和页表项 552 20.5 各种特殊分页方式 555 20.5.1 PAE分页方式 555 20.5.2 PSE-36分页机制 558 20.5.3 IA-32e模式下的线性地址 559 20.6 分页编程实践 562 20.6.1 页目录和页目录指针表的获取 562 20.6.2 页表的获取 564 20.6.3 线性地址的结构 567 第21章 处理器权限级别切换 571 21.1 Ring0和Ring3权限级别 571 21.2 保护模式下的分页内存保护 572 21.3 分页内存不可执行保护 574 21.3.1 不可执行保护原理 574 21.3.2 不可执行保护的漏洞 575 21.3.3 上机实践 577 21.4 权限级别的切换 579 21.4.1 调用门及其漏洞 579 21.4.2 sysenter和sysexit指令 581 21.4.3 上机实践 583 第22章 IA-32体系结构中的中断 585 22.1 中断基础知识 585 22.1.1 中断描述符表 585 22.1.2 中断处理过程 587 22.1.3 64位模式下的中断处理机制 589 22.1.4 多核下的中断 589 22.2 Windows中断机制 593 22.3 中断编程实践 596 22.3.1 IDT Hook 596 22.3.2 巧用IDT Hook实现安全防护 598 第23章 Windows内核挂钩 601 23.1 系统服务描述符表挂钩 602 23.1.1 系统服务描述符表(SSDT) 602 23.1.2 系统服务描述符表挂钩的意图 603 23.1.3 寻找要挂钩的函数的地址 604 23.1.4 函数被挂钩的过程 605 23.1.5 具体实现的代码 606 23.2 函数导出表挂钩 608 23.2.1 内核函数的种类 608 23.2.2 挂钩IoCallDriver 610 23.2.3 对跳转地址进行修改 611 23.3 Windows 7系统下IofCallDriver的跟踪 612 23.4 Windows 7系统下内联挂钩 615 23.4.1 写入跳转指令并拷贝代码 615 23.4.2 实现中继函数 617 高 级 篇 第24章 Windows通知与回调 620 24.1 Windows的事件通知与回调 620 24.2 常用的事件通知 620 24.2.1 创建进程通知 621 24.2.2 创建线程通知 625 24.2.3 加载模块通知 626 24.2.4 注册表操作通知 629 24.3 Windows回调机制 636 24.3.1 回调对象 636 24.3.2 回调对象的创建 637 24.3.3 回调对象的注册 637 24.3.4 回调的通告 638 24.4 安全的死角,回调的应用 639 第25章 保护进程 640 25.1 内核对象简介 640 25.2 内核对象的结构 641 25.3 保护内核对象 642 25.3.1 处理对象的打开 643 25.3.2 处理句柄的复制 644 25.3.3 处理句柄的继承 646 25.4 进程的保护 652 25.4.1 保护原理 652 25.4.2 Vista以后的进程对象保护 654 25.4.3 进程的其他保护 655 附录A 如何使用本书的源码光盘 656 附录B 练习题 659

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值