【大话QT之五】Windows与Linux下文件操作监控的实现

一、需求分析:

随着渲染业务的不断进行,数据传输渐渐成为影响业务时间最大的因素。究其原因就是因为数据传输耗费较长的时间。于是,依托于渲染业务的网盘开发逐渐成为迫切需要解决的需求。该网盘的实现和当前市场上网盘实现有一些的不同,主要在客户端与服务器端的操作需要双向进行,即:用户在客户端的操作需要及时同步到服务器端;在服务器端作业渲染生成的文件要及时同步到客户端。即:用户不在需要单独的下载数据,而是在作业运行的同时,渲染就过就会自动同步到客户端,大大缩短了等待时间。当然,无论是在客户端还是在服务端都面临着一个问题,即:实现对文件操作的监控,这里的文件操作包括:文件(夹)创建、文件(夹)删除、文件(夹)重命名、文件(夹)移动等操作。除此之外还要能够同步客户端文件的修改操作,即:当用户退出网盘后,修改了原有同步目录中的文件,当用户再次启动网盘时通过一次扫描与md5值的比较能缺确定出哪些文件发生了改动,并将改动后的操作及时同步到服务端。这里,先将Windows(客户端实现需要)与Linux(服务端实现需要)下文件监控的实现方法简要概述。

二、文件监控实现方法分析

1> Windows下文件监控的实现

Windows下实现文件监控的原理是利用SHChangeNotifyRegister把指定的窗口添加到系统的消息监视链中,从而注册窗口就可以接收到来自文件系统或Shell的通知了。在继续向下说明之前,需要解释一下Windows外壳名字空间(Shell Name Space)的概念。

外壳名字空间是Windows下的标准文件系统,它大大扩展了Dos文件系统,形成了以”桌面“为根的单一文件系统树,原有的C、D盘等目录树变成了“我的电脑”这一外壳名字控件子树的下一级子树,而像“控制面板”、“回收站”、“网上邻居”等应用程序以及“打印机”等设备也被虚拟成了外壳名字空间中的节点。为了区别于Dos中“目录”的概念,Windows引入了“文件夹”的概念。“文件夹”一般是指外壳名字空间树中的非叶几点,既可以是DOS下的目录,也可以是“控制面板”、“回收站”这类虚拟的目录。

新的“路径”PIDL:外壳对象标识符列表。PIDL是一个元素类型为ITEMIDLIST结构的数组,数组中元素的个数是未知的,但紧接着数组的末尾的必是一个双字节的零。每个数组元素代表了外壳名字空间树中的一层(即一个文件夹或文件),数组中的前一元素代表的是后一元素的父文件夹。由此可见,PIDL实际上就是指向一块由若干个顺序排列的ITEMIDLIST结构组成、并在最后有一个双字节零的空间的指针。所以PIDL的类型就被Windows定义为ITEMIDLIST结构的指针。

DOS中的路径是一个字符串,但PIDL是一种二进制结构,所以我们不能直接从PIDL中获知它所代表的到底是哪个文件夹或文件,而必须调用相应的函数把它转换为代表路径的字符串。如果某绝对PIDL是文件系统的一部分,则调用SHGetPathFromIDList函数即可;但如不是,就无法获得路径字符串了,因为DOS中根本就不存在这种路径。

总体来说:实现Windows下文件监控的基本流程如下:

上图是在windows下利用C++实现文件监控的一种方式(根据需要灵活改动),这里简要叙述一下:1) 利用DialogBoxParam创建一个模态对话框,进入窗口过程函数,在该窗口函数中根据各种消息来完成我们的操作。这里模态对话框与非模态对话框的区别之一是因为它有一套自己的消息泵机制,不需要我们再手动写消息的接收了(非模态对话框要自己接收消息)。拦截用户消息,根据各个不同的阶段可以加入我们自己的操作,比如初始化等。2) 获取指定路径的PIDL:即目标路径的外壳对象标识符,有了它才能继续后续的处理,这里的获取有两种方式,一种是利用IFileDialog打开对话框让用户选择(IFileDialog
*pfd),从而通过GetResult(IShellItem *psi ; pfs->GetResult(&psi))获取IShellItem对象;然后利用QueryInterface(IShellItem2 *_psiDrop ; psi->QueryInterface(&_psiDrop))获取IShellItem2对象;最后利用它就可以获得PIDL了(利用SHGetIDListFromObject)。3) 最后利用SHChangeNotifyRegister完成最终目标窗体的挂载,从而将一个目录加入到系统的消息链中,从而可以获取文件系统或Shell中关于文件操作的相关信息。最后将信息解析出来就可以了。

还有一种方式即直接提供目标文件夹的绝对路径,由该路径获取到PIDL,从而将窗体挂载到系统消息链中,注意:如果是在QT中实现的话,可以很轻松的获取到QWidget的窗口句柄。关键代码如下:

void houqd::RegisterWindow()
{
	char absoluteFolderPath[] = "C:\\openssl";

	//! 由文件夹的绝对路径获取PIDL:外壳对象标识符列表,即在windows 外壳名字空间 "Shell Name Space"中的表示方法。
	LPITEMIDLIST myFolderPIDL = ParsePidlFromPath(absoluteFolderPath);

	HRESULT res ;
	IShellItem *psi = NULL;

	//! 创建一个IShellItem(interface)对象,IShellItem interface提供了查找一个关于Shell Item相关信息的方法。
	//! IShell Item接口都继承自IUnknown interface
	res = SHCreateShellItem(NULL, NULL, myFolderPIDL, &psi);

	IShellItem2 *ppsi ;

	//! 检索一个对象上支持的接口的指针
	psi->QueryInterface(&ppsi);

	//! =======================================注册文件监控=============================================
	PIDLIST_ABSOLUTE pidlWatch;
	HRESULT hr = SHGetIDListFromObject(ppsi, &pidlWatch);

	if(SUCCEEDED(hr))
	{
		SHChangeNotifyEntry const entries[] = { pidlWatch, true };

		int const nSources = SHCNRF_ShellLevel | SHCNRF_InterruptLevel | SHCNRF_NewDelivery;

		//! 注册窗口主函数
		_ulRegister = SHChangeNotifyRegister(_hdlg, nSources, SHCNE_ALLEVENTS, c_notifyMessage, ARRAYSIZE(entries), entries);
		hr = _ulRegister != 0 ? S_OK : E_FAIL;
	}

	//ShowWindow(SW_HIDE);
	//! ====================================================================================
}

ParsePidFromPath 的具体实现如下:

LPITEMIDLIST houqd::ParsePidlFromPath(LPCSTR lpszPath)
{
	//存放以Unicode内码表示的路径字符串的缓冲区
	 OLECHAR szOleChar[MAX_PATH];

	 //“桌面“的IshellFolder接口指针
	 LPSHELLFOLDER lpsfDeskTop;

	 //返回的PIDL
	 LPITEMIDLIST lpifq;

	 ULONG ulEaten, ulAttribs;
	 HRESULT hres;

	 //得到“桌面”的IshellFolderr 接口指针
	 SHGetDesktopFolder(&lpsfDeskTop);

	 //将Ansi字符集的路径字符串转换成Unicode字符串,存入szOleChar
	 MultiByteToWideChar(CP_ACP,MB_PRECOMPOSED, lpszPath, -1, szOleChar, sizeof(szOleChar));

	 //将szOleChar,中的路径径字符串翻译成相应的PIDL,存入lpifq
	 hres = lpsfDeskTop->ParseDisplayName(NULL, NULL, szOleChar, &ulEaten, &lpifq, &ulAttribs);

	 hres = lpsfDeskTop->Release();

	 //如果翻译失败,则返回NULL

	 if(FAILED(hres))
	  return NULL;

	 return lpifq;
}

2> Linux下文件监控的实现

Linux下主要是通过inotify实现文件监控。它是一个内核用户通知用户空间程序文件系统变化的机制。在用户状,inotify通过三个系统调用和在返回的文件描述符上的文件I/O操作来使用.

1) 使用inotify的第一步是创建inotify的实例:int fd = inotify_init() ; 每一个inotify实例对应一个独立的排序的队列。

2) int wd = inotify_add_watch(fd , file_dir_path , mask);添加一个目录的监控。

3) 删除一个监控:inotify_rm_watch(fd , wd);

关键代码如下:

if (m_InotifyFd != LHFSMC_FD_UNCREATED_STATE)
        close(m_InotifyFd);

    //! inotify_init()
    if ((m_InotifyFd = inotify_init()) < 0)
    {
        qDebug() << "[error] LHFileSystemMonitor::Start: inotify_init failure.";
        return 0;
    }

    //! 为m_CreatedDirList中所有保存的目录创建监控
    if(!CreateWatcherForEachDir(m_CreatedDirList))
    {
        qDebug() << "[error] LHFileSystemMonitor::Start: CreateNotifierForEachDir failure.";
        return 0;
    }

CreateWatcherForEachDir的实现:

int LHFileSystemMonitor::CreateWatcherForEachDir(QStringList &dirLocationList)
{
#ifndef WIN32
    for (QStringList::const_iterator iter = dirLocationList.begin();
         iter != dirLocationList.end();
         ++iter)
    {
        int watchDescriptor;

        if ((watchDescriptor = CreateWatcher(iter->toStdString().c_str())) > 0)
            m_MonitoredObjectList.push_back(LHFSMonitorData(*iter, watchDescriptor));
        else
            qDebug() << "[error] LHFileSystemMonitor::CreateWatcherForEachDir: CreateWatcher for"
                     << *iter << "failure";
    }
#endif
    return 1;
}

CreateWatcher的实现:

int LHFileSystemMonitor::CreateWatcher(const char *fileLocation) const
{
    if (fileLocation == NULL)
        return 0;

    QDir dir(fileLocation);

    if (!dir.exists())
    {
        qDebug() << "[error] LHFileSystemMonitor::CreateNotifier:" << fileLocation << "is not exist!";
        return 0;
    }

    return ((m_InotifyFd != LHFSMC_FD_UNCREATED_STATE) ? inotify_add_watch(m_InotifyFd, fileLocation, LHFSMC_MONITOR_EVENT) : -1);
}

总结:

以上就是Windows以及Linux下文件监控系统实现的相关思路及代码,仅仅作为一个引入,利用这种方式均可以实现对应的功能。

时间: 2024-12-28 01:26:33

【大话QT之五】Windows与Linux下文件操作监控的实现的相关文章

Windows与Linux下文件操作监控的实现

一.需求分析: 随着渲染业务的不断进行,数据传输渐渐成为影响业务时间最大的因素.究其原因就是因为数据传输耗费较长的时间.于是,依托于渲染业务的网盘开发逐渐成为迫切需要解决的需求.该网盘的实现和当前市场上网盘实现有一些的不同,主要在客户端与服务器端的操作需要双向进行,即:用户在客户端的操作需要及时同步到服务器端:在服务器端作业渲染生成的文件要及时同步到客户端.即:用户不在需要单独的下载数据,而是在作业运行的同时,渲染就过就会自动同步到客户端,大大缩短了等待时间.当然,无论是在客户端还是在服务端都面

Windows、Linux下文件操作(写、删除)错误的产生原因、及解决方法

catalog 0. 引言 1. Linux平台上涉及的File IO操作 2. Windows平台上涉及的File IO操作 0. 引言 本文试图讨论在windows.linux操作系统上基于C库进行文件IO操作时,可能遇到的错误,及其解决方法,主机安全攻防产品除了需要将安全攻防上的领域知识固化到程序实现上之外,还极度依赖关联系统本身.编程语言库的特性,原则上,并不是所有的安全需求都能100%地落实到程序设计中,这需要我们对操作系统.编程语言本身具有较深的理解 Relevant Link: h

qt ui程序使用Linux的文件操作open、close (转)

原文地址:qt ui程序使用Linux的文件操作open.close 作者:kjpioo 提出这个问题是因为在qt的QWidget类型的对象中,close()函数会和QWidget::close()冲突,如果在类函数实现时直接用close(),在程序编译时会提示提示错误(具体什么错误不记得了). 错误原因是QWidget::close()与stdio.h中的文件关闭标准函数close()产生了歧义.所以可以参考下文链接中的解决方案. http://bytes.com/topic/c/answer

Linux下 文件操作(base)

1.新建文件夹 mkdir bigdata:在当前文件夹下新建bigdata文件夹: 2.显示当前文件夹全目录 pwd: 3.移动文件:mv /usr/etc/spark-2.3.1-bin-hadoop2.7.tgz /usr/etc/bigdata/ 4 .解压到当前文件夹  tar zxvf spark-2.3.1-bin-hadoop2.7.tgz -C ./ 原文地址:https://www.cnblogs.com/hoge66/p/9429707.html

Linux下文件操作

四:文件.  1:有7种文件类型:    1.1:    -  : 常规文件    1.2:    d  :  目录文件    1.3:    l  :  链接文件(soft)(类似windows中的快捷方式)    1.4:    c  :  字符设备文件(对应一个外围设备)    1.5:    b  : (块设备文件)(网络设备(不对应到文件))    1.6:    p  :  (pipe) 管道文件 (一般见不到)    1.7:    s  :  (socket)套接字文件    注

如何在windows下和linux下获取文件(如exe文件)的详细信息和属性

程序员都很懒,你懂的! 最近在项目开发中,由cs开发的exe的程序,需要自动升级,该exe程序放在linux下,自动升级时检测不到该exe程序的版本号信息,但是我们客户端的exe程序需要获取服务器上新程序的版本号信息.最后由我用java实现linux上exe文件的版本号读取功能.下面是详细代码: package com.herman.utils; import java.io.File; import java.io.FileNotFoundException; import java.io.I

打通windows和Linux下的传输问题解决只能使用SSH协议下的22端口来传输文件(Openssh for windows)

目的:打通windows和Linux下的传输问题解决只能使用SSH协议下的22端口来传输文件 环境: windows IP192.168.1.120 Linux IP192.168.101 方法: 安装opensshfor windows (次软件开源并增加ssh协议将windows模拟成Unix环境) 安装完软件导入本地用户或者域用户 进入到bin目录下面执行 cd "c:\Program Files(x86)\OpenSSH\bin" mkgroup -l >>..\e

windows和linux下读取文件乱码的终极解决办法!

乱码是个很恶心的问题. windows和linux读取txt文件,一旦读取了,编码发生改变,就无法再还原了,只有重启项目. 网上有很多方法都是读取文件头,方法很好,但是亲测都不能用(右移8位判断0xff的,取3个字节的-1,-2的,大体上网上最多就这两种). 后来偶然发现一个方法,CharsetPrinter. 这个方法需要引入jar包,非常好用. public static String guessEncoding(String filename) { try { CharsetPrinter

socket在windows下和linux下的区别

windows到Linux代码移植遇到的问题 1.一些常用函数的移植 http://www.vckbase.com/document/viewdoc/?id=1586 2.网络 ------ 转载 & 修改(待整理) socket相关程序从windows移植到linux下需要注意的 1)头文件 windows下winsock.h/winsock2.h linux下sys/socket.h 错误处理:errno.h 2)初始化 windows下需要用WSAStartup linux下不需要 3)关