The header:
#pragma once
//-----------------------------------------------------------------------------
// Asynchronous File I/O class
//-----------------------------------------------------------------------------
#define ASYNC_IO_SEARCH_COMPLETE 0x00000001
#define ASYNC_IO_SEARCH_INCOMPLETE 0x00000002
#define ASYNC_IO_SEARCH_FAILED 0x00000003
#define ASYNC_IO_READWRITE_SUCCEEDED 0x00000010
#define ASYNC_IO_READWRITE_FAILED 0x00000020
#define ASYNC_IO_READWRITE_PENDING 0x00000030
#define ASYNC_IO_READWRITE_INCOMPLETE 0x00000040
namespace async_io
{
template <class ptrType>
class CAsyncIO
{
public:
CAsyncIO(): Status(0), /*FileName(NULL),*/ SearchThreadHandle(NULL), SearchEventHandle(NULL), DataPtr(NULL),
FileSize(0)
{
// Initialize the critical section for the file search
InitializeCriticalSection( &CriticalSection );
// Clear the overlapped struct
::ZeroMemory( &Overlapped, sizeof( OVERLAPPED ) );
//
::ZeroMemory( &FileName, 128 );
}
virtual ~CAsyncIO()
{
// Delete the critical section
DeleteCriticalSection( &CriticalSection );
}
public:
DWORD LocateFile( CHAR* szFileName, bool bFullPathIncluded = false );
DWORD Open( DWORD dwDesiredAccess, DWORD dwShareMode, DWORD dwCreationDisposition, bool bNoAllocPointer = false );
DWORD Read( DWORD dwBytesToRead, PDWORD pdwBytesRead, ptrType Ptr = NULL );
DWORD Write( DWORD dwBytesToWrite, PDWORD pdwBytesWritten, ptrType Ptr = NULL );
void Cancel();
void Close();
DWORD GetStatus() { return Status; } // Only returns the immediate status last set!
DWORD GetAsyncStatus();
HANDLE GetFileHandle() { return FileHandle; }
ptrType GetDataPtr() { return DataPtr; }
DWORD GetSize() { return FileSize; }
protected:
unsigned int __stdcall FileSearchThread( void* Params )
{
// Set the file search status to incomplete
Status = ASYNC_IO_SEARCH_INCOMPLETE;
// Enter the critical section. STL is not thread safe.
EnterCriticalSection( &CriticalSection );
// Search for the file requested (this may take a while)
strcpy( FileName, effect_api::GetFilePath( (char*) Params ).c_str() );
// Leave the critical section; all the non-thread safe code is finished.
LeaveCriticalSection( &CriticalSection );
// Signal the event of this thread finishing...
SetEvent( SearchEventHandle );
// Did we find the file at all?
if( !strcmp( FileName, "" ) )
Status = ASYNC_IO_SEARCH_FAILED;
else
Status = ASYNC_IO_SEARCH_COMPLETE;
}
protected:
DWORD Status;
DWORD FileSize;
CHAR FileName[128];
OVERLAPPED Overlapped;
HANDLE SearchThreadHandle;
HANDLE SearchEventHandle;
HANDLE FileHandle;
CRITICAL_SECTION CriticalSection;
ptrType DataPtr;
};
};
The source file:
#include "Platform.h"
#include "AsyncIO.h"
//-----------------------------------------------------------------------------
// Name: async_io::CAsyncIO<ptrType>::LocateFile
// Desc: Searches for the full path of a given file name. You can specify the
// entire file path if you need to, but remember to specify that you'll be
// doing so by setting the second parameter to true. The return value is
// a WinNT error message from GetLastError().
//-----------------------------------------------------------------------------
template <class ptrType>
DWORD async_io::CAsyncIO<ptrType>::LocateFile( CHAR *szFileName, bool bFullPathIncluded )
{
// Is the full path included?
if( bFullPathIncluded )
{
// If so, then just copy the file path over...
strcpy( FileName, szFileName );
// ... and state that the file was found (sorta)
// TODO: Verify that the file exists before continuing?
Status = ASYNC_IO_SEARCH_COMPLETE;
return ERROR_SUCCESS;
}
else
{
// If not, then create a new thread and an event that is signaled when
// the thread is finished. This thread will search for the file without
// stalling the current thread.
SearchThreadHandle = (HANDLE) _beginthreadex( NULL, 0, this->FileSearchThread, szFileName, CREATE_SUSPENDED, NULL );
if( !SearchThreadHandle )
return GetLastError();
// Create the event used to signal the end of this thread's existence.
SearchEventHandle = CreateEvent( NULL, FALSE, FALSE, NULL );
if( !SearchEventHandle )
{
CloseHandle( SearchThreadHandle );
return GetLastError();
}
// Start the thread
ResumeThread( SearchThreadHandle );
// The class instance's status should be set to incomplete by now, go ahead
// and exit with a success...
}
return ERROR_SUCCESS;
}
//-----------------------------------------------------------------------------
// Name: async_io::CAsyncIO<ptrType>::Open
// Desc: Attempts to open the file that was previously searched for. If the file
// exists, then a handle to the file will be created to read/write to/from
// the file asynchronously when ready. If the function returns E_PENDING,
// then the file search thread has not finished.
//-----------------------------------------------------------------------------
template <class ptrType>
DWORD async_io::CAsyncIO<ptrType>::Open( DWORD dwDesiredAccess, DWORD dwShareMode, DWORD dwCreationDisposition, bool bNoAllocPointer )
{
// Is the file is already open?
if( FileHandle )
return E_FAIL;
// Has the file search thread finished searching for the file?
if( SearchThreadHandle )
{
// Test the status of this thread
DWORD dwResult = WaitForSingleObject( SearchThreadHandle, WAIT_OBJECT_0 );
// The thread is still searching? Return with a pending error message.
if( dwResult == WAIT_TIMEOUT )
{
return E_PENDING;
}
// If the thread is finished, go ahead and close it now.
else if( dwResult == 0 )
{
CloseHandle( SearchThreadHandle );
SearchThreadHandle = NULL;
}
// If the thread failed somehow, return the detailed error code...
else
{
return GetLastError();
}
}
// Attempt to open the actual file.
FileHandle = CreateFile( FileName, dwDesiredAccess, dwShareMode, NULL, dwCreationDisposition,
FILE_FLAG_RANDOM_ACCESS|FILE_FLAG_OVERLAPPED, 0 );
if( !FileHandle )
{
return GetLastError();
}
// Get the file size and allocate a pointer large enough to hold the entire file (if desired)
FileSize = GetFileSize( FileHandle, NULL );
if( !bNoAllocPointer )
{
DataPtr = new BYTE[FileSize];
if( !DataPtr )
{
return E_OUTOFMEMORY;
}
}
// Now we can call read/write when necessary.
return ERROR_SUCCESS;
}
//-----------------------------------------------------------------------------
// Name: async_io::CAsyncIO<ptrType>::Read
// Desc: Reads in the file's data asynchronously. The first parameter can be
// either the desired number of bytes to read or the size of the entire file.
// If 0 is specified, then this method will default to the file's total
// size. The second parameter returns the number of bytes actually read.
// The third parameter is an optional pointer to read data into. If this
// parameter is NULL, then data will be read into the default data pointer.
//-----------------------------------------------------------------------------
template <class ptrType>
DWORD async_io::CAsyncIO<ptrType>::Read( DWORD dwBytesToRead, PDWORD pdwBytesRead, ptrType Ptr )
{
BOOL bRet = FALSE;
// Has the file been opened?
if( !FileHandle )
return E_FAIL;
// How much data are we reading in?
if( dwBytesToRead == 0 )
{
dwBytesToRead = FileSize;
}
// Are we using the third parameter?
if( Ptr != NULL )
{
// If so, read data into this pointer. Assume the pointer is unallocated
// and that the caller is responsible for deallocating it.
Ptr = new BYTE[dwBytesToRead];
if( !Ptr )
return E_OUTOFMEMORY;
// Now let's read in the data
bRet = ReadFile( FileHandle, Ptr, dwBytesToRead, pdwBytesRead, &Overlapped );
}
else
{
// If not, use the previously allocated pointer. If it does not exist, then
// the user didn't want it allocated when ::Open was called. In my honest
// opinion, I wouldn't recommend allocating it at the specific size set because
// what if we have subsequent calls to this method where a larger pointer is
// needed later? Sure I could use a variable to keep track of the current
// allocation size, but I don't feel like going through that hassle, and I won't.
// So if you didn't allocate it before and all of a sudden you want it now? Guess
// what?? Tough luck pal! Too bad, so sad, cry me a river! I hope you like getting
// a big fat E_INVALIDARG as your return value!!!
if( !DataPtr )
return E_INVALIDARG;
// Read in the desired amount of data
bRet = ReadFile( Filehandle, DataPtr, dwBytesToRead, pdwBytesRead, &Overlapped );
}
// Check for errors.
if( !bRet )
{
DWORD dwError = GetLastError();
// If it's pending or still in the process of reading, we're okay.
if( dwError == ERROR_IO_PENDING || dwError == ERROR_IO_INCOMPLETE )
{
Status = ASYNC_IO_READWRITE_PENDING;
}
else // Uh oh, something else happened...
{
Status = ASYNC_IO_READWRITE_FAILED;
return dwError;
}
}
return ERROR_SUCCESS;
}
//-----------------------------------------------------------------------------
// Name: async_io::CAsyncIO<ptrType>::Write
// Desc: Writes in the file's data asynchronously. The parameters are pretty
// much the same as the above (with a hint of common sense), only the
// 3rd parameter is required to be valid, not optional. Just don't bite off
// more than you can chew; be sure that the allocation size and the pointer
// itself match or the allocation size isn't greater than the pointer itself.
//-----------------------------------------------------------------------------
template <class ptrType>
DWORD async_io::CAsyncIO<ptrType>::Write( DWORD dwBytesToWrite, PDWORD pdwBytesWritten, ptrType Ptr )
{
BOOL bRet = FALSE;
// Has the file been opened?
if( !FileHandle )
return E_FAIL;
// Do we have a valid pointer passed in?
if( !Ptr )
return E_INVALIDARG;
// How much data are we reading in?
if( dwBytesToRead == 0 )
{
dwBytesToRead = FileSize;
}
// Start writing...
bRet = WriteFile( FileHandle, Ptr, dwBytesToWrite, pdwBytesWritten, &Overlapped );
// Check for errors.
if( !bRet )
{
DWORD dwError = GetLastError();
// If it's pending or still in the process of reading, we're okay.
if( dwError == ERROR_IO_PENDING || dwError == ERROR_IO_INCOMPLETE )
{
Status = ASYNC_IO_READWRITE_PENDING;
}
else // Uh oh, something else happened...
{
Status = ASYNC_IO_READWRITE_FAILED;
return dwError;
}
}
return ERROR_SUCCESS;
}
//-----------------------------------------------------------------------------
// Name: async_io::CAsyncIO<ptrType>::Cancel
// Desc: Cancels any asynchronous file read/write operations going on (if any).
//-----------------------------------------------------------------------------
template <class ptrType>
void async_io::CAsyncIO<ptrType>::Cancel()
{
// Verify we have a valid file handle first.
if( !FileHandle )
return;
// Cancel any asynchronous file read/write operations now.
::CancelIoEx( FileHandle, &Overlapped );
}
//-----------------------------------------------------------------------------
// Name: async_io::CAsyncIO<ptrType>::Close
// Desc: Cancels any asyncronous operations, closes all handles, and deallocates
// any pointers. Note that it doesn't delete the critical section. The
// critical section is deleted in the deconstructor. That is by design
// in case the class instance is used more than once.
//-----------------------------------------------------------------------------
template <class ptrType>
void async_io::CAsyncIO<ptrType>::Close()
{
// Be sure that there are no asynchronous reads/writes being performed now.
Cancel();
// Kill the thread if it exists
if( SearchThreadHandle )
{
// Wait for the thread to finish or abort
WaitForSingleObject( SearchThreadHandle, INFINITE );
// Now kill the thread
CloseHandle( SearchThreadHandle );
SearchThreadHandle = NULL;
}
// Kill the event if it exists
if( SearchEventHandle )
{
CloseHandle( SearchEventHandle );
SearchEventHandle = NULL;
}
// Deallocate the default pointer if it exists
SAFE_DELETE_ARRAY( DataPtr );
// Now close the file
if( FileHandle )
{
CloseHandle( FileHandle );
FileHandle = NULL;
}
return 0;
}
//-----------------------------------------------------------------------------
// Name: async_io::CAsyncIO<ptrType>::GetAsyncStatus
// Desc: Returns the status of the current asynchronous operation. This method
// only returns a WinNT error code. To get the simplified error code ass-
// ociated with this class, call CAsyncIO::GetStatus() afterwards.
//-----------------------------------------------------------------------------
template <class ptrType>
DWORD async_io::CAsyncIO<ptrType>::GetAsyncStatus()
{
DWORD dwBytesTransferred;
// Get the overlap status (don't wait for it to finish).
BOOL bRet = GetOverlappedResult( FileHandle, &Overlapped, &dwBytesTransferred, FALSE );
// If it returns false, either it's not done yet or it failed...
if( !bRet )
{
// Check the error code
DWORD dwError = GetLastError();
// Has the operation even started?
if( dwError == ERROR_IO_PENDING )
{
Status = ASYNC_IO_READWRITE_PENDING;
return dwError;
}
// Is the operation started but unfinished?
else if( dwError == ERROR_IO_INCOMPLETE )
{
Status = ASYNC_IO_READWRITE_INCOMPLETE;
return dwError;
}
// Anything else is considered a major error...
else
{
Status = ASYNC_IO_READWRITE_FAILED;
return dwError;
}
}
// If we get here, then the data is finished being read or written.
return ERROR_SUCCESS;
}
As you can see, the class's function methods are defined, but I still get these darn linker errors! And if this matters, this is the test program I've written to test the class:
#include "AsyncIO.h"
using namespace async_io;
async_io::CAsyncIO<void*>* FileIO = NULL;
int main()
{
// Create a new instance of the class
FileIO = new async_io::CAsyncIO<void*>();
// Lets try searching for a file
printf( "Beginning search for the file...\n" );
if( FAILED( FileIO->LocateFile( "eb_property.cpp", false ) ) )
{
delete FileIO;
return -1;
}
// Wait for it to finish searching for the file...
printf( "Searching" );
while( true )
{
DWORD dwStatus = FileIO->GetStatus();
if( dwStatus == ASYNC_IO_SEARCH_INCOMPLETE )
{
printf( "." );
continue;
}
else if( dwStatus == ASYNC_IO_SEARCH_COMPLETE )
{
printf( "Done\n" );
break;
}
else
{
printf( "An error has occured...\n" );
delete FileIO;
return -1;
}
}
// We're finished
FileIO->Close();
delete FileIO;
return 0;
}
This is the linker error it gives me:
asyncio2.obj : error LNK2001: unresolved external symbol "public: void __thiscall async_io::CAsyncIO<void *>::Close(void)" (?Close@?$CAsyncIO@PAX@async_io@@QAEXXZ)
asyncio2.obj : error LNK2001: unresolved external symbol "public: unsigned long __thiscall async_io::CAsyncIO<void *>::LocateFile(char *,bool)" (?LocateFile@?$CAsyncIO@PAX@async_io@@QAEKPAD_N@Z)
Debug/asyncio2.exe : fatal error LNK1120: 2 unresolved externals
It doesn't really make any sense to me. Does it have to do with the template? This sucks. Any ideas? Thanks.
EDIT: It doesn't matter what version of Visual Studio C++ I use, I still get the same linker errors. From Visual Studio 6.0 to .net 2008 and beyond...