Get executable path with AssocQueryString in C++ - 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.

Related

Copying a file from C:\Windows\System32 folder to C:\Windows\SysWOW64 folder using Fortran and/or C++

I am trying to copy a file from C:\Windows\System32 folder to C:\Windows\SysWOW64 folder using Fortran and/or C++ code(s).
Fortran code:
call system ('copy C:\Windows\System32\filename.extension C:\Windows\SysWOW64\filename.extension')
end
C++ code:
#include <iostream>
#include<stdlib.h>
#include<string.h>
using namespace std;
int main() {
system('copy C:\Windows\System32\filename.extension C:\Windows\SysWOW64\filename.extension');
return 0;
}
Execution of the codes above returns an error as follows:
The system cannot find the file specified.
When I enter
copy C:\Windows\System32\filename.extension C:\Windows\SysWOW64\filename.extension
on the Command Prompt in Administrator mode, it works fine and returns
1 file(s) copied.
Any idea how can I copy a file from C:\Windows\System32 folder to C:\Windows\SysWOW64 folder using Fortran and/or C++ programming languages?
Thank you very much in advance for your time and help in this matter,
Bakbergen
On Windows, there's an API called CopyFile. Example in C++:
std::wstring source = L"C:\\Windows\\System32\\filename.extension";
std::wstring dest = L"C:\\Windows\\SysWOW64\\filename.extension";
BOOL result = CopyFileW(source.c_str(), dest.c_str(), TRUE);
DWORD dwLastError = result : 0 : GetLastError();
The above code will work just fine when compiled as a 64-bit executable and run on 64-bit Windows with administrator priveleges. However be advised:
The SysWow64 folder doesn't exist on 32-bit Windows.
On 64-bit Windows, if your code is compiled as 32-bit, it won't see the SysWow64 folder. That's because it's already been mapped as the System32 folder. You should read up on the File System Redirector here
Needs admin privs to run. App compatibility in Windows might redirect the file copy operation to a private per-app or per-user folder anyway.
Don't hardcode these paths. Use APIs such as GetSystemWow64Directory and GetSystemDirectory
You really shouldn't be able to mucking with files in the Windows System folders anyway. This is reserved for the Operating System. No one should be putting stuff here - as that creates application compatibility and versioning issues. I know it's an easy way to get EXEs and DLLs in "the path" so they load easier, but it's completely the wrong way to do it.

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.

How to read a file from installed folder 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);

C++ LoadLibrary() from the current path

I'm a .net developer mostly, doing something small in C++ so im having a little trouble.
I'm loading another C++ dll using hInst = LoadLibrary(TEXT("mydll.dll")); and I came to realize that this looks for the dll I'm trying to load in 'C:\' or in 'system32'.
Can someone show me how to load the dll from the current directory (without knowing what the current directory is ahead of time) ??
I know I should be using something like GetFullPathName but I'm having a little trouble getting it to work on my own...
The process current directory can be acquired using the function GetCurrentDirectory():
TCHAR bufCurrentDirectory[MAX_PATH + 1] = { 0 };
DWORD dwNumCharacters = ::GetCurrentDirectory(MAX_PATH, bufCurrentDirectory);
if(dwNumCharacters == 0) error();
Now the path to the process's current directory is in bufCurrentDirectory, unless the function failed. Just append "\\mydll.dll" onto the path, and you have an absolute path to the DLL.
Since the current directory of a process is somewhat variable, it may be better to use the path of the executable. To get the path of the current executable, you can do:
TCHAR bufExePath[MAX_PATH + 1] = { 0 };
DWORD dwNumCharacters = ::GetModuleFileName(NULL, bufExePath, MAX_PATH);
if(dwNumCharacters == 0) error();
Now bufExePath contains the entire path to the exe, including "\\my.exe". Replace my.exe with mydll.dll, and you'll have the absolute path to the DLL. I would recommend this method over the GetCurrentDirectory() method due to security concerns.
I believe loading from current directory is the default and first search. See the MSDN reference
The first directory searched is the
directory containing the image file
used to create the calling process
(for more information, see the
CreateProcess function). Doing this
allows private dynamic-link library
(DLL) files associated with a process
to be found without adding the
process's installed directory to the
PATH environment variable
http://msdn.microsoft.com/en-us/library/ms684175(v=vs.85).aspx
Use GetCurrentDirectory.

Application path location in MS windows

Where should I store the application installation folder for application to reffer ?
I believe what you're looking for is the GetModuleFileName function, which you can use like so:
// get the full path to the current executable
wchar_t fullPath[MAX_PATH];
GetModuleFileName(NULL, fullPath, MAX_PATH);
// cut the string at the last slash, because we just want the directory
// not the actual executeable file name.
TCHAR *lastSlash = wcsrchr(fullPath, L'\\');
lastSlash = 0;
// directory where the executable is location is fullPath
This will work regardless of the current working directory and in general, this is how I do all "relative path" stuff (that is, I never actually use relative paths, they're always absolute paths, based off the value returned from the function above).
You can use the registry for storing installation folders.
::GetModuleFileName(NULL, out_lpPathName, in_size) will give you full path of your executable file.
ApplicationData folder, even not admin access can help to access you install data. use SHGetSpecialFolderPath with CISDL CSIDL_APPDATA
In the registry. To be specific, under HKEY_LOCAL_MACHINE\SOFTWARE\YourCompanyName\YourApplicationName\ if you installed your app for all users (i.e. in \Program Files), and under HKEY_CURRENT_USER\SOFTWARE\YourCompanyName\YourApplicationName\ if you installed it for a single user only.