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.
I haven't always written software for a living. When I graduated from Surrey University in 1989, it was with an Electronic Engineering degree, but unfortunately that never really gave me the opportunity to do anything particularly interesting (with the possible exception of designing
Darth Vader's Codpiece * for the UK Army in 1990).
* Also known as the Standard Army Bootswitch. But that's another story...
Since the opportunity arose to lead a software team developing C++ software for
Avionic Test Systems in 1996, I've not looked back. More recently I've been involved in the development of subsea acoustic navigation systems, digital TV broadcast systems, port security/tracking systems, and most recently software development tools with my own company,
Riverblade Ltd.
One of my personal specialities is IDE plug-in development.
ResOrg was my first attempt at a plug-in, but my day to day work is with
Visual Lint, an interactive code analysis tool environment with works within the Visual Studio and Eclipse IDEs or on build servers.
I love lots of things, but particularly music, photography and anything connected with history or engineering. I
despise ignorant, intolerant and obstructive people - and it shows...I can be a bolshy cow if you wind me up the wrong way...
I'm currently based 15 minutes walk from the beach in Bournemouth on the south coast of England. Since I moved here I've grown to love the place - even if it is full of grockles in Summer!