65.9K
CodeProject 正在变化。 阅读更多。
Home

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

starIconstarIconstarIcon
emptyStarIcon
starIcon
emptyStarIcon

3.88/5 (12投票s)

2008年10月19日

CPOL

6分钟阅读

viewsIcon

44723

downloadIcon

2393

它将可移动设备上的文件创建、删除和重命名记录到文件中。

snapshotusbmonitor.jpg

引言

此应用程序维护可移动驱动器上文件活动(例如文件重命名、删除和创建)的日志。每次活动的详细信息以及发生的日期和时间都会被记录下来。用户需要指定存储日志文件的路径(txt),然后单击“开始日志记录”。应用程序在启动时会查找所有已连接的可移动设备,并会处理所有新连接的设备。它会自动检索所有已连接或新连接驱动器的盘符,并激活相应的 FileSystemWatcher 对象,我们将在后续部分中进行解释。

背景

USB 已成为传输信息无处不在的手段。然而,它可能导致宝贵数据、文件、手册等的意外传输。有时,监视可移动驱动器上的文件活动变得很有必要,尤其是在拥有敏感数据的组织中。关于这个问题,我们在互联网上没有找到很多相关的文献或应用程序。因此,我们决定开发一个我们自己的小型应用程序,允许用户执行此任务。该应用程序适用于只想监视可移动驱动器上文件活动的普通用户,也适用于程序员和开发人员,他们在阅读完本文后将能够根据自己的需求修改程序(例如,使窗口不可见,在 Windows 启动时运行,远程监视等)。

应用程序详情 

在以下段落中,我们将描述应用程序的主要功能。

System.IO.FileSystemWatcher 类

根据 MSDN,FileSystemWatcher侦听文件系统更改通知,并在目录或目录中的文件更改时引发事件。” 更多信息可以在这里找到。我们将以最简单的术语简要解释 FileSystemWatcher 类的主要功能。

Path - 将在其上监视活动的路径。默认情况下,包含子目录,但如果您想更改它,可以将 IncludeSubdirectories 属性设置为 false。

NotifyFilter - 这指定要监视的更改类型。对于此应用程序,我们感兴趣的筛选器是 LastAccess LastWrite FileNameDirectoryName。程序员可以添加或删除任何可用的筛选器。

EventHandlers - 每次触发与文件活动相关的事件时,都需要执行一些操作。这些操作包含在单独的函数中,其引用需要注册到相应的属性。这些属性是 Changed Created RenamedDelete。这些引用需要以称为委托的特殊签名形式保存。阅读此文章以获取有关事件和委托的更多信息。但是,如果您对委托不完全熟悉,只需知道您需要以特殊格式(通常是发送者和函数本身)将函数与事件关联起来即可。当我们查看下一节中的实现时,它会更清楚。

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 对象的 PathEnableRaisingEvents 属性完成的。

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 
onCreatedonDeletedonRenamed 是分别包含在文件创建、删除和重命名时执行的实现的函数。在我们的例子中,实现包括将信息写入日志文件。

beginWatcherOnDrive() 和 stopWatcherOnDrive() 

这些函数将要启动或停止 FileSystemWatcher 的驱动器盘符作为参数。一旦设备连接到系统,重写的 Windows 过程函数(稍后描述)会在检测到驱动器盘符并检查设备是否可移动后调用 beginWatcherOnDrive。同样,一旦驱动器被移除,stopWatcherOnDrive 就会停止 FileSystemWatcher。同样,这些都是通过 PathEnableRaisingEvents 属性完成的。
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 类的主要函数。
  1. CheckExistingRemovableDrives() - 这会返回一个包含 26 个布尔值的数组,其中可移动驱动器存在的索引为 true,其余为 false。

  2. EvaluateMask(DWORD Mask) - 这会在评估从稍后描述的 WndProc 函数收到的掩码后,返回连接或断开可移动设备的驱动器盘符。

  3. 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。另外两个是 WParamLParam,它们分别包含有关设备更改类型和驱动器详细信息的信息。要从 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

© . All rights reserved.