c++ check if file is empty - c++

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)
}

Related

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"

how to append text to a file in windows?

Everytime this function is called the old text data is lost?? Tell me how to maintain previous data and appending new data.
This function is called 10 times:
void WriteEvent(LPWSTR pRenderedContent)
{
HANDLE hFile;
DWORD dwBytesToWrite = ((DWORD)wcslen(pRenderedContent)*2);
DWORD dwBytesWritten = 0;
BOOL bErrorFlag = FALSE;
printf("\n");
hFile = CreateFile(L"D:\\EventsLog.txt", FILE_ALL_ACCESS, 0, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
if (hFile == INVALID_HANDLE_VALUE)
{
printf("Terminal failure: Unable to open file \"EventsLog.txt\" for write.\n");
return;
}
printf("Writing %d bytes to EventsLog.txt.\n", dwBytesToWrite);
bErrorFlag = WriteFile(
hFile, // open file handle
pRenderedContent, // start of data to write
dwBytesToWrite, // number of bytes to write
&dwBytesWritten, // number of bytes that were written
NULL); // no overlapped structure
if (FALSE == bErrorFlag)
{
printf("Terminal failure: Unable to write to file.\n");
}
else
{
if (dwBytesWritten != dwBytesToWrite)
{
printf("Error: dwBytesWritten != dwBytesToWrite\n");
}
else
{
printf("Wrote %d bytes to EventsLog.txt successfully.\n",dwBytesWritten);
}
}
CloseHandle(hFile);
}
You should pass FILE_APPEND_DATA as the dwDesiredAccess to CreateFile, as documented under File Access Rights Constants (see sample code at Appending One File to Another File). While this opens the file using the correct access rights, your code is still responsible for setting the file pointer. This is necessary, because:
Each time a file is opened, the system places the file pointer at the beginning of the file, which is offset zero.
The file pointer can be set using the SetFilePointer API after opening the file:
hFile = CreateFile( L"D:\\EventsLog.txt", FILE_APPEND_DATA, 0x0, nullptr,
OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr );
if ( hFile == INVALID_HANDLE_VALUE ) {
printf( "Terminal failure: Unable to open file \"EventsLog.txt\" for write.\n" );
return;
}
// Set the file pointer to the end-of-file:
DWORD dwMoved = ::SetFilePointer( hFile, 0l, nullptr, FILE_END );
if ( dwMoved == INVALID_SET_FILE_POINTER ) {
printf( "Terminal failure: Unable to set file pointer to end-of-file.\n" );
return;
}
printf("Writing %d bytes to EventsLog.txt.\n", dwBytesToWrite);
bErrorFlag = WriteFile( // ...
Unrelated to your question, the calculation of dwBytesToWrite should not use magic numbers. Instead of * 2 you should probably write * sizeof(*pRenderedContent). The parameter to WriteEvent should be constant as well:
WriteEvent(LPCWSTR pRenderedContent)
The parameter for appending data to a file is FILE_APPEND_DATA instead of FILE_ALL_ACCESS in the CreateFile function.
Here is an example: http://msdn.microsoft.com/en-us/library/windows/desktop/aa363778(v=vs.85).aspx

How make FILE* from HANDLE in WinApi?

Is there easy way to create FILE* from WinApi HANDLE which points to one end of pipe?
Something like we do in unix: fdopen(fd,<mode>);
You can do this but you have to do it in two steps. First, call _open_osfhandle() to get a C run-time file descriptor from a Win32 HANDLE value, then call _fdopen() to get a FILE* object from the file descriptor.
FILE* getReadBinaryFile(LPCWSTR path) {
HANDLE hFile = CreateFile(path, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if (hFile == INVALID_HANDLE_VALUE) {
return nullptr;
}
int nHandle = _open_osfhandle((long)hFile, _O_RDONLY);
if (nHandle == -1) {
::CloseHandle(hFile); //case 1
return nullptr;
}
FILE* fp = _fdopen(nHandle, "rb");
if (!fp) {
::CloseHandle(hFile); //case 2
}
return fp;
}
my code for get an open read binary file descriptor.
you should use fclose to close FILE* if you don't need it .
i didn't test for case 1 and 2, so use it at your own risk.
you can't exchange(convert) them.. if you need to have a file with FILE* and HANDLE you need to open it twice

how in C++ send file to browser

I need to send file from my directory to user. The problem file was not send.
Can any one help me?
My code is like:
CHttpServerContext* pCtxt;
// ... there i set headers for open
DWORD dwRead;
CString fileName = "c:\txt.doc";
HANDLE hFile = CreateFile (fileName, GENERIC_READ, FILE_SHARE_READ,
(LPSECURITY_ATTRIBUTES) NULL,
OPEN_EXISTING, FILE_ATTRIBUTE_READONLY, (HANDLE) NULL);
if (hFile == INVALID_HANDLE_VALUE)
{
return;
}
int c = 0;
CHAR szBuffer [2048];
do
{
if (c++ > 20) {
break;
return;
}
// read chunk of the file
if (!ReadFile (hFile, szBuffer, 2048, &dwRead, NULL))
{
return;
}
if (!dwRead)
// EOF reached, bail out
break;
// Send binary chunk to the browser
if (!pCtxt->WriteClient( szBuffer, &dwRead, 0))
{
return;
}
}
while (1);
CloseHandle (hFile);
}
Doctor, I'm sick. What's wrong with me?
I mean, you give almost no information about what happened.
Do you know if some function returned you an error in your code?
Why do you abort the loop after 20 iterations? This limits you to 40KB.
How exactly do you initialize CHttpServerContext?
You might use high-performance TransmitFile function if you just send the file as-is.
What is your client? How do you know it didn't get the file?
No point in re-inventing the wheel - just use the TransmitFile API instead - it is built into CHttpServerContent::TransmitFile().

Creating a MiniDump of a running process

Im trying to make a tool for my end users that can create a MiniDump of my application if it hangs (i.e. external to the app). Im using the same code as the internal MiniDumper but with the handle and processid of the app but i keep getting error code 0xD0000024 when calling MiniDumpWriteDump. Any ideas?
void produceDump( const char* exe )
{
DWORD processId = 0;
HANDLE process = findProcess(exe, processId);
if (!process || processId == 0)
{
printf("Unable to find exe %s to produce dump.\n", exe);
return;
}
LONG retval = EXCEPTION_CONTINUE_SEARCH;
HWND hParent = NULL; // find a better value for your app
// firstly see if dbghelp.dll is around and has the function we need
// look next to the EXE first, as the one in System32 might be old
// (e.g. Windows 2000)
HMODULE hDll = NULL;
char szDbgHelpPath[_MAX_PATH];
if (GetModuleFileName( NULL, szDbgHelpPath, _MAX_PATH ))
{
char *pSlash = _tcsrchr( szDbgHelpPath, '\\' );
if (pSlash)
{
_tcscpy( pSlash+1, "DBGHELP.DLL" );
hDll = ::LoadLibrary( szDbgHelpPath );
}
}
if (hDll==NULL)
{
// load any version we can
hDll = ::LoadLibrary( "DBGHELP.DLL" );
}
LPCTSTR szResult = NULL;
int err = 0;
if (hDll)
{
MINIDUMPWRITEDUMP pDump = (MINIDUMPWRITEDUMP)::GetProcAddress( hDll, "MiniDumpWriteDump" );
if (pDump)
{
char szDumpPath[_MAX_PATH];
char szScratch [_MAX_PATH];
time_t rawtime;
struct tm * timeinfo;
time ( &rawtime );
timeinfo = localtime ( &rawtime );
char comAppPath[MAX_PATH];
SHGetFolderPath(NULL, CSIDL_COMMON_APPDATA , NULL, SHGFP_TYPE_CURRENT, comAppPath );
//COMMONAPP_PATH
_snprintf(szDumpPath, _MAX_PATH, "%s\\DN", comAppPath);
CreateDirectory(szDumpPath, NULL);
_snprintf(szDumpPath, _MAX_PATH, "%s\\DN\\D", comAppPath);
CreateDirectory(szDumpPath, NULL);
_snprintf(szDumpPath, _MAX_PATH, "%s\\DN\\D\\dumps", comAppPath);
CreateDirectory(szDumpPath, NULL);
char fileName[_MAX_PATH];
_snprintf(fileName, _MAX_PATH, "%s_Dump_%04d%02d%02d_%02d%02d%02d.dmp", exe, timeinfo->tm_year+1900, timeinfo->tm_mon, timeinfo->tm_mday, timeinfo->tm_hour, timeinfo->tm_min, timeinfo->tm_sec );
_snprintf(szDumpPath, _MAX_PATH, "%s\\DN\\D\\dumps\\%s", comAppPath, fileName);
// create the file
HANDLE hFile = ::CreateFile( szDumpPath, GENERIC_WRITE, FILE_SHARE_WRITE, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL );
if (hFile!=INVALID_HANDLE_VALUE)
{
MINIDUMP_CALLBACK_INFORMATION mci;
mci.CallbackRoutine = (MINIDUMP_CALLBACK_ROUTINE)MyMiniDumpCallback;
mci.CallbackParam = 0;
MINIDUMP_TYPE mdt = (MINIDUMP_TYPE)(MiniDumpWithPrivateReadWriteMemory |
MiniDumpWithDataSegs |
MiniDumpWithHandleData |
//MiniDumpWithFullMemoryInfo |
//MiniDumpWithThreadInfo |
MiniDumpWithProcessThreadData |
MiniDumpWithUnloadedModules );
// write the dump
BOOL bOK = pDump( process, processId, hFile, mdt, NULL, NULL, &mci );
DWORD lastErr = GetLastError();
if (bOK)
{
printf("Crash dump saved to: %s\n", szDumpPath);
return;
}
else
{
_snprintf( szScratch, _MAX_PATH, "Failed to save dump file to '%s' (error %u)", szDumpPath, lastErr);
szResult = szScratch;
err = ERR_CANTSAVEFILE;
}
::CloseHandle(hFile);
}
else
{
_snprintf( szScratch, _MAX_PATH, "Failed to create dump file '%s' (error %u)", szDumpPath, GetLastError());
szResult = szScratch;
err = ERR_CANTMAKEFILE;
}
}
else
{
szResult = "DBGHELP.DLL too old";
err = ERR_DBGHELP_TOOLD;
}
}
else
{
szResult = "DBGHELP.DLL not found";
err = ERR_DBGHELP_NOTFOUND;
}
printf("Could not produce a crash dump of %s.\n\n[error: %u %s].\n", exe, err, szResult);
return;
}
this code works 100% when its internal to the process (i.e. with SetUnhandledExceptionFilter)
Are you opening the process with the necessary access rights? MiniDumpWriteDump() needs the process handle to be opened using PROCESS_QUERY_INFORMATION and PROCESS_VM_READ access rights. When using GetCurrentProcess(), I think these are granted automatically, but when using OpenProcess() to open another process, you have to request these rights.
To do so, you might also have to enable SeDebugPrivilege, which would cause problems for users whose accounts don't have that privilege. But the documentation doesn't seem to be clear on whether SeDebugPrivilege is necessary for PROCESS_QUERY_INFORMATION and PROCESS_VM_READ rights specifically (as opposed to all process access rights), particularly when opening a process that is running as the same user account.
I see that you are explicitly casting MyMiniDumpCallback to be a PMINIDUMP_CALLBACK_INFORMATION type. That looks fishy, as if you had a compiler error that you were getting around because the types didn't match. That, and PMINIDUMP_CALLBACK_INFORMATION is a struct, not a function pointer.
The direct cast of function pointer to PMINIDUMP_CALLBACK_INFORMATION might be valid since the first parameter of that struct is the callback function. But again, it looks real fishy. Perhaps you misdeclared your callback function (like forgetting the CALLBACK/__stdcall modifier). Get your code to compile without casting those formal params first, then I'll be more inclined to help you.
Also, did you even check that your callback function is even getting called at all?