Last write FILETIME always returning current time - c++

I need to compare a file's last modified time to a date time stored in a database. I initially looked at this question to get started.
I am currently getting the FILETIME for the last write of the file, converting it to a SYSTEMTIME. Then I use that SYSTEMTIME to create a TDateTime object that I can use for my comparison. However, the FileModifiedDT variable, is always coming out to be the current time, despite the file having been modified previously.
FILETIME lastWriteTime;
String * FileNamePtr = new String( FileName );
GetFileTime( FileNamePtr, NULL, NULL, &lastWriteTime );
SYSTEMTIME systemTime;
FileTimeToSystemTime( &lastWriteTime, &systemTime );
TDateTime * FileModifiedDT = new TDateTime( systemTime.wYear, systemTime.wMonth,
systemTime.wDay, systemTime.wHour,
systemTime.wMinute, systemTime.wSecond,
systemTime.wMilliseconds );
Am I missusing GetFileTime in some way? Is there a better way I should go about this?

The error is
String * FileNamePtr = new String( FileName );
GetFileTime( FileNamePtr, NULL, NULL, &lastWriteTime );
According to the documentation, the first argument has to be a file handle created by CreateFile and nothing else.
Thus you need something like this:
HANDLE fileHandle = CreateFile(
FileName, //LPCTSTR
GENERIC_READ,
FILE_SHARE_READ,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL
);
if ( fileHandle != INVALID_HANDLE )
{
GetFileTime( fileHandle, NULL, NULL, &lastWriteTime );
CloseHandle( fileHandle );
}
else
{
// error, do something else...
}

Related

C++ Overwriting Contents of a Handle

I am trying to get a number from a HANDLE file, store it in an int, and possibly replace it in the same file. My code right now looks like this
HANDLE numFile = INVALID_HANDLE_VALUE; //Just in case file not found
numFile = CreateFile("numFile.txt",
GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL);
ReadFile(numFile, input, sizeof(char), &bytesRead, NULL);
int myNumber = input[0];
I know that there exists a WriteFile method in the API, but it looks like it will append the file as opposed to overwriting the contents. I have briefly considered deleting and recreating the file each time, but this seems unnecessarily complex for this problem. Any ideas out there?
Use the function SetFilePointer prior WriteFile for moving back to the begin
SetFilePointer(numFile, 0, NULL, FILE_BEGIN);

Why does WriteFile not run more than once?

Here's my code in which I've got on an infinite loop (to my knowledge)
while(true) {
DWORD TitleID = XamGetCurrentTitleId();
std::ostringstream titleMessageSS;
titleMessageSS << "Here's the current title we're on : " << TitleID << "\n\n";
std::string titleMessage = titleMessageSS.str(); // get the string from the stream
DWORD dwBytesToWrite = (DWORD)titleMessage.size();
DWORD dwBytesWritten = 0;
BOOL bErrorFlag = FALSE;
HANDLE logFile = CreateFile( "Hdd:\\LOGFile.txt", GENERIC_WRITE, 0, NULL, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, NULL);
bErrorFlag = WriteFile(logFile, titleMessage.c_str(), dwBytesToWrite, &dwBytesWritten, NULL);
CloseHandle(logFile);
Sleep(30000);
}
return NULL;
Does anyone see a reason as to why this only writes just once? I've waited over 5 minutes to see if it does anything in the end to no avail.
The Flag CREATE_NEW in CreateFile prevents the update of the file because CreateFile fail with ERROR_FILE_EXISTS. Use OPEN_ALWAYS instead.
Also it will always truncate. Replace GENERIC_WRITE with FILE_APPEND_DATA if you want to add a new line at the end of your logfile.
The whole CreateFile line should be:
HANDLE logFile = CreateFile( "Hdd:\\LOGFile.txt", FILE_APPEND_DATA , 0, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
Read CreateFile documentation carefully, it worth it, because it has a central role in the windows IO universe:
https://msdn.microsoft.com/en-us/library/windows/desktop/aa363858(v=vs.85).aspx
look also add:
https://stackoverflow.com/a/9891875/1922748
As Martin James mentioned, from MSDN:
CREATE_NEW
Creates a new file, only if it does not already exist.
If the specified file exists, the function fails and the last-error
code is set to ERROR_FILE_EXISTS (80).
If the specified file does not exist and is a valid path to a writable
location, a new file is created.
So it seems that the handle is invalid after the first call, and hence WriteFile() fails.

Add/Remove bytes from end of file on Windows

So, I've looked around, but couldn't find a way to remove bytes from the end of a file without rewriting the entire file. I found that a truncate function works for linux, but didn't find anything for windows. Now, obviously, to expand a file, I can just pad the end with null bytes, but for reducing a file's size, is it literally necessary to rewrite the whole file on windows? or is there a function, maybe in windows.h, that allows me, like truncate on linux, to reassign a file's size?
EDIT: I did just find the function _chdir(int,long), and I'm reading on how to use it.
EDIT: And, why exactly did fstream leave out this vital function?
EDIT: Ok, so it appears that _chdir() will not work (I forgot to mention this, btw), because the function must support files larger than 4 GB - i.e., I'm using 64bit file pointers. I thought that would be inherent, but after reading the arguments to chsize, the length is not size_t.
You truncate a file by calling SetFilePointer or SetFilePointerEx to the desired location followed by SetEndOfFile. The following shows how a truncate function can be implemented:
bool truncate( HANDLE hFile, LARGE_INTEGER NewSize ) {
LARGE_INTEGER Size = { 0 };
if ( GetFileSizeEx( hFile, &Size ) ) {
LARGE_INTEGER Distance = { 0 };
// Negative values move the pointer backward in the file
Distance.QuadPart = NewSize.QuadPart - Size.QuadPart;
return ( SetFilePointerEx( hFile, Distance, NULL, FILE_END ) &&
SetEndOfFile( hFile ) );
}
return false;
}
// Helper function taking a file name instead of a HANDLE
bool truncate( const std::wstring& PathName, LARGE_INTEGER NewSize ) {
HANDLE hFile = CreateFileW( PathName.c_str(), GENERIC_WRITE, FILE_SHARE_READ,
NULL, OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL, NULL );
if ( hFile == INVALID_HANDLE_VALUE ) {
return false;
}
bool Success = truncate( hFile, NewSize );
CloseHandle( hFile );
return Success;
}
EDIT: Shorter Version
The truncate function can be shortened to the following:
bool truncate( HANDLE hFile, LARGE_INTEGER NewSize ) {
return ( SetFilePointerEx( hFile, NewSize, NULL, FILE_BEGIN ) &&
SetEndOfFile( hFile ) );
}
If you would rather want to pass the amount of bytes by which to shrink the file, the following implementation can be used:
bool truncate( HANDLE hFile, LARGE_INTEGER ShrinkBy ) {
ShrinkBy.QuadPart = -ShrinkBy.QuadPart;
return ( SetFilePointerEx( hFile, ShrinkBy, NULL, FILE_END ) &&
SetEndOfFile( hFile ) );
}
To grow a file, open the file using CreateFile with a dwDesiredAccess that contains FILE_APPEND_DATA. Using SetFilePointer again to set the file pointer to the end of file you can then write new data calling WriteFile. For an example, see Appending One File to Another File.
EDIT: Growing a file without writing to it
If you don't care about the file contents beyond the original file size you can apply the same sequence as shown for truncating a file to extend it:
bool SetFileSize( HANDLE hFile, LARGE_INTEGER NewSize ) {
return ( SetFilePointerEx( hFile, NewSize, NULL, FILE_BEGIN ) &&
SetEndOfFile( hFile ) );
}
This is documented behavior for SetEndOfFile:
The SetEndOfFile function can be used to truncate or extend a file. If the file is extended, the contents of the file between the old end of the file and the new end of the file are not defined.
You probably want the SetEndOfFile function.
EDIT: This should work with files larger than 4GB. Use the SetFilePointerEx function for that.
To remove bytes from end of file on Windows:
FSUTIL file seteof <filename> <new size>
To add (null)bytes to end of (existing)file:
FSUTIL file seteof <filename> <new size>
No need to copy/"rewrite the whole file on windows"

c++ check if file is empty

I got a project in C++ which I need to edit. This is a declaration of variable:
// Attachment
OFSTRUCT ofstruct;
HFILE hFile = OpenFile( mmsHandle->hTemporalFileName , &ofstruct , OF_READ );
DWORD hFileSize = GetFileSize( (HANDLE) hFile , NULL );
LPSTR hFileBuffer = (LPSTR)GlobalAlloc(GPTR, sizeof(CHAR) * hFileSize );
DWORD hFileSizeReaded = 0;
ReadFile( (HANDLE) hFile , hFileBuffer, hFileSize, &hFileSizeReaded, NULL );
CloseHandle( (HANDLE) hFile );
I need to check if the file is attached (I suppose I need to check if hFile has any value), but don't know how. I tried with hFile == NULL but this doesn't do the job.
Thanks,
Ile
Compare hFile with HFILE_ERROR (not with NULL!). Also, you should change OpenFile to CreateFile and call it properly, OpenFile has long been deprecated. In fact MSDN clearly states:
OpenFile Function
Only use this function with 16-bit
versions of Windows. For newer
applications, use the CreateFile
function.
When you make this change, you will get a HANDLE back, which you should compare with INVALID_HANDLE_VALUE.
Update: Correct way to get a file's size:
LARGE_INTEGER fileSize={0};
// You may want to use a security descriptor, tweak file sharing, etc...
// But this is a boiler plate file open
HANDLE hFile=CreateFile(mmsHandle->hTemporalFileName,GENERIC_READ,0,NULL,
OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL);
if (hFile!=INVALID_HANDLE_VALUE && GetFileSizeEx(hFile,&fileSize) &&
fileSize.QuadPart!=0)
{
// The file has size
}
else
{
// The file is missing or size==0 (or an error occurred getting its size)
}
// Do whatever else and don't forget to close the file handle when done!
if (hFile!=INVALID_HANDLE_VALUE)
CloseHandle(hFile);
Before you open the file you can try this:
WIN32_FIND_DATA wfd;
HANDLE h = FindFirstFile(filename, &wfd);
if (h != INVALID_FILE_HANDLE)
{
// file exists
if (wfd.nFileSizeHigh != 0 || wfd.nFileSizeLow != 0)
{
// file is not empty
}
FindClose(h)
}

WCHAR to LPCWSTR

when i use CreateFile function like below ...it gives me valid handle
HANDLE hDevice = CreateFile (TEXT("\\\\.\\G:"),
0,FILE_SHARE_READ | FILE_SHARE_WRITE, // share mode
NULL, OPEN_EXISTING, 0, NULL);
if( hDevice == INVALID_HANDLE_VALUE )
{
qDebug()<<"In valid handle";
}
else
{
qDebug()<<"valid handle";
}
when i use like below ...it gives me invalid handle..
WCHAR Drive[4];
qDebug ()<<QString::fromWCharArray ( Drive );
The above prints like "G:\"
HANDLE hDevice = CreateFile ( Drive,
0,FILE_SHARE_READ | FILE_SHARE_WRITE, // share mode
NULL, OPEN_EXISTING, 0, NULL);
if( hDevice == INVALID_HANDLE_VALUE )
{
qDebug()<<"In valid handle";
}
else
{
qDebug()<<"valid handle";
}
How can i change the wchar to LPCWSTR
Thank you
The problem is not the conversion of the string, but the contents of the string. You can't open a volume (I guess that's what you're trying to do) with "G:\". It needs to be in the same format as you used in the first example. From MSDN:
When opening a volume or floppy drive,
the lpFileName string should be the
following form: \\.\X:. Do not use a
trailing backslash, which indicates
the root directory of a drive.
Hint: Always use GetLastError() after API functions fail to get the reason for the failure.
Update:
MSDN Link
You can either use the toWCharArray function or try something like this:
handle = CreateFile((LPCWSTR) fileName.constData(), FILE_SHARE_READ | FILE_SHARE_WRITE, 0, OPEN_EXISTING, 0, 0);
or this:
handle = CreateFile((LPCWSTR) fileName.utf16(), FILE_SHARE_READ | FILE_SHARE_WRITE, 0, OPEN_EXISTING, 0, 0);
LPCWSTR is a pointer (LP) to a constant (C) wide character (W) string (STR). In other words, this is a const WCHAR*
WCHAR Drive[4]; is a wide character array, which can also be called a wide character string.
Any array of a certain type can implicitly convert to a pointer to that same type. Furthermore, a pointer of a certain type can implicitly convert to a constant pointer of the same type, especially in the case of a function call.
Thus passing Drive to that function implicitly converts to LPCWSTR.
Your problem in not in that conversion. Your problem is most likely in the contents of your strings, as humbagumba's answer already explained.