Finding where a symbolic link points to (Windows) - c++

I use FindFirstFile() and co-functions to browse the contents of C:\example\dir. I know that a file read can be a symbolic link, a junction, etc by checking whether d.dwAttributes & FILE_ATTRIBUTE_REPARSE_POINT != 0. However, I haven't found a way to follow the link and see the location it is pointing to. Is that even possible?

To find the target of a symbolic link, you have to open the symbolic link. The object manager dereferences the link and returns a handle to the target location. Calling GetFinalPathNameByHandle on that handle returns the pathname of the target.
The following implementation returns the target location, given a symbolic link:
std::wstring GetLinkTarget( const std::wstring& a_Link ) {
// Define smart pointer type for automatic HANDLE cleanup.
typedef std::unique_ptr<std::remove_pointer<HANDLE>::type,
decltype( &::CloseHandle )> FileHandle;
// Open file for querying only (no read/write access).
FileHandle h( ::CreateFileW( a_Link.c_str(), 0,
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr ),
&::CloseHandle );
if ( h.get() == INVALID_HANDLE_VALUE ) {
h.release();
throw std::runtime_error( "CreateFileW() failed." );
}
const size_t requiredSize = ::GetFinalPathNameByHandleW( h.get(), nullptr, 0,
FILE_NAME_NORMALIZED );
if ( requiredSize == 0 ) {
throw std::runtime_error( "GetFinalPathNameByHandleW() failed." );
}
std::vector<wchar_t> buffer( requiredSize );
::GetFinalPathNameByHandleW( h.get(), buffer.data(),
static_cast<DWORD>( buffer.size() ),
FILE_NAME_NORMALIZED );
return std::wstring( buffer.begin(), buffer.end() - 1 );
}
Note: For details about the RAII wrapper based on std::unique_ptr see std::unique_ptr, deleters and the Win32 API.

Related

how to create own cache for file loading from disk to memory in c++ framwork?

how to create own cache for file loading from disk to memory in c++ framwork,I do not want to use windows cache ,because some of case windows cache does not gives the good results ?
Is there any plugin in c++ I can directly use for multiImport of file from disk to memory .
thanks in advance
What do you mean by "windows cache does not gives the good results"? What version of Windows are we talking about here?
The Windows file cache is actually quite efficient, but there are things a developer can do to their own data (if they fully control it) which can greatly improve the performance of file I/O. In particular, if you ensure your files are organized into multiples of 4096 bytes (aka 4k), you can make use of "overlapped" I/O which avoids both blocking behavior and the need to do additional copies of memory data.
An example of this is the DirectX Tool Kit and the WaveBankReader class. The xwbtool command-line utility is used to pack a number of audio .wav files into a single file where each individual sound file is aligned to the 4096 boundary, a xwb file.
At runtime, the xwb reader then sets up the target memory and issues asynchronous reads. Ideally the application sets up a number of other reads, and then at some later time ensures that all async I/O is complete before using the data.
struct handle_closer { void operator()(HANDLE h) { if (h) CloseHandle(h); } };
typedef public std::unique_ptr<void, handle_closer> ScopedHandle;
inline HANDLE safe_handle( HANDLE h ) { return (h == INVALID_HANDLE_VALUE) ? 0 : h; }
ScopedHandle m_event;
#if (_WIN32_WINNT >= _WIN32_WINNT_VISTA)
m_event.reset( CreateEventEx( nullptr, nullptr, CREATE_EVENT_MANUAL_RESET, EVENT_MODIFY_STATE | SYNCHRONIZE ) );
#else
m_event.reset( CreateEvent( nullptr, TRUE, FALSE, nullptr ) );
#endif
if ( !m_event )
{
return HRESULT_FROM_WIN32( GetLastError() );
}
#if (_WIN32_WINNT >= _WIN32_WINNT_WIN8)
CREATEFILE2_EXTENDED_PARAMETERS params = { sizeof(CREATEFILE2_EXTENDED_PARAMETERS), 0 };
params.dwFileAttributes = FILE_ATTRIBUTE_NORMAL;
params.dwFileFlags = FILE_FLAG_OVERLAPPED | FILE_FLAG_SEQUENTIAL_SCAN;
ScopedHandle hFile( safe_handle( CreateFile2( szFileName,
GENERIC_READ,
FILE_SHARE_READ,
OPEN_EXISTING,
&params ) ) );
#else
ScopedHandle hFile( safe_handle( CreateFileW( szFileName,
GENERIC_READ,
FILE_SHARE_READ,
nullptr,
OPEN_EXISTING,
FILE_FLAG_OVERLAPPED | FILE_FLAG_SEQUENTIAL_SCAN,
nullptr ) ) );
#endif
if ( !hFile )
{
return HRESULT_FROM_WIN32( GetLastError() );
}
// Read and verify header
OVERLAPPED request;
memset( &request, 0, sizeof(request) );
request.hEvent = m_event.get();
bool wait = false;
if( !ReadFile( hFile.get(), &m_header, sizeof( m_header ), nullptr, &request ) )
{
DWORD error = GetLastError();
if ( error != ERROR_IO_PENDING )
return HRESULT_FROM_WIN32( error );
wait = true;
}
DWORD bytes;
#if (_WIN32_WINNT >= _WIN32_WINNT_WIN8)
BOOL result = GetOverlappedResultEx( hFile.get(), &request, &bytes, INFINITE, FALSE );
#else
if ( wait )
(void)WaitForSingleObject( m_event.get(), INFINITE );
BOOL result = GetOverlappedResult( hFile.get(), &request, &bytes, FALSE );
#endif
if ( !result || ( bytes != sizeof( m_header ) ) )
{
return HRESULT_FROM_WIN32( GetLastError() );
}
// ... code here to verify and parse header cut for readability ...
m_waveData.reset( new (std::nothrow) uint8_t[ waveLen ] );
if ( !m_waveData )
return E_OUTOFMEMORY;
dest = m_waveData.get();
memset( &m_request, 0, sizeof(OVERLAPPED) );
m_request.Offset = m_header.Segments[HEADER::SEGIDX_ENTRYWAVEDATA].dwOffset;
m_request.hEvent = m_event.get();
if ( !ReadFile( hFile.get(), dest, waveLen, nullptr, &m_request ) )
{
DWORD error = GetLastError();
if ( error != ERROR_IO_PENDING )
return HRESULT_FROM_WIN32( error );
}
else
{
m_prepared = true;
memset( &m_request, 0, sizeof(OVERLAPPED) );
}
// ...
// At some later point we need to check to see if the data is ready
// or wait if the data is not yet ready
if ( !m_prepared )
{
WaitForSingleObjectEx( m_request.hEvent, INFINITE, FALSE );
m_prepared = true;
}
This code makes use of the buffering hint FILE_FLAG_SEQUENTIAL_SCAN that the file will be read sequentially. You can also use the hint FILE_FLAG_RANDOM_ACCESS if the file will really be access randomly instead, but it is more efficient if you can arrange your data for a sequential scan.
The complexity here is that this code builds for Windows Vista, Windows 7, Windows 8.x, Windows 10, Xbox One, Windows phone 8, Windows 8 Store, and universal Windows apps. Namely I'm using the improved GetOverlappedResultEx on Windows 8 or later, but have to emulate it on older versions of the OS with WaitForSingleObject and GetOverlappedResult.
Having a few dozen outstanding read-requests of a reasonable size can also help optimize disk seek behavior but it is important not to flood the system with lots of small requests. Generally prefer to make read requests of 32k or greater at a time.
If you want to bypass the file cache for some reason (say you are doing streaming of audio and don't want any extra copies of it in memory anywhere because you know the data will only get used once before you read it again) you can use FILE_FLAG_NO_BUFFERING--be sure you aren't opening another handle to the same file without this flag or it will get buffered anyhow:
#if (_WIN32_WINNT >= _WIN32_WINNT_WIN8)
CREATEFILE2_EXTENDED_PARAMETERS params2 = { sizeof(CREATEFILE2_EXTENDED_PARAMETERS), 0 };
params2.dwFileAttributes = FILE_ATTRIBUTE_NORMAL;
params2.dwFileFlags = FILE_FLAG_OVERLAPPED | FILE_FLAG_NO_BUFFERING;
m_async = CreateFile2( szFileName,
GENERIC_READ,
FILE_SHARE_READ,
OPEN_EXISTING,
&params2 );
#else
m_async = CreateFileW( szFileName,
GENERIC_READ,
FILE_SHARE_READ,
nullptr,
OPEN_EXISTING,
FILE_FLAG_OVERLAPPED | FILE_FLAG_NO_BUFFERING,
nullptr );
#endif
As with all optimizations, be sure to profile both with and without FILE_FLAG_NO_BUFFERING under real world loads to make sure you aren't actually making things slower by using it.

CreateFile failing with errorcode 2, while file exists

I try to open existing file via CreateFile, but it is always failing with errorcode 2 - like file doesn't exist, but it exists - it's in folder with executable.
hFile = CreateFile( argv[ 1 ], GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL );
if ( hFile == INVALID_HANDLE_VALUE )
{
printf( "\nError: Unable to open file (%d)\n", GetLastError( ) );
return -1;
}
it fails even if I replace argv[1] with hardcoded filename string.
app is running as an admin.
The error code is accurate. The file cannot be found. Possible explanations include:
You used the wrong file name.
You used a relative path and the process working directory is not what you expect it to be.
If you wish to interpret the file name as being relative to the directory in which the executable resides then do that. Form an absolute path from the directory containing the executable and the specified file name.
There is no reason to expect a process working directory to be the directory in which the executable resides.
You are attempting to open a file using a relative pathname. Relative pathnames are relative to the current working directory (see GetCurrentDirectory). The current working directory is not necessarily the directory, where the executable image resides. It can be different for a number of reasons, for example:
The application was launched through a shortcut that explicitly sets the working directory.
The application called SetCurrentDirectory.
The application was launched through the command interpreter from a directory other than the executable's directory.
If you want to open a file located relative to the application's executable image, you need to construct a fully qualified pathname, based on the executable's location and the desired filename. The following code retrieves the executable's directory1):
#include <windows.h>
#include <Shlwapi.h>
#pragma comment(lib, "Shlwapi.lib")
#include <string>
#include <vector>
std::wstring GetExePath() {
// Retrieve fully qualified module pathname
std::vector<wchar_t> buffer( MAX_PATH );
DWORD cbSize = ::GetModuleFileNameW( nullptr, buffer.data(),
static_cast<DWORD>( buffer.size() ) );
while ( cbSize == buffer.size() ) {
buffer.resize( buffer.size() + MAX_PATH );
cbSize = ::GetModuleFileNameW( nullptr, buffer.data(),
static_cast<DWORD>( buffer.size() ) );
}
if ( cbSize == 0 ) {
throw ::GetLastError();
}
// Remove filename from fully qualified pathname
if ( ::PathRemoveFileSpecW( buffer.data() ) ) {
::PathAddBackslashW( buffer.data() );
}
// Construct string object from character buffer
std::wstring str( &buffer[0] );
return str;
}
This can be used as follows:
int wmain( int argc, const wchar_t* argv[] ) {
if ( argc <= 1 ) {
return -1;
}
std::wstring pathname = GetExePath();
pathname += argv[1];
HANDLE hFile = ::CreateFileW( pathname.c_str(), GENERIC_READ,
FILE_SHARE_READ | FILE_SHARE_WRITE,
NULL, OPEN_EXISTING, 0, NULL );
if ( hFile == INVALID_HANDLE_VALUE )
{
wprintf( L"\nError: Unable to open file (%d)\n", GetLastError() );
return -1;
}
// ...
::CloseHandle( hFile );
return 0;
}
1) Code targeting Windows 8 and later should use PathCchRemoveFileSpec and PathCchAddBackslash instead.

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"

CreateProcess( ) doesn't create a new window with CREATE_NEW_CONSOLE flag - C/C++

I've been trying to create a process with CreateProcess() using the Windows API of course. I haven't been able to create a new console for some reason even after scouring the web.
Reasearch I've Done :
I used the MSDN example code as a base for the parameters I should use in the function :
http://msdn.microsoft.com/en-us/library/windows/desktop/ms682512%28v=vs.85%29.aspx
I read the following MSDN article for information on how you should create new console windows :
http://msdn.microsoft.com/en-us/library/windows/desktop/ms682528%28v=vs.85%29.aspx
I also read a similar SO problem to mine about someone having the same problem :
CreateProcess does not create additional console windows under Windows 7?
Results :
I've written the code I will post below with all the requirements needed to create a new console, but it doesn't behave as expected. I've spent a long time trying to find the answer on my own, but the articles above were the only relevant ones I could find through google. What happens is that the process is created, but it is inside my C program's console. I want to be able to create the process without it inherting my program's console.
There are also other discrepancies as well. If I print lots of characters in my do-while loop without a Sleep() to slow it down, TerminateProcess() will fail with Access Denied and the program will crash when I press the escape key. This is also not desired behavior.
Here is the C program that I have right now :
#include <stdio.h>
#include <time.h>
#include <stdlib.h>
#define WIN32_LEAN_AND_MEAN
#include <process.h>
#include <windows.h>
#define IS_PRESSED( vk ) ( GetAsyncKeyState( vk ) & 0x8000 )
typedef struct process
{
PROCESS_INFORMATION p_info;
STARTUPINFO s_info;
} process;
void win_error( char * message, int is_exit )
{
char buffer[BUFSIZ] = { 0 };
DWORD error_code = GetLastError( );
FormatMessage
(
FORMAT_MESSAGE_FROM_SYSTEM,
NULL,
error_code,
MAKELANGID( LANG_NEUTRAL, SUBLANG_DEFAULT ),
( LPTSTR ) buffer,
BUFSIZ,
NULL
);
MessageBox( NULL, buffer, message, MB_ICONWARNING | MB_OK );
if ( is_exit ) exit( error_code );
return;
}
int create_process( process * p, const char * exe_path, const char * cmd_line_args )
{
p->s_info.cb = sizeof( STARTUPINFO );
p->s_info.dwFlags |= CREATE_NEW_CONSOLE;
return CreateProcess(
exe_path,
( LPSTR )cmd_line_args,
NULL,
NULL,
FALSE,
0,
NULL,
NULL,
&p->s_info,
&p->p_info
);
}
int main( )
{
process p = { { 0 }, { 0 } };
srand( time( NULL ) );
if ( !create_process( &p, "J:\\C programs and compiliers\\C\\WindowsTest\\bin\\Debug\\matrix.bat", NULL ) )
win_error( "CreateProcess", 1 );
CloseHandle( p.p_info.hThread );
do
{
if ( IS_PRESSED( VK_ESCAPE ) )
if ( !TerminateProcess( p.p_info.hProcess, 0 ) )
win_error( "TerminateProcess", 0 );
Sleep( 50 );
} while ( WaitForSingleObject( p.p_info.hProcess, 0 ) != WAIT_OBJECT_0 );
CloseHandle( p.p_info.hProcess );
return 0;
}
Here is the Batch program I'm calling :
#echo off
setlocal enabledelayedexpansion
:start
echo Hello PSAPI on Windows...
pause >nul
exit
I'm expecting someone will know how to mess with processes more than I do. This is my first time using the CreateProcess() function. Yes, I am aware of ShellExecute(). I am also aware that my Batch file isn't a matrix, but I wanted to start simple.
CREATE_NEW_CONSOLE is a flag of CreateProcess() itself, not of STARTUPINFO. You are putting the flag in the wrong place. Try this instead:
int create_process( process * p, const char * exe_path, const char * cmd_line_args )
{
...
return CreateProcessA(
exe_path,
cmd_line_args,
NULL,
NULL,
FALSE,
CREATE_NEW_CONSOLE, // <-- here
NULL,
NULL,
&p->s_info,
&p->p_info
);
}
Also, keep in mind that a STARTUPINFOEX can be passed to CreateProcess(), so your create_process() function should not be forcing p->s_info.cb, that should be the caller's responsibility depending on whether a STARTUPINFO or a STARTUPINFOEX is being used.

Faster way to read file than boost::file_mapping?

I'm writing a latency-sensitive app which reads a text file upon initialisation. I have profiled and re-written all my algorithms such that 85% of my execution time is from the lines:
boost::interprocess::file_mapping file(Path, read_only);
boost::interprocess::mapped_region data(file, read_only);
I am writing this on windows- is there any faster way to map a file into memory? Portability is not a concern.
You could just use the native functions of Win32, but I think you won't save alot, because boost will not add alot of overhead:
OFSTRUCT ofStruct;
ofStruct.cBytes=sizeof (OFSTRUCT);
HANDLE file=(HANDLE)OpenFile(fileName, &ofStruct, OF_READ);
if (file==INVALID_HANDLE_VALUE)
handle errors
else {
HANDLE map=CreateFileMapping(file, NULL, PAGE_READONLY, 0, 0, 0);
if (map==INVALID_HANDLE_VALUE)
handle errors
else {
const char *p=(const char *)MapViewOfFile(map, FILE_MAP_READ, 0, 0, 0));
if (p) {
// enjoy using p to read access file contents.
}
// close all that handles now...
}
I would suggest dropping the idea of file mapping.
FM is a complicated construct and adds some overhead. Plain cached read also involves non-trivial interaction with the physical device. You could do unbuffered reads. Probably the next thing to ask is what kind of IO you actually want - how big is the file? is it sequential? Is it on the network? Do you have a choice of hardware, or is it on the customers' machine?
If the files are small, just open and read them into memory using standard Win32 CreateFile()/ReadFile() APIs.
If you're consuming each file sequentially (or could arrange your code in such a way that you do), you should specify FILE_FLAG_SEQUENTIAL_SCAN. This is a hint for the file/caching subsystem to read-ahead aggressively. For small files, the file might be read into cache before your first call to ReadFile() is issued.
Edit: As requested, Here's a snippet that illustrates reading the contents of a file into a vector of bytes using the Win32 API:
void ReadFileIntoBuffer( const std::wstring& fileName, std::vector< uint8_t >& output )
{
HANDLE hFile( INVALID_HANDLE_VALUE );
try
{
// Open the file.
hFile = CreateFile( filename.c_str(),
GENERIC_READ,
FILE_SHARE_READ,
NULL,
OPEN_EXISTING,
FILE_FLAG_SEQUENTIAL_SCAN,
NULL );
if( INVALID_HANDLE_VALUE != hFile )
throw std::runtime_error( "Failed to open file." );
// Fetch size
LARGE_INTEGER fileSize;
if( !GetFileSizeEx( hFile, &fileSize ) );
throw std::runtime_error( "GetFileSizeEx() failed." );
// Resize output buffer.
output.resize( fileSize.LowPart );
// Read the file contents.
ULONG bytesRead;
if( !ReadFile( hFile, &output[0], fileSize.LowPart, &bytesRead, NULL ) )
throw std::runtime_error( "ReadFile() failed." );
// Recover resources.
CloseHandle( hFile );
}
catch( std::exception& )
{
// Dump the error.
std::cout << e.what() << " GetLastError() = " << GetLastError() << std::endl;
// Recover resources.
if( INVALID_HANDLE_VALUE != hFile )
CloseHandle( hFile );
throw;
}
}