Download
demo project - 34 Kb
Download
source - 6 Kb
Overview
CNGDiskSpaceMonitor is a class which monitors the free space on a drive until
either told to stop, or a predetermined alarm threshold is reached.
If the disk space falls below the threshold, a Windows message (defined by
the client) is sent to a window specified when the monitoring process was started.
The monitoring process is carried out by a worker thread, which polls the free
disk space periodically in a while
loop. The thread exits when
either the disk space falls below the predetermined threshold, or the client
signals it to stop monitoring.
The Worker Thread
The worker thread is fairly simple. When monitoring is started (via the Start()
method), it calls an implementation method [CreateMonitoringThread()
]
which calls AfxBeginThread()
to start the worker thread.
Once initialised, the worker thread enters an infinite while()
loop:-
while (TRUE)
{
if (WAIT_OBJECT_0 == ::WaitForSingleObject(hEvent, 0))
{
break;
}
if ( (NULL != m_hwndTarget) && !::IsWindow(m_hwndTarget) )
{
break;
}
if (GetFreeDiskSpace(sPath, m_dwFreeSpace) )
{
if (m_dwFreeSpace < m_dwThreshold)
{
OnThresholdReached(sPath,
m_dwFreeSpace,
m_dwThreshold);
break;
}
}
else
{
break;
}
if (m_dwPollInterval > 0)
{
::Sleep(m_dwPollInterval);
}
}
Reading Free Disk Space
Reading the free disk space on a Windows machine is one of those tasks which
ought to be easier than it is. The problem is that the preferred method - the
GetDiskFreeSpaceEx()
function - is not available on all Win32 platforms (it was introduced in Windows 95 OSR2).
In order to remain portable, the class uses explicit linking [via GetModuleHandle()
and GetProcAddress()
] to determine whether GetDiskFreeSpaceEx()
is available. If not, GetDiskFreeSpace()
is used instead; the consequence
of this being that disks over 2GB in size (which aren't supported by FAT16 file
system of the original Windows 95 release anyway) can't be read accurately.
The (rather messy) details of this are buried in the GetFreeDiskSpace()
method:-
BOOL CNGDiskSpaceMonitor::GetFreeDiskSpace(const CString& sPath,
DWORDLONG& rdwFreeSpace)
{
BOOL bResult = FALSE;
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;
ULARGE_INTEGER nDiskSize;
ULARGE_INTEGER nTotalFreeBytes;
bResult = pfnGetDiskFreeSpaceEx(sPath,
&nCallerFreeBytes,
&nDiskSize,
&nTotalFreeBytes);
if (bResult)
{
rdwFreeSpace = nCallerFreeBytes.QuadPart;
}
}
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;
}
It's worthy of note here that the latest recommended way of doing this - the
SHGetDiskFreeSpace()
function (which performs this voodoo for you)
- is only available on systems with version 4.71 or later of Shell32.dll. Hence,
if you're running on a platform without it (i.e. first edition Windows 95),
you're back where you started.
To make the code as widely applicable as possible, I've deliberately avoided
such dependencies, and done it the hard way myself.
Operations
The Start()
method starts monitoring a specified path.
Along with the path to monitor, the free space threshold must be specified,
and (optionally) a target window and message. If required, the priority of the
monitoring thread may also be specified (the default is THREAD_PRIORITY_LOWEST
):
BOOL Start( const CString& sPath,
DWORDLONG dwThreshold,
CWnd* pWnd = NULL,
UINT nMsg = 0,
UINT nID = 0,
int ePriority = THREAD_PRIORITY_LOWEST);
BOOL Start( const CString& sPath,
DWORDLONG dwThreshold,
HWND hWnd,
UINT nMsg,
UINT nID = 0,
int ePriority = THREAD_PRIORITY_LOWEST);
Note that it is perfectly OK to call Start()
without specifying
a notification window and message - however in this case the OnThresholdReached()
virtual method must be overridden to implement the action needed when the threshold is reached.
At any time after monitoring is started, the client can stop the process using
the Stop()
method:
BOOL Stop(void);
While the worker thread is running, the alarm threshold may be read or changed
by the following methods:
DWORDLONG GetThreshold(void) const
BOOL SetThreshold(DWORDLONG dwThreshold);
The following methods determine the interval at which polling takes place (the
default is every 100 milliseconds):
DWORD GetPollInterval(void) const
BOOL SetPollInterval(DWORD dwInterval);
Two methods to return disk space are provided. The first, which takes no parameters, returns the free space read by the worker thread on its last monitor cycle. In consequence, this method is valid only whilst the worker thread is running.
The second method is the one used by the worker thread itself. It is provided
as part of the interface to make it easier for clients to read the disk space
for any drive (and can be used at any time):
DWORDLONG GetFreeDiskSpace(void) const;
static BOOL GetFreeDiskSpace( const CString& sPath,
DWORDLONG& rdwFreeSpace);
Finally, if the client forgets which disk is being monitored, or isn't sure
whether the monitor loop is running, the GetPath()
and IsRunning()
methods can be used to find out:
CString GetPath(void) const;
BOOL IsRunning(void) const;
Overridables
CNGDiskSpaceMonitor
has just one virtual method:
virtual BOOL OnThresholdReached( const CString& sPath,
DWORDLONG dwFreeSpace,
DWORDLONG dwThreshold);
The default implementation uses a PostMessage()
call to post a
message to the window specified in the Start()
method.
If you need to signal the client in a different way (as will be the case if
the client is not a window), this method can be overridden in a derived class
to implement an alternative scheme.
Usage
Using the class is straightforward:
- Add a
CNGDiskSpaceMonitor
object to one of your classes
- Call one of the overloads of
CNGDiskSpaceMonitor::Start()
to
start the monitoring thread
- If the threshold is reached, a message will be sent to a target window.
You can change this behaviour by overriding the
CNGDiskSpaceMonitor::OnThresholdReached()
virtual method.
The demo app (DiskSpaceMonitorTest) shows how to use the CNGDiskSpaceMonitor
class to monitor the free space on a specified drive. Both ANSI and Unicode builds are provided; the code compiles cleanly at warning level 4.
Updates
Version 1.1 (1st February, 2001) - Initial submission
Version 1.2 (5th February, 2001) - Bug fixes for Unicode compatibility; also
simplified the implementation of GetFreeDiskSpace()
as suggested
by Wes Jones.