监控可移动设备上的文件活动






3.88/5 (12投票s)
它将可移动设备上的文件创建、删除和重命名记录到文件中。
引言
此应用程序维护可移动驱动器上文件活动(例如文件重命名、删除和创建)的日志。每次活动的详细信息以及发生的日期和时间都会被记录下来。用户需要指定存储日志文件的路径(txt),然后单击“开始日志记录”。应用程序在启动时会查找所有已连接的可移动设备,并会处理所有新连接的设备。它会自动检索所有已连接或新连接驱动器的盘符,并激活相应的 FileSystemWatcher 对象,我们将在后续部分中进行解释。
背景
USB 已成为传输信息无处不在的手段。然而,它可能导致宝贵数据、文件、手册等的意外传输。有时,监视可移动驱动器上的文件活动变得很有必要,尤其是在拥有敏感数据的组织中。关于这个问题,我们在互联网上没有找到很多相关的文献或应用程序。因此,我们决定开发一个我们自己的小型应用程序,允许用户执行此任务。该应用程序适用于只想监视可移动驱动器上文件活动的普通用户,也适用于程序员和开发人员,他们在阅读完本文后将能够根据自己的需求修改程序(例如,使窗口不可见,在 Windows 启动时运行,远程监视等)。
应用程序详情
在以下段落中,我们将描述应用程序的主要功能。
System.IO.FileSystemWatcher 类
根据 MSDN,FileSystemWatcher
“侦听文件系统更改通知,并在目录或目录中的文件更改时引发事件。” 更多信息可以在这里找到。我们将以最简单的术语简要解释 FileSystemWatcher 类的主要功能。
Path - 将在其上监视活动的路径。默认情况下,包含子目录,但如果您想更改它,可以将 IncludeSubdirectories
属性设置为 false。
NotifyFilter - 这指定要监视的更改类型。对于此应用程序,我们感兴趣的筛选器是 LastAccess
、 LastWrite
、 FileName
和 DirectoryName
。程序员可以添加或删除任何可用的筛选器。
EventHandlers - 每次触发与文件活动相关的事件时,都需要执行一些操作。这些操作包含在单独的函数中,其引用需要注册到相应的属性。这些属性是 Changed
、 Created
、 Renamed
和 Delete
。这些引用需要以称为委托的特殊签名形式保存。阅读此文章以获取有关事件和委托的更多信息。但是,如果您对委托不完全熟悉,只需知道您需要以特殊格式(通常是发送者和函数本身)将函数与事件关联起来即可。当我们查看下一节中的实现时,它会更清楚。
Filter - 指定要监视的文件类型。默认情况下,它监视所有类型的文件。
EnableRaisingEvents - 此属性在为 true 时启动 FileSystemWatcher
活动。
watcherArray 类
要查看此类的完整实现,请下载源代码。它基本上是一个包含 26 个 FileSystemWatcher
对象的数组的类,每个驱动器对应一个(0 对应 A,1 对应 B,…,25 对应 Z)。在构造函数中,会调用 initializeArray()
函数。
initializeArray()
首先,我们检查现有的可移动驱动器,并将值存储在名为drives
的布尔数组中。(0 对应 A,1 对应 B,…,25 对应 Z)。bool * drives = DrivesFunctions::CheckExistingRemovableDrives();
DrivesFunctions
类在下一节中会非常简要地解释。
接下来,它根据 drives
数组的不同值初始化数组中的所有对象。这是通过数组中每个 FileSystemWatcher
对象的 Path
和 EnableRaisingEvents
属性完成的。
for(int i=0; i<26; i++) { watcherArray[i] = gcnew FileSystemWatcher(); if(drives[i]) { watcherArray[i]->Path = StringManipulations::AsciiToChar(i+65); watcherArray[i]->EnableRaisingEvents = true; } else { watcherArray[i]->Path = ""; watcherArray[i]->EnableRaisingEvents = false; } //for loop continues...
然后设置 notifyFilters
属性。这对于数组中的所有对象都是相同的。
watcherArray[i]->NotifyFilter = static_cast最后,为每种事件类型注册函数。出于我们的目的,我们没有包含(NotifyFilters::LastAccess |NotifyFilters::LastWrite | NotifyFilters::FileName | NotifyFilters::DirectoryName);
Changed
属性,因为它会引入冗余。但是,如果任何程序员希望,可以通过以下类似方法将其激活。watcherArray[i]->Created += gcnew FileSystemEventHandler(this,&WatcherArray::onCreated); watcherArray[i]->Deleted += gcnew FileSystemEventHandler(this,&WatcherArray::onDeleted); watcherArray[i]->Renamed += gcnew RenamedEventHandler(this,&WatcherArray::onRenamed); }//for loop ends
onCreated
、onDeleted
和 onRenamed
是分别包含在文件创建、删除和重命名时执行的实现的函数。在我们的例子中,实现包括将信息写入日志文件。
beginWatcherOnDrive() 和 stopWatcherOnDrive()
这些函数将要启动或停止FileSystemWatcher
的驱动器盘符作为参数。一旦设备连接到系统,重写的 Windows 过程函数(稍后描述)会在检测到驱动器盘符并检查设备是否可移动后调用 beginWatcherOnDrive
。同样,一旦驱动器被移除,stopWatcherOnDrive
就会停止 FileSystemWatcher
。同样,这些都是通过 Path
和 EnableRaisingEvents
属性完成的。void beginWatcherOnDrive(char driveLetter) { int index = driveLetter-65; if( index >=0 && index<26) { if(watcherArray[index]->Path == "") watcherArray[index]->Path = StringManipulations::AsciiToChar(int(driveLetter)) + ":\\"; watcherArray[index]->EnableRaisingEvents = true; } }
void stopWatcherOnDrive(char driveLetter) { int index = driveLetter-65; if( index>=0 && index<26) { watcherArray[index]->EnableRaisingEvents = false; } }
DrivesFunctions
解释这个带有静态函数的类需要另一篇文章。如果您有兴趣了解它们的实现方式,请阅读 GetLogicalDrives()、GetVolumeInformation() 和 GetDriveType()。我们将简要讨论我们的DrivesFunctions
类的主要函数。- CheckExistingRemovableDrives() - 这会返回一个包含 26 个布尔值的数组,其中可移动驱动器存在的索引为 true,其余为 false。
- EvaluateMask(DWORD Mask) - 这会在评估从稍后描述的
WndProc
函数收到的掩码后,返回连接或断开可移动设备的驱动器盘符。 - DetermineDriveType(char Drive) - 在此函数中,我们只是通过驱动器盘符返回设备类型(可移动、固定等)。但是,可以从中提取更多信息(请参阅上面的文章)。
WndProc 方法
诸如连接 USB 等硬件更改通过操作系统通过一个名为WndProc
的函数传达给所有顶级窗口。有关更改的信息(例如,在我们的例子中是设备到来或移除、驱动器盘符等)通过 Message
对象发送到此函数。为了读取此消息中的信息,我们需要重写 WndProc
方法。这在 Form
的函数声明区域中完成。//Overriding Windows Procedure virtual void WndProc(System::Windows::Forms::Message %m) override { switch (m.Msg){ case WM_DEVICECHANGE: Main_OnDeviceChange(m.WParam,m.LParam); } Form::WndProc(m); }
Message
类有几个属性,其中三个是我们感兴趣的。第一个是 Msg
,它是一个整数,告诉我们收到的消息类型。在这种情况下,它是 WM_DEVICECHANGE
。另外两个是 WParam
和 LParam
,它们分别包含有关设备更改类型和驱动器详细信息的信息。要从 LParam
变量中提取有关驱动器的有用信息,我们必须执行两步类型转换。首先,我们需要获取指向 DEV_BROADCAST_HDR
结构的指针,通过它我们确定通过 WM_DEVICECHANGE
事件收到的结构。
PDEV_BROADCAST_HDR lpdb = (PDEV_BROADCAST_HDR)lparam.ToPointer();
这会进一步类型转换为指向 DEV_BROADCAST_VOLUME
结构的指针,该结构包含卷信息的单元掩码。
PDEV_BROADCAST_VOLUME lpdbv = (PDEV_BROADCAST_VOLUME)lpdb;然后,我们可以调用上一节中提到的
DeviceFunctions
类中的函数,以了解有关驱动器的详细信息并启用或停止相应的 FileSystemWatcher
对象。char driveType[15]="", driveName; switch(wparam.ToInt32()) { case DBT_DEVICEARRIVAL: driveName = DrivesFunctions::EvaluateMask(lpdbv->dbcv_unitmask); strcpy(driveType,""); strcpy(driveType, DrivesFunctions::DetermineDriveType(driveName)); if( strcmp(driveType,"Removable") == 0 ) if(usbWatcher) usbWatcher->beginWatcherOnDrive(driveName); break; case DBT_DEVICEREMOVECOMPLETE: driveName = DrivesFunctions::EvaluateMask(lpdbv->dbcv_unitmask); //Drive letter determined, remove FileSystemWatcher if(usbWatcher) usbWatcher->stopWatcherOnDrive(driveName); break; default: break; }
建议和意见
请将您的评论和建议发送至 abdulazizrehan@gmail.com