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.