原文地址:http://www.cnblogs.com/xxonehjh/p/3634889.html

因工作需要,最近与同事合作使用Dokan开发了一个虚拟磁盘的简单程序,初步实现了远程目录映射到本地虚拟磁盘的功能。

远程服务端是用Python写的,主要是将远程主机上的目录文件传给客戶端,在这里就不细说了。

Dokan客户端则由Delphi开发,其参考代码来自网络上的Delphi例子,比如Mirror Driver

本篇文章主要是Dokan开发过程的一些总结,所以不会对Dokan本身做介绍,与Dokan有关的资料及代码,请到google里搜索,或到Dokan的官方网站去下载(Dokan官网),源码是C语言的,应用例子有Ruby、.Net及C的。如果想要Delphi的例子代码,只能自己去找了。

刚开始时由于不清楚如何用Dokan来实现一个文件系统,所以需要做一些试验,结果一不小心就蓝屏了!悲剧啊,用XP系统已经好多年没遇到蓝屏了。几次蓝屏之后,终于受不了了,于是在VMWare里装了个虚拟机的XP,这下不怕蓝屏了,哈哈。强烈建议装个虚拟机来玩Dokan,否则刚开始的时候你会蓝屏N次!

为简单起见,我做的Dokan虚拟磁盘采用将远程目录缓存到本地目录的方法来实现,这样就不用自己维护一堆目录、文件的信息,只需要关注如何更新同步目录与文件就可以了。由于Dokan是多线程的,因此实现时需要做到线程安全;查看Dokan使用的结构类型,发现只有两个成员可以使用,即DOKAN_OPTIONS里的GlobalContext和DOKAN_FILE_INFO里的Context,其中GlobalContext只能用来存储全局的信息,比如存放线程实例的指针,这样一来,实际上就剩下 DOKAN_FILE_INFO里的Context 一个成员可以用来存储与文件有关的信息了,一般用它来存储文件指针。我这次实现没有自己定义类来管理目录与文件,而是直接利用缓存目录,因此只需要处理文件指针和是否需要更新文件两个信息就可以了,而 DOKAN_FILE_INFO里的Context是Int64的,在Win32里可以用32位存文件指针,另32位用来存储文件更新信息。

//以下来自于Dokan.pas里的定义

_DOKAN_OPTIONS = packed record
    DriveLetter: WCHAR; // Drive letter to be mounted
    ThreadCount: Word; // Number of threads to be used
    DebugMode: Boolean;
    UseStdErr: Boolean;
    UseAltStream: Boolean;
    UseKeepAlive: Boolean;
    GlobalContext: Int64; // User-mode filesystem can use this variable
end;
PDOKAN_OPTIONS = ^_DOKAN_OPTIONS;
DOKAN_OPTIONS = _DOKAN_OPTIONS;

TDokanOptions = _DOKAN_OPTIONS;
PDokanOptions = PDOKAN_OPTIONS;

_DOKAN_FILE_INFO = packed record
    Context: Int64; // User-mode filesystem can use this variable
    DokanContext: Int64; // Reserved. Don't touch this!
    DokanOptions: PDOKAN_OPTIONS;
    ProcessId: ULONG; // Process id for the thread that originally requested the I/O operation
    IsDirectory: Boolean; // Indicates a directory file
    DeleteOnClose: Boolean; // Delete when Cleanup is called
    PagingIo: Boolean; // Read or write is paging IO
    SynchronousIo: Boolean; // Read or write is synchronous IO
    Nocache: Boolean; // No caching
    WriteToEndOfFile: Boolean; // If true, write to the current end of file instead of Offset parameter
end;
PDOKAN_FILE_INFO = ^_DOKAN_FILE_INFO;
DOKAN_FILE_INFO = _DOKAN_FILE_INFO;

TDokanFileInfo = _DOKAN_FILE_INFO;
  PDokanFileInfo = PDOKAN_FILE_INFO;

研究了几天,发现只需要实现少数几个回调函数就可以了:

1.FindFiles: 在这个回调函数里可以实现从远程目录同步其下的所有目录及文件。当然也可以在OpenDirectory回调函数里做,但实际使用时我发现OpenDirectory调用太频繁,而FindFiles调用次数要少一些。

2.CreateDirectory: 在这个回调函数里可以实现同步创建远程目录。

3.DeleteDirectory: 实现同步删除远程目录。 

4.CreateFile: 这个回调函数调用极其频繁,每次操作目录文件(包括打开文件)时首先都会调到它,我在这里实现了从远程目录同步更新本地文件的内容。需要注意的是,在虚拟磁盘里新建文件时,为了能在Cleanup里正确同步到远程目录,必须记下来。我使用了以下代码来实现:

if not DokanFileInfo.IsDirectory and (CreationDisposition in [CREATE_NEW, OPEN_ALWAYS, CREATE_ALWAYS]) then begin
  MySetFileDate(DokanFileInfo, DateTimeToFileDate(Now)); //Cleanup里会判断FileDate来决定是否保存到远程目录
end;

5.WriteFile: 可用于指示文件是否已修改,和Cleanup配合,以便保存文件时能正确提交到远程服务器。需要注意的WriteFile可能会被调用多次,所以它并不适合提交修改,只能记录修改标志。

6.Cleanup: 同步删除远程目录中的文件及保存本地修改的文件到远程目录。实现时我发现,在Cleanup中判断DokanFileInfo.DeleteOnClose及DokanFileInfo.IsDirectory来删除目录的代码根本就不会走到(所以我在DeleteDirectory里实现删除目录的同步),而删除文件则没问题。

这里有一点需要注意:因为执行Cleanup之前,可能会多次调用CreateFile,比如记事本保存文档时就会执行两次CreateFile之后再调用Cleanup,所以我在Cleanup的最后执行MySetFileDate(DokanFileInfo, 0)来清空标志,而没有在CreateFile里清空标志。

7.MoveFile: 这个回调函数仅在移动虚拟磁盘里的文件到另一个虚拟磁盘目录中去时才触发,故实现在远程目录中同步移动文件后,就可以正常实现目录文件的移动了。由于操作多个目录文件时,Windows会每个目录文件分别调用相关操作,因此实现这个回调函数后,自然就实现了多个目录文件的移动。如果是从其他盘移动目录文件到虚拟磁盘或从虚拟磁盘移动目录文件到其他盘,都不会触发MoveFile这个回调函数;而目录文件改名,则会触发MoveFile这个回调函数。

实现时还有一个调试信息如何显示的问题,对控制台程序,可以直接写到控制台;而对带窗口的程序,可以写日志文件,也可以发Windows消息。我采用了SendMessage来处理调试信息,具体实现请参看下面的代码。

最终的实现是由一个线程来实现Dokan虚拟磁盘的,目录与文件的同步函数则放到一个单独的单元文件里,连接远程服务端则采用IndyTCPClient实现,传输采用了JSON,以便于和服务端的Python脚本通讯。

Dokan[转]的更多相关文章

  1. Dokan官方说明文档

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

  2. Dokan简介[转]

    1.      Dokan Library 简介 Dokan Library 帮助程序员在windows系统下轻松建立用户级文件系统,不需要写设备驱动,其与FUSE(Linux user mode f ...

  3. Dokan虚拟磁盘开发实战

    因工作需要,最近与同事合作使用Dokan开发了一个虚拟磁盘的简单程序,初步实现了远程目录映射到本地虚拟磁盘的功能. 远程服务端是用Python写的,主要是将远程主机上的目录文件传给客戶端,在这里就不细 ...

  4. 利用dokan作虚拟磁盘开发

    dokan是用户态的文件系统驱动,可以称之为fuse for windows.可以用来开发虚拟磁盘,即在“我的电脑”中虚拟出一个硬盘来,可以是硬盘,也可以是可移动磁盘或者网络硬盘. CreateFil ...

  5. 利用SSH Filesystem实现远程文件系统

         远程文件系统的访问有很多种不同的实现方式,一些常见的连接方式比其它特定情况下的更有用.最著名的一个例子就是微软的通用互联网文件系统(CIFS),它可以容许微软Windows"映射网 ...

  6. sshpass----------------sshfs--sftp(sublime)

    源码下载地址:http://sourceforge.net/projects/sshpass/   tar -zxvf sshpass-1.05.tar.gz cd sshpass-1.05 ./co ...

  7. winsshfs的快速入手

    之前在公司使用mac ,并且通过mac下的osfuse和sshfs连接,直接将虚拟机的文件目录同步到了本地,并且可以进行实时操作修改,对于写项目,确实是省了很大一部分上传的精力. 于是在自己的win下 ...

  8. Windows编译安装使用cephfs客户端

    本文介绍如何将cephfs映射到windows的一个盘上,以磁盘映射的方式访问cephfs. 1.下载必要安装包 tdm-gcc:(安装时选32位)https://sourceforge.net/pr ...

  9. 虚拟机和主机文件实时同步 -- winsshfs的快速入手

    之前在公司使用mac ,并且通过mac下的osfuse和sshfs连接,直接将虚拟机的文件目录同步到了本地,并且可以进行实时操作修改,对于写项目,确实是省了很大一部分上传的精力. 于是在自己的win下 ...

随机推荐

  1. Attribute value is quoted with " which must be escaped when used within the value 问题解决

    访问JSP时,报错:Attribute value is quoted with " which must be escaped when used within the value .相信 ...

  2. Python学习笔记基础篇——总览

    Python初识与简介[开篇] Python学习笔记——基础篇[第一周]——变量与赋值.用户交互.条件判断.循环控制.数据类型.文本操作 Python学习笔记——基础篇[第二周]——解释器.字符串.列 ...

  3. mysql报错1024-can't get hostname for your address

    前一段时间mysql用的好好的,突然一天,mysql启动后,在使用navicat连接数据库的时候 报错1024-can't get hostname for your address 这里我是这样解决 ...

  4. Java入门第三季排序练习

    package imooc_collection_map_demo; import java.util.ArrayList;import java.util.Collections;import ja ...

  5. IOS开发调整UILabel的行间距

    CGFloat heih = 20;   NSString * cLabelString = @"这是测试UILabel行间距的text.这是测试UILabel行间距的text.n 这是测试 ...

  6. 自动生成 Makefile (automake/autoconf 入门)

    作为Linux 下的程序开发人员,大家一定都遇到过Makefile ,用make 命令来编译自己写的程序确实是很方便.一般情况下,大家都是手工写一个简单Makefile ,如果要想写出一个符合自由软件 ...

  7. span标记

    span标记为内联元素,本身布局有宽高所以要给他个属性 display:block; 转成快元素.

  8. [APP]如果你想反编译

    反编译,主要用到两类工具,一个就是获取apk包的包名(appPackage)和类名(appActivity)的工具,其实就是反编译出java源代码,dex2jar和jd-gui:一个是将一个apk包反 ...

  9. 华硕笔记本进pe之前的设置

    1.开机的时候长按F2键进入BIOS界面,通过方向键进[Secure]菜单,通过方向键选择[Secure Boot Control]选项,将其设定为 "Disabled"2.通过方 ...

  10. My Sql多表操作(转载)

    DELETE 在Mysql4.0之后,mysql开始支持跨表delete. Mysql可以在一个sql语句中同时删除多表记录,也可以根据多个表之间的关系来删除某一个表中的记录. 假定我们有两张表:Pr ...