I have a program that is executed by another program. The program that is being executed needs files located at its own location [same folder]. If I call myfile.open("xpo.dll") I might get an error because I am not passing the [fullpath + name + extension]. The program that is being executed can vary paths depending on the installation path. Therefore, I was wondering if there is a way to get the application path [where the application is located] and set it so that when another program executes from another path everything might work properly...?
[Using C++ without .NET Framework]
Thanks.
Use GetModuleFileName and pass NULL for hModule.
DWORD GetModuleFileName(
HMODULE hModule, // handle to module
LPTSTR lpFilename, // path buffer
DWORD nSize // size of buffer
);
First off, I run into this problem in other languages a lot, and find Process Monitor (http://technet.microsoft.com/en-us/sysinternals/bb896645.aspx) very useful for finding out what folder it is currently trying to access.
There is no standard function for doing this.
Just a thought, have you tried doing myfile.open "./xpo.dll"?
If it's a console application, you can use the POSIX getcwd function: http://www.dreamincode.net/code/snippet77.htm
If it's a Windows app and you can use the windows API, you can use GetModuleFileName... see the second reply to this question here: How do I get the directory that a program is running from?
Related
I am trying to open sigverif.exe from my code in c++ but the return value is 2 and .exe does not open
ShellExecute(NULL, _T("open"), _T("C:\\Windows\\System32\\sigverif.exe"), NULL, NULL, SW_RESTORE);
If I open the sigverif.exe from run command typing
"C:\Windows\system32\sigverif.exe"
it works fine
What could be the issue?
the return value is 2 and .exe does not open
The return value based on System Error Codes means ERROR_FILE_NOT_FOUND.
Yes, indeed your application failed to find the given path, because you're building it on x86, where Windows's automatic redirection involves, and replace C:\Windows\System32 with C:\Windows\SysWOW64, which contains 32-bit binaries for Windows.
You have two options:
Either you just build it on x64, or disable the automatic redirection by using Wow64DisableWow64FsRedirection as follows:
PVOID OldValue = nullptr;
Wow64DisableWow64FsRedirection(&OldValue);
ShellExecute(NULL, _T("open"), _T("C:\\Windows\\System32\\sigverif.exe"), NULL, NULL, SW_RESTORE);
Be aware that Wow64DisableWow64FsRedirection affects globally in the current thread, as you can find more detail in the page:
Note The Wow64DisableWow64FsRedirection function affects all file operations performed by the current thread, which can have unintended consequences if file system redirection is disabled for any length of time....
So make sure it won't affect other operations unintentionally, or set it back to enabled immediately after your desire is resolved by invoking Wow64EnableWow64FsRedirection.
I have an application that relies heavily on plugins.
On startup it scans a directory for DLLs and loads them one by one, looking for ones that implement a certain exported function. However - if someone were to rename a different type of file to *.dll and put it in the directory, that file would then also be loaded by LoadLibrary(). LoadLibrary() doesn't like that and produces an error [dialog].
Is there a way to simply ignore invalid / incompatible .dll files (either detecting them prior to the call or have LoadLibrary() return NULL rather than throwing a fit)?
You need to set the error mode for your process. Do this once and for all at startup:
UINT oldMode = SetErrorMode(0);
SetErrorMode(oldMode | SEM_FAILCRITICALERRORS | SEM_NOOPENFILEERRORBOX);
After you've set the process error mode, when LoadLibrary fails no dialog box will be displayed and LoadLibrary will return NULL.
The documentation says:
Best practice is that all applications call the process-wide SetErrorMode function with a parameter of SEM_FAILCRITICALERRORS at startup. This is to prevent error mode dialogs from hanging the application.
I also recommend adding SEM_NOOPENFILEERRORBOX for reasons that I guess should be obvious.
http://msdn.microsoft.com/en-us/library/windows/desktop/ms684175%28v=vs.85%29.aspx
Remarks
To enable or disable error messages displayed by the loader during DLL loads, use the SetErrorMode function.
Don't forget to restore the error mode after you are finished.
If you wanted to do this yourself, in a primitive manner you could parse the PE header to identify obvious cases of bad DLL files, however it is not really possible to determine if a DLL is valid and loadable without a full PE loader which requires a lot of work and is already built into the operating system.
Updated answer after feedback in comments from djgandy and Remy Lebeau. Now a complete
function, better preservation of error mode at entry:
// This function will load the DLL named by pszPath if it is a valid library.
// If the function succeeds, it will return a valid HMODULE for the DLL. This
// handle should be passed to FreeLibrary when it is no longer needed.
// If the function fails, it will return NULL and no annoying dialog boxes will
// be displayed. It is therefore up to the caller to notify the user about what
// happened or take any other appropriate action. The reason for failure can
// be obtained from GetLastError(). Common problems:
// ERROR_BAD_EXE_FORMAT - Bad DLL (tested function with text file)
// ERROR_MOD_NOT_FOUND - Missing DLL (tested with file that did not exist)
//
// Module-loading functions can return several other errors, look at winerror.h
// list starting at ERROR_INVALID_MODULETYPE
//
// Obviously, since it's just a wrapper around LoadLibrary this function is not
// safe to call from DllMain.
//
// NB: GetErrorMode() is only available on Vista / Server 2003 or later.
HMODULE LoadLibraryIfValid(LPCTSTR pszPath)
{
HMODULE hModule = NULL;
UINT prevErrorMode = GetErrorMode();
SetErrorMode(prevErrorMode | SEM_FAILCRITICALERRORS);
hModule = LoadLibrary(pszPath);
SetErrorMode(prevErrorMode);
return hModule;
}
If targeting Windows 7 / Server 2008 R2 or later, the Get/SetThreadErrorMode()
functions are available, but might not be worth it or even a good alternative
(discussion in comments, below)
If anyone cared enough to put the time into it (I sure don't), a version of
this function could easily be written using GetModuleHandle for kernel32 and
GetProcAddress to be compatible with earlier versions of Windows as well as
provide a global/per-thread error mode option for platforms that support it
(truly pointless because it's only changed for the duration of one call anyway).
This is the largest commentary-to-code ratio in my life.
A 32-bit process running on 64-bit would get generalized path to a redirected path, due to file-redirection. A call to GetCurrentDirectory, for example, if process is running from \Windows\SysWOW64 would get result: Windows\System32.
Though, it is very much possible to determine if 32-bit process is running on 64-bit host machine. I am looking for a solution to give me correct/actual path. Something like ResolveWOWPathToActual path, that would give \Windows\SysWOW64 for a path \Windows\System32.
EDIT:
WCHAR szCurFolder[MAX_PATH];
PVOID dummy;
Wow64DisableWow64FsRedirection(&dummy);
GetCurrentDirectoryW(MAX_PATH, szCurFolder);
printf("Current Dir: %S\n", szCurFolder);
The API function GetFinalPathNameByHandle might serve your needs. You will have to open the file or directory to obtain a handle to it. Pass that handle to GetFinalPathNameByHandle and it will give you the true path of the file. There is no need to disable the file system redirector.
I suppose the only possible downside is that this does require you to supply a handle rather than a path.
I've written a very simple service application based on this code example.
The application as part of its normal running assumes there exists a file in the directory it is found, or in its execution path.
When I 'install' the service and then subsequently 'start' the service from the service manager in control panel. The application fails because it can't find the file to open and read from (even though the file is in the same directory as the installed executable).
My question is when a windows service is run, which is the expected running path supposed to be?
When calling 'CreateService' there only seems to be a path parameter for the binary, not for execution. Is there someway to indicate where the binary should be executed from?
I've tried this on windows vista and windows 7. Getting the same issues.
Since Windows services are run from a different context than normal user-mode applications, it's best if you don't make any assumptions about working directories or relative paths. Aside from differences in working directories, a service could run using a completely different set of permissions, etc.
Using an absolute path to the file that your service needs should avoid this problem entirely. Absolute paths will be interpreted the same regardless of the working directory, so this should make the working directory of your service irrelevant. There are several ways to go about this:
Hard-code the absolute path - This is perhaps the easiest way to avoid the problem, however it's also the least flexible. This method is probably fine for basic development and testing work, but you probably want something a bit more sophisticated before other people start using your program.
Store the absolute path in an environment variable - This gives you an extra layer of flexibility since the path can now be set to any arbitrary value and changed as needed. Since a service can run as a different user with a different set of environment variables, there are still some gotchas with this approach.
Store an absolute path in the registry - This is probably the most fool-proof method. Retrieving the path from the registry will give you the same result for all user accounts, plus this is relatively easy to set up at install time.
By default, the current directory for your Windows service is the System32 folder.
A promising solution is creating an environment variable that keeps the full path of your input location and retrieving the path from this variable at runtime.
If you use the same path as binary, you could just read binary path and modify it accordingly. But this is rather quick-fix rather than designed-solution. If I were you, I would either create system-wide environment variable and store value there, or (even better) use windows registry to store service configuration.
Note:
You will need to add Yourself some privileges using AdjustTokenPrivileges function, you can see an example here in ModifyPrivilege function.
Also be sure to use HKEY_LOCAL_MACHINE and not HKEY_CURRENT_USER. Services ar running under different user account so it's HKCU's will be different than what you can see in your registry editor.
Today I solved this problem as it was needed for some software I was developing.
As people above have said; you can hardcode the directory to a specific file - but that would mean whatever config files are needed to load would have to be placed there.
For me, this service was being installed on > 50,000 computers.
We designed it to load from directory in which the service executable is running from.
Now, this is easy enough to set up and achieve as a non-system process (I did most of my testing as a non-system process). But the thing is that the system wrapper that you used (and I used as well) uses Unicode formatting (and depends on it) so traditional ways of doing it doesn't work as well.
Commented parts of the code should explain this. There are some redundancies, I know, but I just wanted a working version when I wrote this.
Fortunately, you can just use GetModuleFileNameA to process it in ASCII format
The code I used is:
char buffer[MAX_PATH]; // create buffer
DWORD size = GetModuleFileNameA(NULL, buffer, MAX_PATH); // Get file path in ASCII
std::string configLoc; // make string
for (int i = 0; i < strlen(buffer); i++) // iterate through characters of buffer
{
if (buffer[i] == '\\') // if buffer has a '\' in it, replace with doubles
{
configLoc = configLoc + "\\\\"; // doubles needed for parsing. 4 = 2(str)
}
else
{
configLoc = configLoc + buffer[i]; // else just add char as normal
}
}
// Complete location
configLoc = configLoc.substr(0, configLoc.length() - 17); //cut the .exe off the end
//(change this to fit needs)
configLoc += "\\\\login.cfg"; // add config file to end of string
From here on, you can simple parse configLoc into a new ifsteam - and then process the contents.
Use this function to adjust the working directory of the service to be the same as the working directory of the exe it's running.
void AdjustCurrentWorkingDir() {
TCHAR szBuff[1024];
DWORD dwRet = 0;
dwRet = GetModuleFileName(NULL, szBuff, 1024); //gets path of exe
if (dwRet != 0 && GetLastError() != ERROR_INSUFFICIENT_BUFFER) {
*(_tcsrchr(szBuff, '\\') + 1) = 0; //get parent directory of exe
if (SetCurrentDirectory(szBuff) == 0) {
//Error
}
}
}
I am using Visual studio 2008 and I want to get the absolute path of the .exe file?
meaning when the user opens the exe file, I need to know its absolute path??
thanks in advance
Under Windows try the following:
char ExeName[8192]; // or what ever max. size you expect.
if (0 != GetModuleFileName (NULL, ExeName, sizeof (ExeName)))
{
printf ("Your array was probably not large enough. Call GetLastError for details\n");
}
If you compile for unicode use wchar_t.
Using the _pgmptr or _wpgmptr global variable is probably the easiest way.* (They're in stdlib.h.)
*Note: Under some rather rare circumstances, it's possible that this won't work... in that case, use GetModuleFileName(NULL, ...);
If you want to obtain a path of the current process, you should use API function:
GetModuleFileName
But, if you want to obtain a full path of the process that is not written by you, use
GetModuleFileNameEx
Above function expects one argument more than GetModuleFileName - it is a HANDLE of a process which path is supposed to be obtained. It is explained in more details on MSDN.