CNGDiskSpaceMonitor - 免费磁盘空间监视类






3.73/5 (11投票s)
一个用于监视指定驱动器上的可用磁盘空间的类
概述
CNGDiskSpaceMonitor 是一个类,它监控驱动器的可用空间,直到被告知停止,或达到预定的报警阈值。
如果磁盘空间低于阈值,将向在启动监控进程时指定的窗口发送一个 Windows 消息(由客户端定义)。
监控进程由一个工作线程执行,该线程在一个 while
循环中定期轮询可用磁盘空间。当磁盘空间低于预定阈值或客户端信号停止监控时,线程退出。
工作线程
工作线程相当简单。当开始监控(通过 Start()
方法)时,它调用一个实现方法 [CreateMonitoringThread()
],该方法调用 AfxBeginThread()
来启动工作线程。
初始化后,工作线程进入一个无限 while()
循环:
// Loop until a the available space drops below the threshold or hEvent // (m_Event) becomes set indicating it's time for the thread to end. while (TRUE) { // Check if the event has been signaled // If so its time for this thread to die.... // // Note that the timeout is set to zero so the wait // will return IMMEDIATELY if (WAIT_OBJECT_0 == ::WaitForSingleObject(hEvent, 0)) { break; } if ( (NULL != m_hwndTarget) && !::IsWindow(m_hwndTarget) ) { // Stop monitoring if the target window dies break; } if (GetFreeDiskSpace(sPath, m_dwFreeSpace) ) { // Managed to read the disk space OK if (m_dwFreeSpace < m_dwThreshold) { // We've dropped below the alarm threshold // If the target window is still there, let the // client know and then kill the thread OnThresholdReached(sPath, m_dwFreeSpace, m_dwThreshold); break; } } else { // Kill this thread (m_Event became signaled) break; } if (m_dwPollInterval > 0) { // Sleep for a little while between polls... ::Sleep(m_dwPollInterval); } }
读取可用磁盘空间
在 Windows 机器上读取可用磁盘空间是一项本应更简单却实则不然的任务。问题在于首选方法 - GetDiskFreeSpaceEx()
函数 - 在所有 Win32 平台(它是在 Windows 95 OSR2 中引入的)上都不可用。
为了保持可移植性,该类使用显式链接 [通过 GetModuleHandle()
和 GetProcAddress()
] 来确定 GetDiskFreeSpaceEx()
是否可用。如果不可用,则使用 GetDiskFreeSpace()
;其后果是,大于 2GB 的磁盘(原始 Windows 95 版本中的 FAT16 文件系统也不支持)无法准确读取。
这(相当混乱的)细节隐藏在 GetFreeDiskSpace()
方法中:
BOOL CNGDiskSpaceMonitor::GetFreeDiskSpace(const CString& sPath, DWORDLONG& rdwFreeSpace) { BOOL bResult = FALSE; // Load the kernel to gain access to the functions we want // If this fails, something's REALLY wrong... HMODULE hKernel = ::GetModuleHandle( _T("Kernel32.dll") ); ASSERT(NULL != hKernel); if (NULL != hKernel) { #ifdef _UNICODE LPFNGETSPACEEX pfnGetDiskFreeSpaceEx = (LPFNGETSPACEEX)::GetProcAddress(hKernel, "GetDiskFreeSpaceExW" ); LPFNGETSPACE pfnGetDiskFreeSpace = (LPFNGETSPACE)::GetProcAddress(hKernel, "GetDiskFreeSpaceW" ); #else LPFNGETSPACEEX pfnGetDiskFreeSpaceEx = (LPFNGETSPACEEX)::GetProcAddress(hKernel, "GetDiskFreeSpaceExA" ); LPFNGETSPACE pfnGetDiskFreeSpace = (LPFNGETSPACE)::GetProcAddress(hKernel, "GetDiskFreeSpaceA" ); #endif ASSERT (NULL != pfnGetDiskFreeSpaceEx); if (NULL != pfnGetDiskFreeSpaceEx) { ULARGE_INTEGER nCallerFreeBytes; // Receives the number of bytes on // disk available to the caller ULARGE_INTEGER nDiskSize; // Receives the number of bytes on disk ULARGE_INTEGER nTotalFreeBytes; // Receives the total free bytes on // the disk bResult = pfnGetDiskFreeSpaceEx(sPath, &nCallerFreeBytes, &nDiskSize, &nTotalFreeBytes); if (bResult) { rdwFreeSpace = nCallerFreeBytes.QuadPart; } } // Failing that try the old fashioned way... if (!bResult) { DWORD dwSectorsPerCluster; DWORD dwBytesPerSector; DWORD dwFreeClusters; DWORD dwTotalClusters; bResult = pfnGetDiskFreeSpace(sPath, &dwSectorsPerCluster, &dwBytesPerSector, &dwFreeClusters, &dwTotalClusters); if (bResult) { rdwFreeSpace = dwFreeClusters * dwSectorsPerCluster * dwBytesPerSector; } } } return bResult; }
值得注意的是,目前推荐的执行此操作的方法 - SHGetDiskFreeSpace()
函数(它为您执行此繁琐工作) - 仅在 Shell32.dll 版本 4.71 或更高版本的系统上可用。因此,如果您运行的平台没有它(即第一版 Windows 95),您就回到了起点。
为了使代码尽可能广泛适用,我故意避免了这些依赖项,而是自己费力地完成了。
操作
Start()
方法开始监控指定的路径。除了要监控的路径外,还必须指定可用空间阈值,以及(可选)目标窗口和消息。如果需要,还可以指定监控线程的优先级(默认值为 THREAD_PRIORITY_LOWEST
)。
// Start monitoring a path // When the disk space drops below a defined threshold // a message is sent to the specified window // BOOL Start( const CString& sPath, // Path to monitor DWORDLONG dwThreshold, // Send notification when space // drops below this value CWnd* pWnd = NULL, // Target window UINT nMsg = 0, // Message to send UINT nID = 0, // ID if message is WM_COMMAND int ePriority = THREAD_PRIORITY_LOWEST); // Priority BOOL Start( const CString& sPath, // Path to monitor DWORDLONG dwThreshold, // Send notification when space // drops below this value HWND hWnd, // Target window UINT nMsg, // Message to send UINT nID = 0, // ID if message is WM_COMMAND int ePriority = THREAD_PRIORITY_LOWEST); // Priority
请注意,即使不指定通知窗口和消息,调用 Start()
也是完全可以的 - 但在这种情况下,必须重写 OnThresholdReached()
虚拟方法来实现阈值达到时所需的动作。
在开始监控后的任何时候,客户端都可以使用 Stop()
方法停止该过程。
BOOL Stop(void);
当工作线程运行时,可以通过以下方法读取或更改报警阈值
DWORDLONG GetThreshold(void) const BOOL SetThreshold(DWORDLONG dwThreshold);
以下方法确定轮询发生的间隔(默认值为每 100 毫秒)
DWORD GetPollInterval(void) const BOOL SetPollInterval(DWORD dwInterval);
提供了两个返回磁盘空间的方法。第一个方法不带参数,返回工作线程在其最后一次监控周期读取的可用空间。因此,此方法仅在工作线程运行时才有效。
第二个方法是工作线程本身使用的方法。它作为接口的一部分提供,以便客户端可以更轻松地读取任何驱动器的磁盘空间(并且可以随时使用)。
DWORDLONG GetFreeDiskSpace(void) const; static BOOL GetFreeDiskSpace( const CString& sPath, DWORDLONG& rdwFreeSpace);
最后,如果客户端忘记了正在监控哪个磁盘,或者不确定监控循环是否正在运行,可以使用 GetPath()
和 IsRunning()
方法来了解情况。
CString GetPath(void) const; BOOL IsRunning(void) const;
可重写函数
CNGDiskSpaceMonitor
只有一个虚拟方法
virtual BOOL OnThresholdReached( const CString& sPath, DWORDLONG dwFreeSpace, DWORDLONG dwThreshold);
默认实现使用 PostMessage()
调用将消息发布到在 Start()
方法中指定的窗口。
如果您需要以不同的方式向客户端发出信号(例如,当客户端不是窗口时),则可以在派生类中重写此方法来实现替代方案。
用法
使用该类非常简单
- 将
CNGDiskSpaceMonitor
对象添加到您的类之一中 - 调用
CNGDiskSpaceMonitor::Start()
的重载之一来启动监控线程 - 如果达到阈值,将向目标窗口发送一条消息。您可以通过重写
CNGDiskSpaceMonitor::OnThresholdReached()
虚拟方法来更改此行为。
演示应用程序(DiskSpaceMonitorTest)展示了如何使用 CNGDiskSpaceMonitor
类来监控指定驱动器的可用空间。同时提供了 ANSI 和 Unicode 构建;代码在警告级别 4 下干净编译。
更新
版本 1.1(2001 年 2 月 1 日)- 初次提交
版本 1.2(2001 年 2 月 5 日)- 修复了 Unicode 兼容性问题;还简化了 GetFreeDiskSpace()
的实现,如 Wes Jones 所建议。