热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

Dokan[转]

原文地址:http:www.cnblogs.comxxonehjhp3634889.html因工作需要,最近与同事合作使用Dokan开发了一个虚拟磁盘的简单程序,初步实现了远程目录

因工作需要,最近与同事合作使用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[转]


推荐阅读
  • QUIC协议:快速UDP互联网连接
    QUIC(Quick UDP Internet Connections)是谷歌开发的一种旨在提高网络性能和安全性的传输层协议。它基于UDP,并结合了TLS级别的安全性,提供了更高效、更可靠的互联网通信方式。 ... [详细]
  • 深入理解OAuth认证机制
    本文介绍了OAuth认证协议的核心概念及其工作原理。OAuth是一种开放标准,旨在为第三方应用提供安全的用户资源访问授权,同时确保用户的账户信息(如用户名和密码)不会暴露给第三方。 ... [详细]
  • 技术分享:从动态网站提取站点密钥的解决方案
    本文探讨了如何从动态网站中提取站点密钥,特别是针对验证码(reCAPTCHA)的处理方法。通过结合Selenium和requests库,提供了详细的代码示例和优化建议。 ... [详细]
  • CSS 布局:液态三栏混合宽度布局
    本文介绍了如何使用 CSS 实现液态的三栏布局,其中各栏具有不同的宽度设置。通过调整容器和内容区域的属性,可以实现灵活且响应式的网页设计。 ... [详细]
  • 导航栏样式练习:项目实例解析
    本文详细介绍了如何创建一个具有动态效果的导航栏,包括HTML、CSS和JavaScript代码的实现,并附有详细的说明和效果图。 ... [详细]
  • 深入理解Tornado模板系统
    本文详细介绍了Tornado框架中模板系统的使用方法。Tornado自带的轻量级、高效且灵活的模板语言位于tornado.template模块,支持嵌入Python代码片段,帮助开发者快速构建动态网页。 ... [详细]
  • 深入理解Cookie与Session会话管理
    本文详细介绍了如何通过HTTP响应和请求处理浏览器的Cookie信息,以及如何创建、设置和管理Cookie。同时探讨了会话跟踪技术中的Session机制,解释其原理及应用场景。 ... [详细]
  • 本文介绍了一款用于自动化部署 Linux 服务的 Bash 脚本。该脚本不仅涵盖了基本的文件复制和目录创建,还处理了系统服务的配置和启动,确保在多种 Linux 发行版上都能顺利运行。 ... [详细]
  • 前言--页数多了以后需要指定到某一页(只做了功能,样式没有细调)html ... [详细]
  • DNN Community 和 Professional 版本的主要差异
    本文详细解析了 DotNetNuke (DNN) 的两种主要版本:Community 和 Professional。通过对比两者的功能和附加组件,帮助用户选择最适合其需求的版本。 ... [详细]
  • MySQL中枚举类型的所有可能值获取方法
    本文介绍了一种在MySQL数据库中查询枚举(ENUM)类型字段所有可能取值的方法,帮助开发者更好地理解和利用这一数据类型。 ... [详细]
  • 如何高效创建和使用字体图标
    在Web和移动开发中,为什么选择字体图标?主要原因是其卓越的性能,可以显著减少HTTP请求并优化页面加载速度。本文详细介绍了从设计到应用的字体图标制作流程,并提供了专业建议。 ... [详细]
  • 本文介绍如何在 Xcode 中使用快捷键和菜单命令对多行代码进行缩进,包括右缩进和左缩进的具体操作方法。 ... [详细]
  • 在Linux系统中配置并启动ActiveMQ
    本文详细介绍了如何在Linux环境中安装和配置ActiveMQ,包括端口开放及防火墙设置。通过本文,您可以掌握完整的ActiveMQ部署流程,确保其在网络环境中正常运行。 ... [详细]
  • 如何在WPS Office for Mac中调整Word文档的文字排列方向
    本文将详细介绍如何使用最新版WPS Office for Mac调整Word文档中的文字排列方向。通过这些步骤,用户可以轻松更改文本的水平或垂直排列方式,以满足不同的排版需求。 ... [详细]
author-avatar
手机用户2502872597
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有