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

CNGDiskSpaceMonitor - 免费磁盘空间监视类

starIconstarIconstarIcon
emptyStarIcon
starIcon
emptyStarIcon

3.73/5 (11投票s)

2001年2月1日

CPOL
viewsIcon

136072

downloadIcon

2106

一个用于监视指定驱动器上的可用磁盘空间的类

  • 下载演示项目 - 34 Kb
  • 下载源代码 - 6 Kb
  • 概述

    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() 方法中指定的窗口。

    如果您需要以不同的方式向客户端发出信号(例如,当客户端不是窗口时),则可以在派生类中重写此方法来实现替代方案。

    用法

    使用该类非常简单

    1. CNGDiskSpaceMonitor 对象添加到您的类之一中
    2. 调用 CNGDiskSpaceMonitor::Start() 的重载之一来启动监控线程
    3. 如果达到阈值,将向目标窗口发送一条消息。您可以通过重写 CNGDiskSpaceMonitor::OnThresholdReached() 虚拟方法来更改此行为。

    演示应用程序(DiskSpaceMonitorTest)展示了如何使用 CNGDiskSpaceMonitor 类来监控指定驱动器的可用空间。同时提供了 ANSI 和 Unicode 构建;代码在警告级别 4 下干净编译。

    更新

    版本 1.1(2001 年 2 月 1 日)- 初次提交

    版本 1.2(2001 年 2 月 5 日)- 修复了 Unicode 兼容性问题;还简化了 GetFreeDiskSpace() 的实现,如 Wes Jones 所建议。

    © . All rights reserved.