Dokan 库
Copyright(c) Hiroki Asakawa http://dokan-dev.net/en

什么是Dokan库
=====================================================================
当你想要在Windows下创建一个新的文件系统的时候,比如,改进FAT或者NTFS,你需要开发一个文件系统驱动。在内核模式开发一个设备驱动是一个非常棘手的问题。通过使用Dokan库,你可以很轻松地创建一个属于你自己的文件系统,而不需要写一个设备驱动。Dokan库非常相思雨FUSE(Linux用户下的文件系统),但是工作在Windows环境下。

许可声明
===================================================================
Dokan库包括LGPL(宽松的GPL授权声明,表示开源代码可以用到非开源私有程序,译者注),MIT许可程序(和LGPL有些类似,编者注)

- 用户模式库(dokan.dll)          LGPL
- 驱动(dokan.sys)   LGPL
- 控制程序(dokanctl.exe)  MIT
- 挂载服务(mounter.exe)  MIT
- 样本程序(mirror.c)   MIT

需要进一步的细节,请查看许可文件。
LGPL license.lgpl.txt
GPL  license.gpl.txt
MIT  license.mit.txt

你可以通过以下网址获取源码 http://dokan-dev.net/en/download

环境
====================================================================
Dokan库运行在 Windowx XP,2003,Vista,2008,7 x86 和 Windows 2003,Vista,2008,7 x64.

它是怎么工作的
====================================================================
Dokan库包括一个用户态DLL(dokan.dll)和一个内核模式文件系统驱动(dokan.sys)。一旦Dokan文件系统驱动安装好之后,你就可以创建和Windows下可以看见的普通的文件系统一样的文件系统了。这个被我们称之为文件系统级的应用程序可以利用Dokan库来创建文件系统。
用户态程序的文件操作请求(比如创建文件,读取文件,写文件。。。)将会被发送到Windows I/O子系统(运行在内核态),紧接着送到Dokan文件系统驱动(dokan.sys)。
通过使用Dokan用户态库(dokan.dll)提供的函数,文件系统程序能够注册回调函数到文件系统驱动中。
文件系统驱动将会调用这些回调例程(routines),为了能够响应它收到的请求。回调例程的结果会被送回到用户态程序。
比如,当Windows浏览器请求打开一个目录的时候,OpenDirectory请求会被送到Dokan文件系统驱动并且驱动会调用文件系统程序提供的OpenDirectory回调函数。这个例程的结果将被送到Windows浏览器,作为OpenDirectory请求的响应。因此,Dokan文件系统驱动扮演者一个基于用户程序和文件系统程序之间的一个代理角色。
这种处理方式的优点就是它允许开发者在用户态下开发文件系统,这样安全并且容易调试。

库的部件组成和安装
=======================================================================
当我们运行安装程序是,它会安装Dokan文件系统驱动(dokan.sys),注册Dokan挂载服务(mounter.exe)和一系列库。安装的文件详细清单如下:

系统文件夹/dokan.dll                                Dokan用户态库
系统文件夹/drivers/dokan.sys                   Dokan挂载服务
ProgramFiles文件夹/Dokan/DokanLibrary/mounter.exe           Dokan挂载服务
ProgramFiles文件夹/Dokan/DokanLibrary/dokanctl.exe           Dokan控制程序
ProgramFiles文件夹/Dokan/DokanLibrary/dokan.lib                 Dokan导入库
ProgramFiles文件夹\Dokan\DokanLibrary\dokan.h                   Dokan库头文件
ProgramFiles文件夹\Dokan\DokanLibrary\readme.txt              这个文件(你正在读的,译者注)

你可以使用控制面板的 Add/Remove 程序来卸载Dokan。卸载后,需要重启你的电脑。

怎样创建你的文件系统
======================================================================
创建文件系统,一个应用程序需要在DOKAN_OPERATIONS结构实现函数(在dokan.h中声明)。函数一旦实现,你就可以把DOKAN_OPERRATIONS作为参数调用DokanMain函数。
为了挂载文件系统,在DOKAN_OPERATIONS中的函数语义和WindowsAPI语义相似并且有相同的名字。这些函数的参数和相应的WindowsAPI相同。这些函数被很多的线程调用,所以他们需要保证线程安全,否则很多问题可能产生。

这些函数典型的以下面的顺序调用:
1、创建文件CreatFile(打开文件)
2、其他函数
3、清除Cleanup
4、关闭文件CloseFile

文件访问操作(展现目录,读取文件属性。。。)之前,经常需要调用文件创建函数(打开目录,创建文件。。。)。在另一方面,当CloseFile Windows API关闭文件的时候,Dokan文件系统驱动会调用清除函数Cleanup。当操作成功结束,每一个函数应该返回0,否则返回一个负数表示错误码。错误码和Windows系统错误码相反。比如当CreatFile不能打开文件,你应该返回-2(-1*ERROR_FILE_NOT_FOUND)。

每一个函数的最后一个参数是一个DOKAN_FILE_INFO结构:
   typedef struct _DOKAN_FILE_INFO {

ULONG64 Context;
       ULONG64 DokanContext;
       ULONG   ProcessId;
       BOOL    IsDirectory;

} DOKAN_FILE_INFO, *PDOKAN_FILE_INFO;

每个用户态的文件句柄(file handle)都和DOKAN_FILE_INFO结构相关。因此,如果相同的文件句柄已经使用,结构内容就不会改变。当CreatFile系统调用打开文件时,DOKAN_FILE_INFO结构就会创建;当CloseFile系统调用关闭文件时,这个结构就会销毁。
这个结构中每一个成员的意义如下:
Context:一个指定的值,由文件系统应用程序指定。文件系统程序可以自由的使用这个变量来存储在文件访问阶段的固定值(从创建文件到关闭文件)比如文件句柄等等。
DokanContext:保留,Dokan库使用。
ProcessId:打开文件进程的进程ID。
IsDirectory:判断打开的文件是否是一个目录。

其他的如下
   int (*CreateFile) (
       LPCWSTR,      // FileName,文件名
       DWORD,        // DesiredAccess,需要访问
       DWORD,        // ShareMode,共享模式
       DWORD,        // CreationDisposition,创建意向
       DWORD,        // FlagsAndAttributes,标识和属性
       PDOKAN_FILE_INFO);

int (*OpenDirectory) (
       LPCWSTR,          // FileName
       PDOKAN_FILE_INFO);

int (*CreateDirectory) (
       LPCWSTR,          // FileName
       PDOKAN_FILE_INFO);

当变量IsDirectory设置为TRUE,操作的文件是目录。当IsDirectory时FALSE时,如果当前的操作是对目录操作,则写文件系统程序的程序员需要把它设置为TRUE。如果IsDirectory的值是FALSE,但是当前操作不是对目录操作,则程序员不需要更变该值。需要注意的是,当访问目录时,把IsDirectory的值设置为TRUE对于Dokan库而言是非常重要的。如果它没有被准确的设定,那么Dokan库将不知道这个操作是作用于目录,会带来许多问题。如果CreationDisposition 设定为 CREATE_ALWAYS 或者OPEN_ALWAYS 并且当前文件已经存在,那么CreatFile函数需要返回ERROR_ALEADY_EXISTS(183)

int (*Cleanup) (
       LPCWSTR,      // FileName
       PDOKAN_FILE_INFO);

int (*CloseFile) (
       LPCWSTR,      // FileName
       PDOKAN_FILE_INFO);

当Windows API提供的CloseHandle函数一旦执行,Cleanup函数将被调用。当函数CreateFile被调用的时候,如果文件系统程序在Context变量中存放了文件句柄,这个将在Cleanup函数中关闭,而不是CloseFile函数。
如果用户态程序调用CloseHandle,并且马上打开相同的文件,那么文件系统程序CloseFile函数在CreatFile API被调用之前可能不会被调用。这有可能引发共享问题。

值得注意的是:当用户使用内存映射文件时,WriteFile或者ReadFile函数可能Cleanup之后会被调用,这是为了完成I/O操作。文件系统应用也应该在这种情况下正常运行。

int (*FindFiles) (
       LPCWSTR,           // PathName,路径名
       PFillFindData,     //调用此函数时, 使用PWIN32_FIND_DATAW作为参数
       PDOKAN_FILE_INFO); //  (see PFillFindData definition)

// 你应该实现 FindFiles 或者 FindFilesWithPattern
   int (*FindFilesWithPattern) (
       LPCWSTR,           // PathName,路径名
       LPCWSTR,           // SearchPattern,查询方式
       PFillFindData,     // call this function with PWIN32_FIND_DATAW
       PDOKAN_FILE_INFO);

调用FindFiles或者FindFilesWithPattern函数是为了相应目录列表请求。你应该实现FindFiles或者FindFilesWithPattern(其中一个,译者注)。对于每一个目录入口(entry),文件系统程序都应该调用FillFindData函数(作为一个函数参数供 FindFiles, FindFilesWithPattern调用),FillFindData会使用包含目录信息的WIN32_FIND_DATAW结构:
FillFindData( &win32FindDataw, DokanFileInfo )。
处理通配符模式(wildcard patterns)是文件系统的责任,因为Windows命令终端(shells)不能够很好地处理模式匹配。当文件系统程序提供FindFiles,通配符模式将会通过Dokan库来自动的处理。你可以通过实现FindFilesWithPattern函数来控制通配符匹配的过程。Dokan库(dokan.dll)提供的DokanIsNameInExpression函数接口可以用来处理通配符匹配。

挂载
====================================================================
#define DOKAN_OPTION_DEBUG       1 // 输出调试信息
   #define DOKAN_OPTION_STDERR      2 // 输出调试信息到标准错误输出
   #define DOKAN_OPTION_ALT_STREAM  4 // use alternate stream
   #define DOKAN_OPTION_KEEP_ALIVE  8 // 使用自动卸载
   #define DOKAN_OPTION_NETWORK    16 // 使用网络驱动
                               //你需要安装Dokan网络提供器(provider)
   #define DOKAN_OPTION_REMOVABLE  32 // 使用可移除驱动

typedef struct _DOKAN_OPTIONS {
       USHORT  Version;  // 支持的Dokan版本, 比如. "530" (Dokan ver 0.5.3)
       ULONG   ThreadCount;  // 使用的线程数
       ULONG   Options;  // combination of DOKAN_OPTIONS_*
       ULONG64 GlobalContext;  // FileSystem 可以使用这个
       LPCWSTR MountPoint;  // 挂载点 "M:\" (驱动字符) 或者
                            // "C:\mount\dokan" (NTFS中的路径名)
   } DOKAN_OPTIONS, *PDOKAN_OPTIONS;

int DOKANAPI DokanMain(
       PDOKAN_OPTIONS    DokanOptions,
       PDOKAN_OPERATIONS DokanOperations);

如上所述,文件系统可以通过DokanMain函数挂载。函数一直阻塞(block)直到文件系统卸载。再把这些参数传递给DokanMain 函数之前,文件系统程序应该为Dokan运行库填写满DokanOptions中的选项,为文件系统操作填写满DokanOperations中的函数指针(比如CreateFile, ReadFile, CloseHandle, ...)。DokanOperations结构中的函数需要保证线程安全,因为他们都被执行不同上下文(contexts)的多线程调用(不是调用DokanMain的线程)。

Dokan选项如下:
 版本:Dokan库的版本号。你需要一个支持的版本。Dokan库可能因为版本号的不同改变一些行为(behavior)。比如ie.530(Dokan 0.5.3)
 线程数:Dokan库内部的线程数。如果这个值设定为0,那么将使用默认的值。当我们调试文件系统的时候,文件系统程序应该设定为1来避免多线程的并发冲突。
 选项:DOKAN_OPTION_*常量的结合。
 全局域:你的文件系统可以使用这个变量来存放一个挂载特定的结构。
 挂载点:一个挂载点挂载点 "M:\" (驱动字符) 或者 "C:\mount\dokan" (需要是空的)NTFS中的路径名。

如果挂载成功,那么返回值是DOKAN_SUCCESS,否则返回值如下。
 #define DOKAN_SUCCESS                0
   #define DOKAN_ERROR                 -1 /* 普通错误 */
   #define DOKAN_DRIVE_LETTER_ERROR    -2 /* 坏的驱动字符 */
   #define DOKAN_DRIVER_INSTALL_ERROR  -3 /* 不能安装驱动 */
   #define DOKAN_START_ERROR           -4 /* 驱动出了某个问题 */
   #define DOKAN_MOUNT_ERROR           -5 /* 不能分配驱动字符或者挂载点 */
   #define DOKAN_MOUNT_POINT_ERROR     -6 /* 挂载点无效 */

卸载
=======================================================================
文件系统可以通过调用DokanUnmount函数来卸载。大部分情况下,程序或者shell使用文件系统挂起,卸载操作来解决问题。因为当文件系统卸载的时候,我们可以把系统恢复到原来的状态。
用户可能使用DokanCtl来卸载文件系统:
 > dokanctl.exe /u DriveLetter

杂项:
======================================================================
如果在Dokan库或者使用库的文件系统程序中有bug,那么你的Windows系统将会蓝屏。因此,我们强烈建议你使用虚拟机来开发文件系统应用程序。

Dokan官方说明文档的更多相关文章

  1. SWFUpload 2.5.0版 官方说明文档 中文翻译版

    原文地址:http://www.cnblogs.com/youring2/archive/2012/07/13/2590010.html#setFileUploadLimit SWFUpload v2 ...

  2. SpringMVC的API和Spring的官方说明文档的地址。

    SpringMVC的API和Spring的官方说明文档的地址. 1.SpringMVC的API的URL: http://docs.spring.io/spring/docs/current/javad ...

  3. 【转】SWFUpload 官方说明文档(2.5.0版)

    原文出自:http://www.runoob.com/w3cnote/swfupload-document.html SWFUpload使用指南请查阅:http://www.w3cschool.cc/ ...

  4. locust===官方说明文档,关于tasks

    安装: >>> pip  install locust locust在官方simple_code中如下: from locust import HttpLocust, TaskSet ...

  5. ICE中间件说明文档

    ICE中间件说明文档 1       ICE中间件简介 2       平台核心功能 2.1        接口描述语言(Slice) 2.2        ICE运行时 2.2.1         ...

  6. BasicExcel说明文档

    BasicExcel说明文档 BasicExcel原始链接:http://www.codeproject.com/Articles/13852/BasicExcel-A-Class-to-Read-a ...

  7. 为ASP.NET WEB API生成人性化说明文档

    一.为什么要生成说明文档 我们大家都知道,自己写的API要供他人调用,就需要用文字的方式将调用方法和注意事项等写成一个文档以更好的展示我们设计时的想法和思路,便于调用者更加高效的使用我们的API. 当 ...

  8. 【腾讯GAD暑期训练营游戏程序班】游戏场景管理作业说明文档

    场景管理作业说明文档                              用了八叉树的算法,测出三层时最快,区域范围内物体数量为21块,控制台打印出的结果如图所示: 场景物体:游戏中,所有具有空 ...

  9. 浏览器内核控制Meta标签说明文档

    浏览器内核控制Meta标签说明文档 原文链接 背景介绍 由于众所周知的情况,国内的主流浏览器都是双核浏览器:基于Webkit内核用于常用网站的高速浏览.基于IE的内核用于兼容网银.旧版网站.以360的 ...

随机推荐

  1. Objective-C运行时态消息传递--拼接方法名

    做IOS开发的人都知道,Objective-C语言中方法的调用是运行时采取绑定的,在编译过程中只声明该方法的存在. 那么我们来简单说下在运行时,类的消息传递. 在运行时,每个方法如[self meth ...

  2. Redis的安装和使用之------Redis相关运用

    原文  http://wangzhijian.blog.51cto.com/6427016/1731962 一.简介 REmote DIctionary Server(Redis) 是一个由 Salv ...

  3. Socket编程中的长连接、短链接以及心跳包机制详解

    参考:http://blog.csdn.net/zdwzzu2006/article/details/7723738 一.定义 1.TCP连接 当网络通信时采用TCP协议时,在真正的读写操作之前,se ...

  4. ng1 http 读取json数据

    在前端开发过程中,有时后端还没开发出接口,需要经常自己构造获取本地mock数据. AngularJS XMLHttpRequest $http 是 AngularJS 中的一个核心服务,用于读取远程服 ...

  5. 前端之Photoshop切片

    什么是切片 ?     (Photoshop中的切片) 切片:将图片切成几部分,一片一片往上传,这样上传的速度比较快.每个切片作为一个独立的文件传输,文件中包含切片自己的设置.颜色调板.链接.翻转效果 ...

  6. 开源的许可证GPL、LGPL、BSD、Apache 2.0的通俗解释

    软件开发者要开源软件,不单单是开放源代码就可以了,选择一种许可证很重要,一个许可证之于软件就相当于价值观之于普通人,代表了这个软件的基本品性.一个错误的许可证选择可能会直接导致整个项目的失败. 各种开 ...

  7. hdu1116回溯N皇后问题

    题目连接 经过思考,不难发现:恰好N个皇后放在不同行不同列,那么是不是可以转换成N个皇后所在行分别确定(一人一行)的情况下对她们的所在列的枚举. 也就是列的全排列生成问题,我们用c[x]表示x行皇后的 ...

  8. UIKit

    UIAlertView UIAlertView 调用创建好对象的[testObject show]的show方法即可弹出UIAlertView UILabel UILabel常见属性 text:显示文 ...

  9. Exchange 2010 实用小技巧

    #Exchange安装必须开启防火墙服务 #批量建用户: for /f  "tokens=1,2,3,4,5,6,7 delims=," %a in (c:\users.csv) ...

  10. json的遍历

    第一种json结构: var jsongood = {"goods":[{"parentId":"null","productId ...