I'm trying to log the actions made by a service I wrote using the Windows-API & C-language, so I made a log file system.
The problem is that at each CreateFile call, the file is overridden instead of just opening it and write at the end of the file.
Here's the code of my WriteInLogfile function :
void WriteInLogFile(LPCTSTR log_string)
{
HANDLE hFile;
DWORD dBytesWritten;
if ((hFile = CreateFile(LOG_FILE_PATH, GENERIC_WRITE, 0, NULL,
OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL)) == INVALID_HANDLE_VALUE
&& (GetLastError() == ERROR_FILE_NOT_FOUND))
{
if ((hFile = CreateFile(LOG_FILE_PATH, GENERIC_WRITE, 0, NULL,
CREATE_NEW, FILE_ATTRIBUTE_NORMAL, NULL)) != INVALID_HANDLE_VALUE)
{
if (!WriteFile(hFile, log_string, strlen(log_string), &dBytesWritten, NULL))
aff_error("WriteInLogFile");
CloseHandle(hFile);
}
}
else
{
if (!WriteFile(hFile, log_string, strlen(log_string), &dBytesWritten, NULL))
aff_error("WriteInLogFile");
CloseHandle(hFile);
}
}
Do someone know where the issue comes from ?
Thanks ;)
Even though you're opening the existing file you're not specifying that you want to append to it. Hence it opens as a generic write and you end up overwriting the contents. You need to pass the FILE_APPEND_DATA flag to the CreateFile method. This is best done by using the FILE_GENERIC_WRITE flag which includes FILE_APPEND_DATA
if ((hFile = CreateFile(LOG_FILE_PATH, FILE_GENERIC_WRITE, 0, NULL,
OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL)) == INVALID_HANDLE_VALUE
&& (GetLastError() == ERROR_FILE_NOT_FOUND))
{
When you open a file, the pointer will always be positioned to the beginning of the file. To append, you need to explicitly seek to the end (SetFilePointer(hFile, 0, 0, FILE_END);).
Although it may not be causing your actual problem, I'd replace your current logic trying to use CreateFile with OPEN_EXSTING, then with CREATE_NEW if the first attempt fails. Instead, just pass the OPEN_ALWAYS flag, which pretty much automates that logic -- open an existing file if it exists, and create a new one if it doesn't.
You need to set the file pointer to the end of the file before writing with SetFilePointer. See the MSDN example.
I couldn't see anything obvious about opening for Append in the CreateFile documentation, but you could use the SetFilePointer
function to seek to the end of the file before writing.
Related
I am pretty new to C, but using the standard library I would use something like the following code if I wanted to open a file and write data to a certain offset without overwriting the entire file.
ptrdiff_t offset = 0x44; // start offset of data I want to overwrite
FILE* f = fopen("file.sra", "r+b"); // open file in read/update mode
fseek(f, offset, SEEK_SET);
fwrite(buffer, 8, 1, f);
fclose(f);
I am learning how to use the Win32 API, and I can't seem to get equivalent functionality from the CreateFile() function. I have searched for similar questions, like these:
https://cboard.cprogramming.com/windows-programming/30783-overwrite-data-file.html
C++ Insert text at specific position in append with Windows API
Inserting text into file at specific offset using Win32 API
These posters seem to be asking the same general question, but the threads all dead end with no real answer.
Of course, I could just call fopen()/fwrite() and be done with it, but since I'm trying to learn the basics of Win32 API, I'd like to know if there is a way to achieve read/update functionality with just the Windows functions, particularly CreateFile().
Also, I know I can read the data and append and shift and all that, but that's not what I'm after. I want to achieve similar functionality to the example C code above by figuring out how to set CreateFile() to have a similar read/update mode.
Windows code I tried:
HANDLE hFile = CreateFile(pszFileName, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS,FILE_ATTRIBUTE_NORMAL, NULL);
....<irrelevant code>....
SetFilePointer(hFile, offset, NULL, FILE_BEGIN);
if(WriteFile(hFile, buffer, 8, NULL, NULL))
bSuccess = TRUE;
}
CloseHandle(hFile);
Thanks to Simon in the comments, I was able to figure this out. The key was to replace CREATE_ALWAYS with OPEN_EXISTING like so:
HANDLE hFile = CreateFile(pszFileName, GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
....<irrelevant code>....
SetFilePointer(hFile, offset, NULL, FILE_BEGIN);
if(WriteFile(hFile, buffer, 8, NULL, NULL))
bSuccess = TRUE;
}
CloseHandle(hFile);
This opens an existing file, sets the file pointer to a certain offset, and then writes 8 bytes to the existing file, overwriting only the previous 8 bytes at that location, leaving the rest of the file intact.
New to C programming.
The following section of code attempts to read a tab-separated list of MD5 (32 chars) and corresponding description (up to 128 chars) from a text file (utf-8), but is causing the application to crash:
HANDLE hFile = CreateFileW(good_path, GENERIC_READ, FILE_SHARE_READ, NULL,
OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED, NULL);
if (hFile == INVALID_HANDLE_VALUE)
{
return FALSE;
}
LPWSTR md5 = malloc(sizeof(wchar_t) * 32);
LPWSTR desc = malloc(sizeof(wchar_t) * 128);
int i;
while((i = fwscanf(hFile, L"%ls %ls", md5, desc)) != EOF)
{
if (i == 2) // OK
{
}
else // Something went wrong
{
}
}
CloseHandle(hFile);
return TRUE;
Few questions:
Is my use of malloc(...) correct?
What might be causing the crash?
Update 1
I've taken this code and made it into a standalone exe (rather than a DLL). Still crashes.
Update 2
Updated to fwscanf as per Chris's comment. Still crashes. If I comment out the while...fwscanf... line it exits properly.
CreateFileW() returns a Windows handle, which is sort of like a file number but different somehow. fwscanf() expects a FILE* not a Windows handle; to get a FILE* open your file with fopen() or _wfopen().
%s stores a null terminator. Malloc 33 and 129 chars.
%s stores a nul-terminated string under the address your provide. To store n significant characters without buffer overflow, you need to provide an address of n+1 long buffer.
HANDLE hFile = CreateFile(LPCTSTR("filename"), // name of the write
GENERIC_READ | GENERIC_WRITE, // open for writing and reading
0, // do not share
NULL, // default security
OPEN_ALWAYS, // create new file only
FILE_ATTRIBUTE_NORMAL, // normal file
NULL);
if(hFile != INVALID_HANDLE_VALUE)
{
printf("Success.\n");
}
CloseHandle(hFile);
The operation succeeds but i can't find 'filename' on the disk.
Does CreateFile() actually creates file on the disk?
I tried a minimal program containing only your code and ... the file was correctly created in current directory ! But only if the program is compiled in ANSI mode, because the LPCTSTR only converts the pointer to be a LPCTSTR but does not convert from ANSI to UNICODE. Only the _T macro does that.
You should use GetCurrentDirectory to control where you try to write the file, and use TCHAR to be UNICODE compatible :
#include <windows.h>
#include <tchar.h>
#include <stdio.h>
int main() {
LPTSTR dir;
DWORD cr = ::GetCurrentDirectory(0, NULL);
cr += 1;
dir = (LPTSTR) malloc(cr * sizeof(TCHAR));
cr = ::GetCurrentDirectory(cr, dir);
/* ::MessageBox(NULL, dir, _T("Current dir"), MB_OK); */
_tprintf(_T("Current dir : %s\n"), dir); // note the _tprintf and _T macro
free(dir);
HANDLE hFile = CreateFile(_T("filename"), // name of the write - _T
GENERIC_READ | GENERIC_WRITE, // open for writing and reading
0, // do not share
NULL, // default security
OPEN_ALWAYS, // create new file only
FILE_ATTRIBUTE_NORMAL, // normal file
NULL);
if(hFile != INVALID_HANDLE_VALUE)
{
printf("Success.\n");
}
CloseHandle(hFile);
return 0;
}
If I compile in UNICODE mode without the _T macro around "filename", the program does create a file, but its name is simply garbage
There is not much information, but here is a blind guess : your file is redirected to the VirtualStore.
Some locations, like "Program Files", are protected on the latest versions of Windows.
If your program is in a protected location and is not run as administrator, the read/write operations will be redirected to C:\Users\MYNAME\AppData\Local\VirtualStore\MYFOLDER.
Running your program as admin should correct this wherever it is located.
See title.
How do I achieve the opposite of this question: How do I get the file HANDLE from the fopen FILE structure?
I create the handle with
HANDLE h = CreateFile(name,
GENERIC_WRITE,
0,
NULL,
OPEN_ALWAYS,
FILE_ATTRIBUTE_NORMAL,
NULL);
and then try to write some data to it using fputs.
The call to fputs fails on the line
_VALIDATE_STREAM_ANSI_RETURN(stream, EINVAL, EOF);
where stream is the handle I obtained from CreateFile.
The reason why I'm doing that is that I use an external library that uses FILE* handles and I'm not opening a plain file (as until now) but trying to write to a pipe instead. And changing the external library is not an option.
Don't know if this is the best way but see _open_osfhandle():
http://msdn.microsoft.com/en-us/library/bdts1c9x(v=vs.71).aspx
int fd = _open_osfhandle(h, ...);
It returns a file descriptor which you'll have to open using fdopen to get a FILE*.
FILE* fp = _fdopen(fd, ...);
I want to use it during uninstall procedure to warn the user ahead.
The procedure should work for W2000+, hence Vista API's are not allowed.
This seems to catch some conflicts:
if( GetFileAttributes( lpPath ) == INVALID_FILE_ATTRIBUTES )
{
// File does not exist
}
else
{
BOOL bCanDelete = FALSE ;
HANDLE hFile = CreateFile( path,
GENERIC_WRITE /*|DELETE*/,
0 /*FILE_SHARE_DELETE*/,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL
);
if( hFile != INVALID_HANDLE_VALUE )
{
DWORD size = 10000 ; // a number > file size allowed
if( size != INVALID_FILE_SIZE )
{
if( LockFile( hFile, 0,0, size,0) )
{
UnlockFile( hFile, 0,0, size,0) ;
bCanDelete = TRUE ;
}
}
CloseHandle( hFile ) ;
}
}
Namely it detects these situations:
a) Deleting running exe file
b) Deleting opened pdf
Using GENERIC_WRITE|DELETE seems to behave similarly.
Using DELETE alone works for the case b), but not for a).
I have no positive evidence that LockFile() catches any meaningful conflict, but suppose it does.
Does anybody have a better idea?
First point: unless you take steps to prevent it, nearly anything you report may change between the time you test, and the time you try to take action based on that test (e.g. after you've tried to check that you can delete it, the user might change it to 'read-only').
To get a meaningful result, instead of using DeleteFile to delete the file, I'd use CreateFile with FILE_SHARE_DELETE and the FILE_FLAG_DELETE_ON_CLOSE flag. If you can't open the file this way, it gives a pretty good clue that you won't be able to delete it. If you can open it this way, then simply closing the handle will delete the file -- and nothing else will be able to open it in the interim unless it also specifies FILE_SHARE_DELETE (and even if it does, when the last handle to the file is closed, the file will be deleted, so if it's not deleted immediately, it will be soon).
I'm not a C++ programmer, but you could try to rename that file. If you can do that, you probably can delete it.