How to read a file from installed folder c/c++ - c++

I'm using Visual Studio and I have a code that uses several files and made an installer for it. Everything works fine if the user installs the program to the directory that is mentioned in the code (c:\games).
For example:
FILE *myfile;
if ((myfile = fopen("c:\\games\\heretics.txt", "r")) == NULL) {
printf("Cannot Open File heretics.txt\n"); return -1;
}
while ( (fgets(p, 256, myfile)) != NULL ) {
printf("%s",p);
}
fclose(myfile);
It stops working if the user installs it to any other directory. How to make the program read the files from any directory or what could be the solution for this?

If a "heretics.txt" file is in directory of your program (or somewhere near in directory tree) you can just write something like:
fopen("heretics.txt", "r") // to open file from current directory
fopen("..\\heretics.txt", "r") // to open file from parent directory
// and so on ;)
to use relative path instead of absolute path (as mentioned in comment above).

If you want to read files from the directory that the application's EXE file is in, you will need to determine that directory at run-time. Don't hard-code paths into your source code, or rely on the working directory!
To do that in Windows, you can call the GetModuleFileName function, which will return the fully-qualified path to the EXE file. Then you need to strip the EXE file name from the path (e.g. using PathRemoveFileSpec), to give you the directory.
#include <Windows.h>
#include <shlwapi.h>
#pragma comment(lib, "shlwapi.lib")
TCHAR szPath[MAX_PATH];
GetModuleFileName(NULL, szPath, MAX_PATH);
PathRemoveFileSpec(szPath);
You can then append your file name to the path like so:
PathAppend(szPath, TEXT("heretics.txt"));
And then you can pass szPath as the fully-qualified path to fopen or whatever function you're using to open the file.
But...
You should consider carefully whether the application's directory is the best place to store these files.
If either the application itself is going to write to the files (e.g., update them with new changes), or if the user might want to edit them, the application's directory is a terrible place. By default, applications get installed into the "Program Files" directory, for which users do not have edit privileges. Same thing with your example, at the root level of the disk.
Instead, you should save the files from one of the "Application Data" folders. You have the following choices:
FOLDERID_ProgramData (a global program data directory available to all users)
FOLDERID_LocalAppData (a per-user program data directory, non-roaming)
FOLDERID_RoamingAppData (a per-user program data directory, roaming)
Those magic names are the KNOWNFOLDERID constants that you pass to the SHGetKnownFolderPath function to retrieve the path to one of these folders. For example:
#include <Windows.h>
#include <ShlObj.h>
#pragma comment (lib, "shell32.lib")
// Retrieve the path.
PWSTR pszPath;
HRESULT hr = SHGetKnownFolderPath(&FOLDERID_ProgramData,
0, // no special options required
NULL, // no access token required
&pszPath);
// Copy it into a local string.
TCHAR szPath[MAX_PATH];
lstrcpy(szPath, pszPath);
// Free the memory allocated by the SHGetKnownFolderPath function,
// so you don't accidentally leak it!
CoTaskMemFree(pszPath);

Related

Visual Studio C++ How to Specify Relative Resource Path For Final Build

I already know how to set a relative working directory path and access resources inside of visual studio. However, when I build my solution and move the exe to a separate file I have to include all the resources in the same directory as the exe. I'd prefer to have a folder with said resources alongside the exe to keep things clean. Is there a macro I need to use? Is the working directory the right setting to change for this?
example
Sorry for this incredibly basic question, but I think I lack the vocabulary to accurately describe the issue.
For a Windows solution, GetModuleFileName to find the exact path of your EXE. Then a simple string manipulation to make a resource path string.
When you program starts, you can use this to ascertain the full path of your EXE.
std::string pathToExe(MAX_PATH, '\0');
GetModuleFileName(nullptr, szPathToExe.data(), MAX_PATH); // #include <windows.h> to get this function declared
On return, szPathToExe will be something like "C:\\Path\\To\\Your\\Program\\Circle.exe"
Then strip off the file name to get the absolute path
std::string resourceFolderPath = szPathToExe;
size_t pos = strPath.find_last_of("\\");
strPath = strPath.substr(0, pos+1); //+1 to include the backslash
Then append your resource folder name and optionally another trailing backslash.
resourceFolderPath += "res\\";
Then change all your I/O calls to open files relative to resourceFolderPath
You should probably add reasonable error checking to the above code such as validating the return code from GetModuleFileName and find_last_of calls.

Get executable path with AssocQueryString in C++

Helo! I need to query application full path in C++, like "meshlab" -> "C:\Program Files\VCG\MeshLab\meshlab.exe"
This information is present in HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\App Paths, but I don't want to work with registry directly, so I am using AssocQueryString like this:
#include "pch.h"
#include <iostream>
#include <Windows.h>
#include <Shlwapi.h>
int main()
{
char* executablePath = nullptr;
DWORD executablePathLen = 0;
std::string shortName = "mspaint";
HRESULT res = AssocQueryStringA(ASSOCF_OPEN_BYEXENAME,
ASSOCSTR_EXECUTABLE,
shortName.c_str(),
NULL,
executablePath,
&executablePathLen);
executablePath = new char[executablePathLen];
res = AssocQueryStringA(ASSOCF_OPEN_BYEXENAME,
ASSOCSTR_EXECUTABLE,
shortName.c_str(),
NULL,
executablePath,
&executablePathLen);
std::cout << executablePath; // prints: C:\Windows\system32\mspaint.exe
delete[] executablePath;
std::cin.get();
}
For mspaint it works as expected, but for meshlab it doesn't. HRESULT is ERROR_NO_ASSOCIATION
Any ideas what I missed?
UPDATE:
Also works well with foobar200 from C:\Program Files (x86)\foobar2000\foobar2000.exe
I suspect it must be related to 32/64 bit registry. I am using Windows 10 64 bit, and my application is 64 bit
I believe ASSOCF_OPEN_BYEXENAME:ASSOCSTR_EXECUTABLE searches HKEY_CLASSES_ROOT\Applications.
ShellExecuteEx searches the "normal directories" and the App Paths key:
Finding an Application Executable
When the ShellExecuteEx function is called with the name of an executable file in its lpFile parameter, there are several places where the function looks for the file. We recommend registering your application in the App Paths registry subkey. Doing so avoids the need for applications to modify the system PATH environment variable.
The file is sought in the following locations:
The current working directory.
The Windows directory only (no subdirectories are searched).
The Windows\System32 directory.
Directories listed in the PATH environment variable.
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\App Paths
(HKEY_CURRENT_USER is also used on recent versions of Windows)
The Assoc API is more focused on file associations.
If you are just going to execute the file then you should just use ShellExecuteEx. If you just want to find the fully qualified path you can use some helper functions to do some of the work for you:
FindExecutable seems promising but it has some compatibility workarounds and it will also find the executable used to open registered types (c:\foo\bar.txt might resolve to c:\Windows\Notepad.exe etc. because it uses ASSOCSTR_EXECUTABLE),
If you want to look for extensions like .exe and .bat automatically you can use PathResolve(..., PRF_REQUIREABSOLUTE|PRF_VERIFYEXISTS|PRF_TRYPROGRAMEXTENSIONS|PRF_DONTFINDLNK) but you must be careful that the search string does not contain \ nor :.
If you only want to look for a .exe you must manually append the extension if it is not present and then call PathFindOnPath(, NULL).
Neither of these functions looks in the App Paths key, they do not use the exact same search order as ShellExecuteEx (system32 before %windir%?) and they are most likely limited to MAX_PATH.
If those functions (or your own equivalent version) fail then you can check the App Paths key. When reading from the registry you must append .exe if there is no extension and use a helper function like SHQueryValueEx that will expand REG_EXPAND_SZ strings.

Open an .exe file without knowing the full path in C++

I want to create a program that opens another .exe file in C++, I want the program to be able to run from any directory, so I will not know the full address of the file.
Eg-
The Program that I make will be present in a folder, which in turn has subfolders, which further contains javaportable.exe. I want the program to run the above mentioned .exe without getting the full path. Something like cd operator in normal DOS navigation. Also, if it is possible, I want to select another .exe, which will run through javaportable.exe, and is present in the previous folder.
First of all the standard C++ library does not provide any such functions to list the files of a directory. You can use boost libraries to get this done. Solution to this problem is not that easy. You will have to implement a lot of techniques to get this done. I can give you all the possible starting pointers.
To port this code to other OS you may need to add OS specific code using # preprocessor directives. Boost Liberay is already cross platform.
First of all you need to get the current path of the directory your program is present in:
For this you can use the following windows os specific code
#include <iostream>
#include <direct.h>
int main(){
char *path = NULL;
path = _getcwd(NULL, 0); // or _getcwd
if (path != NULL)
std::cout << path;
}
Here the path variable contains the address of the current directory. You can pass this path to the next function which will further use it to list the files and directories for you. For listing the directories you can use Boost Liberaries http://www.boost.org/
Next you will need to get all the files and folders present in current directory. Use Boost library for this
Use this sample code from boost
http://www.boost.org/doc/libs/1_37_0/libs/filesystem/example/simple_ls.cpp
Then make a class and store the address received and files names with path in its objects. You can store all the listed address in a object like dir[1].str[size], dir[2].str[size],...so on.
Now again pass all the folder addresses you received to boost function and get further filenames. All of the above will require many passes.
You can also get the list of files for specific file extension too:
#define BOOST_FILESYSTEM_VERSION 3
#define BOOST_FILESYSTEM_NO_DEPRECATED
#include <boost/filesystem.hpp>
namespace fs = ::boost::filesystem;
// return the filenames of all files that have the specified extension
// in the specified directory and all subdirectories
void get_all(const fs::path& root, const string& ext, vector<fs::path>& ret)
{
if(!fs::exists(root) || !fs::is_directory(root)) return;
fs::recursive_directory_iterator it(root);
fs::recursive_directory_iterator endit;
while(it != endit)
{
if(fs::is_regular_file(*it) && it->path().extension() == ext) ret.push_back(it->path().filename());
++it;
}
}
Finally compare the filenames with the one you want to run and execute it.
The problem can be solved by many other methods too but I think it will be good to start and you can always improve your code.
References:
How to get list of files with a specific extension in a given folder
http://www.boost.org/
How do I get a list of files in a directory in C++?
Hope this helps. If you need any further help feel free to ask!

Opening a file in the current directory

I'm trying to load files, and previously I was using hardcoded file locations, (like "c:\location\file.txt") but now that a few friends are also using the file, I'd like to allow them to put the executable wherever they want.
my current code looks like:
ifstream myfile;
myfile.open("c:\\client\\settings.cfg");
I'm trying to change it so that the user puts their executable into whatever folder they want, and then they create a folder and put their settings file into it and the exe will load that with their settings.
ifstream myfile;
myfile.open("\\settings\\settings.cfg");
I have some basic error handling in place, and now the program always errors out saying that it can't find the file.
The file structure looks like this:
[ART]
asset.png
[SETTINGS]
settings.cfg
client.exe
This seems like a really simple thing to do, but I can't find any way to do it. Every example and tutorial about reading and writing to files deals only with files in the executable's directory, or hardcoded into c:\folder...
Could anyone point me to how I do this?
The search path for most systems starts with the current working directory and then to a PATH environment variable. So, all you need to do is specify the file/folder without the absolute path markings and it will use the path relative to the working directory:
ifstream myfile;
myfile.open("settings\\settings.cfg");
// ^^ Note the lack of \\ to start the file path
Paths beginning with \ are always relative to the current drive's root directory. If the current drive is C:, then \settings\settings.cfg means C:\settings\settings.cfg.
Note that you can use / in order to avoid escaping everything. So you can use: settings/settings.cfg. This will be relative to the user's current directory. Note however, that this doesn't necessarily correspond to the directory where the executable resides. If you need the directory of the executable, then you need to use a Windows API function to get it:
#include <Windows.h>
// ...
HMODULE module = GetModuleHandleW(NULL);
WCHAR path[MAX_PATH];
GetModuleFileNameW(module, path, MAX_PATH);
Now if you want to open settings/settings.cfg relative to the directory of the executable, create a path that starts with path and append /settings/settings.cfg to it.

exe does not find a text file in the same directory

I am running an exe that reads text from a file, which is saved in the same directory as the exe.
I am using the _tfopen function, that returns errno 2, which means that the file is missing.
As is already said, the text file is in the same directory as the exe, so Im using only filename of the text file and not the whole path (even though ive tried to use the absolute filepath...).
I am running the exe from a different computer, not the one I am developing on (The release version).
Another application that using the same file, also in the same directory, works perfectly.
Why does the exe cant find the text file?
FILE* fileData;
if ((fileData = _tfopen(_T("Data.txt"), _T("r"))) == NULL)
return false;
You might want to try this: have your program call GetCurrentDirectory() and output the result somewhere to that you can confirm that the program's current directory is truly what you think it is. You could use OutputDebugString() if you are running under a debugger or MessageBox() if not.
When I need to be sure I'm opening a co-located file, I call GetModuleFileName( NULL, szExeFqn, sizeof(szExeFqn)) to get the FQN of the EXE file, strip off the EXE file name and add on the name of the file I need. That way, I'm not making any assumptions that might be false -- after all, it is trivial to have a shortcut set the default directory for a program different from the directory containing the EXE file.
Also, if you run your program from a command line by entering the full path to your program, then the program's current directory is the one you were in when you ran it, not the one where the EXE was found. That's also true if your program is found by searching the PATH environment variable (yes, it still exists in Windows.)
Here's an example of what I've used for more than a decade to do what you've described:
char szHelpFileName[_MAX_FNAME];
char *cp;
GetModuleFileName( NULL, szHelpFileName, sizeof(szHelpFileName) );
cp = strrchr( szHelpFileName, '\\' );
if( cp )
{
cp++; // Point to the char just following the final slash.
}
else
{ // No backslash, is there a colon?
cp = strrchr( szHelpFileName, ':' );
if( cp )
{
cp++; // Point to the char just following the colon.
}
else
{ // I give up. I'll have no path information.
cp = szHelpFileName;
}
}
strcpy( cp, "program.hlp" );
The final result is the name of a help file that is co-located with the EXE. Unless the help file is missing (or its permissions have been scrogged somehow) this always works. I've never had it follow the "paranoia" path where the comment says "I give up."
Usually operating systems open files with relative address based on the current directory, rather than the executable location.
So for example if your executable is in /path/to/exec/executable and you invoke it from /path/to/invoke, it will try to open Data.txt as if it was /path/to/invoke/Data.txt.
You can take a look at this question and its answers to find out how to find the path to executable in Windows. Or this answer for various other operating systems.
It should work even without specifying the full path. Do you launch the executable from Visual Studio? If yes, make sure that the Data.txt file is inside the project directory where the *.vcxproj file or the *.sln file is.