Get path of executable
Asked Answered
C

26

178

I know this question has been asked before but I still haven't seen a satisfactory answer, or a definitive "no, this cannot be done", so I'll ask again!

All I want to do is get the path to the currently running executable, either as an absolute path or relative to where the executable is invoked from, in a platform-independent fashion. I though boost::filesystem::initial_path was the answer to my troubles but that seems to only handle the 'platform-independent' part of the question - it still returns the path from which the application was invoked.

For a bit of background, this is a game using Ogre, which I'm trying to profile using Very Sleepy, which runs the target executable from its own directory, so of course on load the game finds no configuration files etc. and promptly crashes. I want to be able to pass it an absolute path to the configuration files, which I know will always live alongside the executable. The same goes for debugging in Visual Studio - I'd like to be able to run $(TargetPath) without having to set the working directory.

Colburn answered 6/10, 2009 at 21:52 Comment(11)
#1023806 and othersHarvin
Note that it is impossible to prove the absence of an answer, therefore you can't get a definitive NO. I'll be happy to give you an authoritative NO :)Milburt
possible duplicate of how to find the location of the executable in CHammertoe
"on load the game finds no configuration files etc." so the game searches for configuration files on the current directory? That's a bad idea, and potentially a security vulnerability. Configuration files should be stored in a standard location.Twyla
Sure, but there are plenty of programs out there that come in a "portable" version where all configuration is stored alongside the executable. I imagine at least someone has come up with a way of doing this that's not a security risk :)Colburn
@BenHymers This type of argument is fatally flawed: you assume that many programs cannot have the same obvious design (not: buffer overflow type) security flaw. History tells us that it is possible.Twyla
Is it acceptable in your scenario if a user can define the path to be almost whatever he wants? F.ex. a user would define path to be his home directory, or /tmp. Is that OK?Twyla
That wasn't an argument; I meant literally what I wrote, which is that I imagine there's a solution to this that's not a security risk. I'm not sure what changing the working directory would achieve other than a user potentially breaking their own system or more likely just the running program. As such, I think it's probably ok if they want to do this to themselves, yes.Colburn
I posted an answer here to a related question that also answers yours, working across platforms using boostAlexi
boost::filesystem::initial_path is not correct, it returns the executer's path. For example, you debug foo.exe, it returns the project path instead of the application's path.Turbid
Is your game using SDL? If yes then you should use "SDL_GetBasePath".Flute
A
123

There is no cross platform way that I know.

For Linux: pass "/proc/self/exe" to std::filesystem::canonical or readlink.

Windows: pass NULL as the module handle to GetModuleFileName.

Amianthus answered 6/10, 2009 at 22:45 Comment(9)
Platform independence is simply a matter of hiding the platform dependency. In this case using the predefined OS macros detailed at predef.sourceforge.net/preos.html to select the method is straightforward.Hypochondriasis
So is this what everyone does whenever they want to find the executable's path in C++? I was hoping something as simple-sounding as this would already be implemented in a library like boost.Colburn
I suppose I did ask for either a method or 'no', and this is both, so I'll accept it :)Colburn
@BenHymers "So is this what everyone does whenever they want to find the executable's path in C++?" why would anyone need to do that?Twyla
@Twyla I'm not sure I understand you; I'm pretty sure that's the whole point of this question :)Colburn
@curiousguy: You'd want to do it if, for example, your program might get installed in a directory of the user's choosing. You need to be able to find your executable and its support files somehow.Swatter
@Amianthus would you update your answer with a link to my lib? My comment is buried down the list.Memnon
@Clifford: Platform independence and a layer that hide the differences is a bit different: If a new OS is released tomorrow, the platform independent solution (which use a standard followed by most/any OS) will work on it, but the layer will need to be updated. (just as an additional note).Bouchard
@Clifford: the repo is now at github.com/cpredef/predefMontherlant
A
47

The boost::dll::program_location function is one of the best cross platform methods of getting the path of the running executable that I know of. The DLL library was added to Boost in version 1.61.0.

The following is my solution. I have tested it on Windows, Mac OS X, Solaris, Free BSD, and GNU/Linux.

It requires Boost 1.55.0 or greater. It uses the Boost.Filesystem library directly and the Boost.Locale library and Boost.System library indirectly.

src/executable_path.cpp

#include <cstdio>
#include <cstdlib>
#include <algorithm>
#include <iterator>
#include <string>
#include <vector>

#include <boost/filesystem/operations.hpp>
#include <boost/filesystem/path.hpp>
#include <boost/predef.h>
#include <boost/version.hpp>
#include <boost/tokenizer.hpp>

#if (BOOST_VERSION > BOOST_VERSION_NUMBER(1,64,0))
#  include <boost/process.hpp>
#endif

#if (BOOST_OS_CYGWIN || BOOST_OS_WINDOWS)
#  include <Windows.h>
#endif

#include <boost/executable_path.hpp>
#include <boost/detail/executable_path_internals.hpp>

namespace boost {

#if (BOOST_OS_CYGWIN || BOOST_OS_WINDOWS)

std::string executable_path(const char* argv0)
{
  typedef std::vector<char> char_vector;
  typedef std::vector<char>::size_type size_type;
  char_vector buf(1024, 0);
  size_type size = buf.size();
  bool havePath = false;
  bool shouldContinue = true;
  do
  {
    DWORD result = GetModuleFileNameA(nullptr, &buf[0], size);
    DWORD lastError = GetLastError();
    if (result == 0)
    {
      shouldContinue = false;
    }
    else if (result < size)
    {
      havePath = true;
      shouldContinue = false;
    }
    else if (
      result == size
      && (lastError == ERROR_INSUFFICIENT_BUFFER || lastError == ERROR_SUCCESS)
      )
    {
      size *= 2;
      buf.resize(size);
    }
    else
    {
      shouldContinue = false;
    }
  } while (shouldContinue);
  if (!havePath)
  {
    return detail::executable_path_fallback(argv0);
  }
  // On Microsoft Windows, there is no need to call boost::filesystem::canonical or
  // boost::filesystem::path::make_preferred. The path returned by GetModuleFileNameA
  // is the one we want.
  std::string ret = &buf[0];
  return ret;
}

#elif (BOOST_OS_MACOS)

#  include <mach-o/dyld.h>

std::string executable_path(const char* argv0)
{
  typedef std::vector<char> char_vector;
  char_vector buf(1024, 0);
  uint32_t size = static_cast<uint32_t>(buf.size());
  bool havePath = false;
  bool shouldContinue = true;
  do
  {
    int result = _NSGetExecutablePath(&buf[0], &size);
    if (result == -1)
    {
      buf.resize(size + 1);
      std::fill(std::begin(buf), std::end(buf), 0);
    }
    else
    {
      shouldContinue = false;
      if (buf.at(0) != 0)
      {
        havePath = true;
      }
    }
  } while (shouldContinue);
  if (!havePath)
  {
    return detail::executable_path_fallback(argv0);
  }
  std::string path(&buf[0], size);
  boost::system::error_code ec;
  boost::filesystem::path p(
    boost::filesystem::canonical(path, boost::filesystem::current_path(), ec));
  if (ec.value() == boost::system::errc::success)
  {
    return p.make_preferred().string();
  }
  return detail::executable_path_fallback(argv0);
}

#elif (BOOST_OS_SOLARIS)

#  include <stdlib.h>

std::string executable_path(const char* argv0)
{
  std::string ret = getexecname();
  if (ret.empty())
  {
    return detail::executable_path_fallback(argv0);
  }
  boost::filesystem::path p(ret);
  if (!p.has_root_directory())
  {
    boost::system::error_code ec;
    p = boost::filesystem::canonical(
      p, boost::filesystem::current_path(), ec);
    if (ec.value() != boost::system::errc::success)
    {
      return detail::executable_path_fallback(argv0);
    }
    ret = p.make_preferred().string();
  }
  return ret;
}

#elif (BOOST_OS_BSD)

#  include <sys/sysctl.h>

std::string executable_path(const char* argv0)
{
  typedef std::vector<char> char_vector;
  int mib[4]{0};
  size_t size;
  mib[0] = CTL_KERN;
  mib[1] = KERN_PROC;
  mib[2] = KERN_PROC_PATHNAME;
  mib[3] = -1;
  int result = sysctl(mib, 4, nullptr, &size, nullptr, 0);
  if (-1 == result)
  {
    return detail::executable_path_fallback(argv0);
  }
  char_vector buf(size + 1, 0);
  result = sysctl(mib, 4, &buf[0], &size, nullptr, 0);
  if (-1 == result)
  {
    return detail::executable_path_fallback(argv0);
  }
  std::string path(&buf[0], size);
  boost::system::error_code ec;
  boost::filesystem::path p(
    boost::filesystem::canonical(
      path, boost::filesystem::current_path(), ec));
  if (ec.value() == boost::system::errc::success)
  {
    return p.make_preferred().string();
  }
  return detail::executable_path_fallback(argv0);
}

#elif (BOOST_OS_LINUX)

#  include <unistd.h>

std::string executable_path(const char *argv0)
{
  typedef std::vector<char> char_vector;
  typedef std::vector<char>::size_type size_type;
  char_vector buf(1024, 0);
  size_type size = buf.size();
  bool havePath = false;
  bool shouldContinue = true;
  do
  {
    ssize_t result = readlink("/proc/self/exe", &buf[0], size);
    if (result < 0)
    {
      shouldContinue = false;
    }
    else if (static_cast<size_type>(result) < size)
    {
      havePath = true;
      shouldContinue = false;
      size = result;
    }
    else
    {
      size *= 2;
      buf.resize(size);
      std::fill(std::begin(buf), std::end(buf), 0);
    }
  } while (shouldContinue);
  if (!havePath)
  {
    return detail::executable_path_fallback(argv0);
  }
  std::string path(&buf[0], size);
  boost::system::error_code ec;
  boost::filesystem::path p(
    boost::filesystem::canonical(
      path, boost::filesystem::current_path(), ec));
  if (ec.value() == boost::system::errc::success)
  {
    return p.make_preferred().string();
  }
  return detail::executable_path_fallback(argv0);
}

#else

std::string executable_path(const char *argv0)
{
  return detail::executable_path_fallback(argv0);
}

#endif

}

src/detail/executable_path_internals.cpp

#include <cstdio>
#include <cstdlib>
#include <algorithm>
#include <iterator>
#include <string>
#include <vector>

#include <boost/filesystem/operations.hpp>
#include <boost/filesystem/path.hpp>
#include <boost/predef.h>
#include <boost/version.hpp>
#include <boost/tokenizer.hpp>

#if (BOOST_VERSION > BOOST_VERSION_NUMBER(1,64,0))
#  include <boost/process.hpp>
#endif

#if (BOOST_OS_CYGWIN || BOOST_OS_WINDOWS)
#  include <Windows.h>
#endif

#include <boost/executable_path.hpp>
#include <boost/detail/executable_path_internals.hpp>

namespace boost {
namespace detail {

std::string GetEnv(const std::string& varName)
{
  if (varName.empty()) return "";
#if (BOOST_OS_BSD || BOOST_OS_CYGWIN || BOOST_OS_LINUX || BOOST_OS_MACOS || BOOST_OS_SOLARIS)
  char* value = std::getenv(varName.c_str());
  if (!value) return "";
  return value;
#elif (BOOST_OS_WINDOWS)
  typedef std::vector<char> char_vector;
  typedef std::vector<char>::size_type size_type;
  char_vector value(8192, 0);
  size_type size = value.size();
  bool haveValue = false;
  bool shouldContinue = true;
  do
  {
    DWORD result = GetEnvironmentVariableA(varName.c_str(), &value[0], size);
    if (result == 0)
    {
      shouldContinue = false;
    }
    else if (result < size)
    {
      haveValue = true;
      shouldContinue = false;
    }
    else
    {
      size *= 2;
      value.resize(size);
    }
  } while (shouldContinue);
  std::string ret;
  if (haveValue)
  {
    ret = &value[0];
  }
  return ret;
#else
  return "";
#endif
}

bool GetDirectoryListFromDelimitedString(
  const std::string& str,
  std::vector<std::string>& dirs)
{
  typedef boost::char_separator<char> char_separator_type;
  typedef boost::tokenizer<
    boost::char_separator<char>, std::string::const_iterator,
    std::string> tokenizer_type;
  dirs.clear();
  if (str.empty())
  {
    return false;
  }
#if (BOOST_OS_WINDOWS)
  const std::string os_pathsep(";");
#else
  const std::string os_pathsep(":");
#endif
  char_separator_type pathSep(os_pathsep.c_str());
  tokenizer_type strTok(str, pathSep);
  typename tokenizer_type::iterator strIt;
  typename tokenizer_type::iterator strEndIt = strTok.end();
  for (strIt = strTok.begin(); strIt != strEndIt; ++strIt)
  {
    dirs.push_back(*strIt);
  }
  if (dirs.empty())
  {
    return false;
  }
  return true;
}

std::string search_path(const std::string& file)
{
  if (file.empty()) return "";
  std::string ret;
#if (BOOST_VERSION > BOOST_VERSION_NUMBER(1,64,0))
  {
    namespace bp = boost::process;
    boost::filesystem::path p = bp::search_path(file);
    ret = p.make_preferred().string();
  }
#endif
  if (!ret.empty()) return ret;
  // Drat! I have to do it the hard way.
  std::string pathEnvVar = GetEnv("PATH");
  if (pathEnvVar.empty()) return "";
  std::vector<std::string> pathDirs;
  bool getDirList = GetDirectoryListFromDelimitedString(pathEnvVar, pathDirs);
  if (!getDirList) return "";
  std::vector<std::string>::const_iterator it = pathDirs.cbegin();
  std::vector<std::string>::const_iterator itEnd = pathDirs.cend();
  for ( ; it != itEnd; ++it)
  {
    boost::filesystem::path p(*it);
    p /= file;
    if (boost::filesystem::exists(p) && boost::filesystem::is_regular_file(p))
    {
      return p.make_preferred().string();
    }
  }
  return "";
}

std::string executable_path_fallback(const char *argv0)
{
  if (argv0 == nullptr) return "";
  if (argv0[0] == 0) return "";
#if (BOOST_OS_WINDOWS)
  const std::string os_sep("\\");
#else
  const std::string os_sep("/");
#endif
  if (strstr(argv0, os_sep.c_str()) != nullptr)
  {
    boost::system::error_code ec;
    boost::filesystem::path p(
      boost::filesystem::canonical(
        argv0, boost::filesystem::current_path(), ec));
    if (ec.value() == boost::system::errc::success)
    {
      return p.make_preferred().string();
    }
  }
  std::string ret = search_path(argv0);
  if (!ret.empty())
  {
    return ret;
  }
  boost::system::error_code ec;
  boost::filesystem::path p(
    boost::filesystem::canonical(
      argv0, boost::filesystem::current_path(), ec));
  if (ec.value() == boost::system::errc::success)
  {
    ret = p.make_preferred().string();
  }
  return ret;
}

}
}

include/boost/executable_path.hpp

#ifndef BOOST_EXECUTABLE_PATH_HPP_
#define BOOST_EXECUTABLE_PATH_HPP_

#pragma once

#include <string>

namespace boost {
std::string executable_path(const char * argv0);
}

#endif // BOOST_EXECUTABLE_PATH_HPP_

include/boost/detail/executable_path_internals.hpp

#ifndef BOOST_DETAIL_EXECUTABLE_PATH_INTERNALS_HPP_
#define BOOST_DETAIL_EXECUTABLE_PATH_INTERNALS_HPP_

#pragma once

#include <string>
#include <vector>

namespace boost {
namespace detail {
std::string GetEnv(const std::string& varName);
bool GetDirectoryListFromDelimitedString(
    const std::string& str,
    std::vector<std::string>& dirs);
std::string search_path(const std::string& file);
std::string executable_path_fallback(const char * argv0);
}
}

#endif // BOOST_DETAIL_EXECUTABLE_PATH_INTERNALS_HPP_

I have a complete project, including a test application and CMake build files available at SnKOpen - /cpp/executable_path/trunk. This version is more complete than the version I provided here. It is also supports more platforms.

I have tested the application on all supported operating systems in the following four scenarios.

  1. Relative path, executable in current directory: i.e. ./executable_path_test
  2. Relative path, executable in another directory: i.e. ./build/executable_path_test
  3. Full path: i.e. /some/dir/executable_path_test
  4. Executable in path, file name only: i.e. executable_path_test

In all four scenarios, both the executable_path and executable_path_fallback functions work and return the same results.

Notes

This is an updated answer to this question. I updated the answer to take into consideration user comments and suggestions. I also added a link to a project in my SVN Repository.

About answered 5/12, 2015 at 18:5 Comment(13)
That looks like a very complete solution with reasonable fallbacks. +1! One question, though: Would it make sense to replace the fixed char[1024] buffers with something like a vector<char> that can be resized if the path exceeds the initial size?Margay
Yes. That is an excellent suggestion. Of course some additional changes would need to be made such as checking for errors, resizing the buffer, and trying again.About
I think the fallback is not correct. argv[0] can also be just executable name, in which case it would be necessary to search for it in PATH on *nix systems.Gwendolyn
I think you want size <= 0 in the if statement after readlink. size could also be const.Malinda
I made a request to add this to the Boost libraries here.Riorsson
I tried using this. but it needs boost, correct? I thought it was standaloneVigilantism
You had me at "boost::dll::program_location"Erect
@Michał Górny: PATH is irrelevant to argv[0]. Your shell (csh, ksh, bash, ...) searches executables from PATH environment variable, and feeds either an absolute or a relative path to argv[0] when calling the system call exec. argv[0] must contain a legal path, not just a name to search in PATH. (otherwise, the system call exec don't know what to execute). So, PATH is a shell feature/function, not the Linux system's.Pentheas
@RobinHsu, "argv[0] must contain a legal path" that assumption is not valid. Just try it: execl("./someprogram", "totally random stuff", NULL);.Gwendolyn
@Michał Górny: ./someprogram is a legal path for execl (relative path). Legal path includes absolute path and relative path. However, searhing path in PATH environment variable occurs in shell only, and shell will convert the search result into a legal path (absolute or relative) and feed it into argv[0]. execl() won't search PATH environment variable. You can check that execl("some-program", ...) won't work.Pentheas
@Michał Górny: To explain it more clearer: The shell first converts the path to a legal path (in the case of ./someprogram, no conversion is needed, since no search is needed, and the path itself is a legal path). Then, it calls fork(). The forked process will inherit the current directory, and thus it understand what '.' (the current directory) is. Since ./someprogram specifies a unique executable path (relative path form), it is a legal path for exec(). Then, it calls exec("./someprogram", ...). Now, argv[0] contains "./someprogram".Pentheas
@RobinHsu, not all programs are started via shell. You can't rely on argv[0] containing anything meaningful.Gwendolyn
@MichałGórny: Started by shell or not, it does not matter. Any program needs to be started from the system call: fork() + execX(). It does not matter if your C/C++-code program (which we are discussing) starts from Perl, Python, Java, ... anything. argc[0] must contain the legal path, which is what execX() only accepts. Yet, since relative path is also legal for execX(), you might need to get the current directory (by the system call getcwd(), to form an absolute path. Also, if you need a canonical path, you call readlink or std::filesystem::canonical().Pentheas
R
33

C++17, windows, unicode, using filesystem new api:

#include "..\Project.h"
#include <filesystem>
using namespace std;
using namespace filesystem;

int wmain(int argc, wchar_t** argv)
{
    auto dir = weakly_canonical(path(argv[0])).parent_path();
    printf("%S", dir.c_str());
    return 0;
}

(Important: Use wmain with wchar_t** - don't mix main with wchar_t**. For cmake projects enable unicode using add_definitions(-DUNICODE -D_UNICODE)).

Suspect this solution should be portable, but don't know how unicode is implemented on other OS's.

weakly_canonical is needed only if you use as Output Directory upper folder references ('..') to simplify path. If you don't use it - remove it.

If you're operating from dynamic link library (.dll /.so), then you might not have argv, then you can consider following solution:

application.h:

#pragma once

//
// https://en.cppreference.com/w/User:D41D8CD98F/feature_testing_macros
//
#ifdef __cpp_lib_filesystem
#include <filesystem>
#else
#include <experimental/filesystem>

namespace std {
    namespace filesystem = experimental::filesystem;
}
#endif

std::filesystem::path getexepath();

application.cpp:

#include "application.h"
#ifdef _WIN32
#include <windows.h>    //GetModuleFileNameW
#else
#include <limits.h>
#include <unistd.h>     //readlink
#endif

std::filesystem::path getexepath()
{
#ifdef _WIN32
    wchar_t path[MAX_PATH] = { 0 };
    GetModuleFileNameW(NULL, path, MAX_PATH);
    return path;
#else
    char result[PATH_MAX];
    ssize_t count = readlink("/proc/self/exe", result, PATH_MAX);
    return std::string(result, (count > 0) ? count : 0);
#endif
}
Reseta answered 8/4, 2019 at 18:39 Comment(1)
The guards inside the header isn't proper testing for the presence of filesystem. cppreference shows that the value of the feature test macro is defined in the filesystem header itself, hence it doesn't work to test before inclusing. __has_include() is a better standard test here.Wein
B
31

This way uses boost + argv. You mentioned this may not be cross platform because it may or may not include the executable name. Well the following code should work around that.

#include <boost/filesystem/operations.hpp>

#include <boost/filesystem/path.hpp>

#include <iostream>

namespace fs = boost::filesystem;


int main(int argc,char** argv)
{
    fs::path full_path( fs::initial_path<fs::path>() );

    full_path = fs::system_complete( fs::path( argv[0] ) );

    std::cout << full_path << std::endl;

    //Without file name
    std::cout << full_path.stem() << std::endl;
    //std::cout << fs::basename(full_path) << std::endl;

    return 0;
}

The following code gets the current working directory which may do what you need

#include <boost/filesystem/operations.hpp>
#include <boost/filesystem/path.hpp>

#include <iostream>

namespace fs = boost::filesystem;


int main(int argc,char** argv)
{
    //current working directory
    fs::path full_path( fs::current_path<fs::path>() );

    std::cout << full_path << std::endl;

    std::cout << full_path.stem() << std::endl;
    //std::cout << fs::basepath(full_path) << std::endl;

    return 0;
}

Note Just realized that basename() was deprecated so had to switch to .stem()

Bant answered 6/10, 2009 at 21:53 Comment(5)
stem seems to give me just the executable minus the path and extension on Windows, but that's a minor point. What I'd like to know is how this works around argv[0] being possibly incorrect? It works for me testing on Windows, but then argv[0] is actually being passed in as the absolute path of the executable, which makes system_complete's job pretty easy :)Colburn
No -- he doesn't need the working directory. and NO argv doesn't help. What do you do when argv contains only the executable name? What to do, when the program was invoked via a symlink?Infanticide
"//Without file name" - you want .parent_path(), not .stem(), no?Tails
This doesn't seem to work on my platform (macOS El Capitan). I get the current working directory instead. Also, as @Claudiu said, I think it should be .parent_path().Riorsson
This will not compileYepez
R
23

I'm not sure about Linux, but try this for Windows:

#include <windows.h>
#include <iostream>

using namespace std ;

int main()
{
     char ownPth[MAX_PATH]; 

     // When NULL is passed to GetModuleHandle, the handle of the exe itself is returned
     HMODULE hModule = GetModuleHandle(NULL);
     if (hModule != NULL)
     {
         // Use GetModuleFileName() with module handle to get the path
         GetModuleFileName(hModule, ownPth, (sizeof(ownPth))); 
         cout << ownPth << endl ;
         system("PAUSE");
         return 0;
     }
     else
     {
         cout << "Module handle is NULL" << endl ;
         system("PAUSE");
         return 0;
     }
}
Rickey answered 9/11, 2012 at 15:10 Comment(6)
Note that one must use WCHAR ownPth.., wrapped around a #ifdef UNICODE in the event that one compiles with Unicode support. If not, use the code provided.Particiaparticipant
just for the record I am just having a funny case where GetModuleDirectory returns a path with the ".." parts in it, like if it was taking the string pure raw from the command line lol. actually in this case visual studio is launching the process and the .. is part of the debugging path. something like $(projectDir)../some.exe I used PathCanonicalize from Shwlib but one needs to link against this lib. this may not be desirable.Whisk
I'd also recomment to use TCHAR for the ownPath instead of char. But nice answer anyways.Flavio
Is it even possible for this to fail? It seems unlikely at a glance... HMODULE hModule = GetModuleHandle(NULL);Untouchable
If first parameter for GetModuleFileName is NULL, it retrieves the path of the executable file of the current process.Chengteh
This answer is not complete. Please look at my answer with an improved versionMishap
D
17

This is what I ended up with

The header file looks like this:

#pragma once

#include <string>
namespace MyPaths {

  std::string getExecutablePath();
  std::string getExecutableDir();
  std::string mergePaths(std::string pathA, std::string pathB);
  bool checkIfFileExists (const std::string& filePath);

}

Implementation


#if defined(_WIN32)
    #include <windows.h>
    #include <Shlwapi.h>
    #include <io.h> 

    #define access _access_s
#endif

#ifdef __APPLE__
    #include <libgen.h>
    #include <limits.h>
    #include <mach-o/dyld.h>
    #include <unistd.h>
#endif

#ifdef __linux__
    #include <limits.h>
    #include <libgen.h>
    #include <unistd.h>

    #if defined(__sun)
        #define PROC_SELF_EXE "/proc/self/path/a.out"
    #else
        #define PROC_SELF_EXE "/proc/self/exe"
    #endif

#endif

namespace MyPaths {

#if defined(_WIN32)

std::string getExecutablePath() {
   char rawPathName[MAX_PATH];
   GetModuleFileNameA(NULL, rawPathName, MAX_PATH);
   return std::string(rawPathName);
}

std::string getExecutableDir() {
    std::string executablePath = getExecutablePath();
    char* exePath = new char[executablePath.length()];
    strcpy(exePath, executablePath.c_str());
    PathRemoveFileSpecA(exePath);
    std::string directory = std::string(exePath);
    delete[] exePath;
    return directory;
}

std::string mergePaths(std::string pathA, std::string pathB) {
  char combined[MAX_PATH];
  PathCombineA(combined, pathA.c_str(), pathB.c_str());
  std::string mergedPath(combined);
  return mergedPath;
}

#endif

#ifdef __linux__

std::string getExecutablePath() {
   char rawPathName[PATH_MAX];
   realpath(PROC_SELF_EXE, rawPathName);
   return  std::string(rawPathName);
}

std::string getExecutableDir() {
    std::string executablePath = getExecutablePath();
    char *executablePathStr = new char[executablePath.length() + 1];
    strcpy(executablePathStr, executablePath.c_str());
    char* executableDir = dirname(executablePathStr);
    delete [] executablePathStr;
    return std::string(executableDir);
}

std::string mergePaths(std::string pathA, std::string pathB) {
  return pathA+"/"+pathB;
}

#endif

#ifdef __APPLE__
    std::string getExecutablePath() {
        char rawPathName[PATH_MAX];
        char realPathName[PATH_MAX];
        uint32_t rawPathSize = (uint32_t)sizeof(rawPathName);

        if(!_NSGetExecutablePath(rawPathName, &rawPathSize)) {
            realpath(rawPathName, realPathName);
        }
        return  std::string(realPathName);
    }

    std::string getExecutableDir() {
        std::string executablePath = getExecutablePath();
        char *executablePathStr = new char[executablePath.length() + 1];
        strcpy(executablePathStr, executablePath.c_str());
        char* executableDir = dirname(executablePathStr);
        delete [] executablePathStr;
        return std::string(executableDir);
    }

    std::string mergePaths(std::string pathA, std::string pathB) {
        return pathA+"/"+pathB;
    }
#endif


bool checkIfFileExists (const std::string& filePath) {
   return access( filePath.c_str(), 0 ) == 0;
}

}

District answered 16/2, 2020 at 16:24 Comment(2)
Thanks for providing your answer. I had to change the following lines in the windows part of getExecutableDir . Cause visual studio didn't like the use of strcpy. So with strcpy_s it looks like this. char* exePath = new char[executablePath.length() + 1]; strcpy_s(exePath, executablePath.length() + 1, executablePath.c_str());Deepfry
Thanks, this was really useful! But, there's a memory error with the Linux implementation of getExecutableDir(). Since dirname() returns a pointer into the original string passed in, the std::string needs to be constructed before delete [] executablePathStr. Otherwise, the string will be constructed from deleted memory and the function will return garbage.Decision
P
14

For windows:

GetModuleFileName - returns the exe path + exe filename

To remove filename
PathRemoveFileSpec

Pedantry answered 17/8, 2012 at 3:54 Comment(1)
Docs note for PathRemoveFileSpec: This function is deprecated. We recommend the use of the PathCchRemoveFileSpec function in its place.Apocalyptic
P
10

QT provides this with OS abstraction as QCoreApplication::applicationDirPath()

Proximal answered 5/12, 2015 at 17:52 Comment(2)
Getting with this: QCoreApplication::applicationDirPath: Please instantiate the QApplication object first. Any idea how to solve that?Police
@GuySoft: Simply create an instance of QCoreApplication like so QApplication application(argc, argv); (do this in your main(argc, argv), and be sure that you do not modify the argc/argv, since these need to stay valid over the lifetime of the QCoreApplication (check the documentation)Proximal
A
9

If using C++17 one can do the following to get the path to the executable.

#include <filesystem>

std::filesystem::path getExecutablePath()
{
    return std::filesystem::canonical("/proc/self/exe");
}

The above answer has been tested on Debian 10 using G++ 9.3.0

Aureolin answered 3/8, 2020 at 12:11 Comment(1)
Note that this will work only if /proc/self/exe exists and is accessible. You should probably check if this is the case.Lindner
Q
6

This is a Windows specific way, but it is at least half of your answer.

GetThisPath.h

/// dest is expected to be MAX_PATH in length.
/// returns dest
///     TCHAR dest[MAX_PATH];
///     GetThisPath(dest, MAX_PATH);
TCHAR* GetThisPath(TCHAR* dest, size_t destSize);

GetThisPath.cpp

#include <Shlwapi.h>
#pragma comment(lib, "shlwapi.lib")

TCHAR* GetThisPath(TCHAR* dest, size_t destSize)
{
    if (!dest) return NULL;
    if (MAX_PATH > destSize) return NULL;

    DWORD length = GetModuleFileName( NULL, dest, destSize );
    PathRemoveFileSpec(dest);
    return dest;
}

mainProgram.cpp

TCHAR dest[MAX_PATH];
GetThisPath(dest, MAX_PATH);

I would suggest using platform detection as preprocessor directives to change the implementation of a wrapper function that calls GetThisPath for each platform.

Queenqueena answered 15/10, 2013 at 14:28 Comment(0)
B
6

Using args[0] and looking for '/' (or '\\'):

#include <string>
#include <iostream> // to show the result

int main( int numArgs, char *args[])
{
    // Get the last position of '/'
    std::string aux(args[0]);

    // get '/' or '\\' depending on unix/mac or windows.
#if defined(_WIN32) || defined(WIN32)
    int pos = aux.rfind('\\');
#else
    int pos = aux.rfind('/');
#endif

    // Get the path and the name
    std::string path = aux.substr(0,pos+1);
    std::string name = aux.substr(pos+1);
    // show results
    std::cout << "Path: " << path << std::endl;
    std::cout << "Name: " << name << std::endl;
}

EDITED: If '/' does not exist, pos==-1 so the result is correct.

Bouchard answered 7/7, 2014 at 16:26 Comment(5)
What if '/' isn't present in the path? There's no checking of that case and I believe it's quite likely - Windows will use backslashes, and args[0] may not actually be a path at all.Colburn
If '/' does not exist, rfind return -1, so "path"=aux.substr(0,0) and "name" = aux.substr(0): the result is correct. Related with Windows, you are right, '/' must be changed to '\\', I will change to allow windows too. I have also tested for filenames with '/', but this last is codified and does not create problems.Bouchard
It's more the part about args[0] not necessarily being the executable path that bothers me. Thanks for fixing your answer for Windows though :)Colburn
If the command is run without giving the path (i.e. it is found by being in a directory given in the PATH env var), args[0] will just be the name of the executable, without the path.Ledezma
@Kevin: you (and others) are right, this is a simple solution, for little tools, that work ~95% of cases. For serious software, a configuration file and/or an environment variable is probably better. Also, this need imply usually a not very good (or even wrong) design.Bouchard
D
3

For Windows you can use GetModuleFilename().
For Linux see BinReloc (old, defunct URL) mirror of BinReloc in datenwolf's GitHub repositories.

Dawn answered 6/10, 2009 at 22:29 Comment(0)
A
3

This is probably the most natural way to do it, while covering most major desktop platforms. I am not certain, but I believe this should work with all the BSD's, not just FreeBSD, if you change the platform macro check to cover all of them. If I ever get around to installing Solaris, I'll be sure to add that platform to the supported list.

Features full UTF-8 support on Windows, which not everyone cares enough to go that far.

procinfo/win32/procinfo.cpp

#ifdef _WIN32
#include "../procinfo.h"
#include <windows.h>
#include <tlhelp32.h>
#include <cstddef>
#include <vector>
#include <cwchar>

using std::string;
using std::wstring;
using std::vector;
using std::size_t;

static inline string narrow(wstring wstr) {
  int nbytes = WideCharToMultiByte(CP_UTF8, 0, wstr.c_str(), (int)wstr.length(), NULL, 0, NULL, NULL);
  vector<char> buf(nbytes);
  return string{ buf.data(), (size_t)WideCharToMultiByte(CP_UTF8, 0, wstr.c_str(), (int)wstr.length(), buf.data(), nbytes, NULL, NULL) };
}

process_t ppid_from_pid(process_t pid) {        
  process_t ppid;       
  HANDLE hp = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);      
  PROCESSENTRY32 pe = { 0 };        
  pe.dwSize = sizeof(PROCESSENTRY32);       
  if (Process32First(hp, &pe)) {        
    do {        
      if (pe.th32ProcessID == pid) {        
        ppid = pe.th32ParentProcessID;      
        break;      
      }     
    } while (Process32Next(hp, &pe));       
  }     
  CloseHandle(hp);      
  return ppid;      
}

string path_from_pid(process_t pid) {
  string path;
  HANDLE hm = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, pid);
  MODULEENTRY32W me = { 0 };
  me.dwSize = sizeof(MODULEENTRY32W);
  if (Module32FirstW(hm, &me)) {
    do {
      if (me.th32ProcessID == pid) {
        path = narrow(me.szExePath);
        break;
      }
    } while (Module32NextW(hm, &me));
  }
  CloseHandle(hm);
  return path;
}
#endif

procinfo/macosx/procinfo.cpp

#if defined(__APPLE__) && defined(__MACH__)
#include "../procinfo.h"
#include <libproc.h>

using std::string;

string path_from_pid(process_t pid) {
  string path;
  char buffer[PROC_PIDPATHINFO_MAXSIZE];
  if (proc_pidpath(pid, buffer, sizeof(buffer)) > 0) {
    path = string(buffer) + "\0";
  }
  return path;
}
#endif

procinfo/linux/procinfo.cpp

#ifdef __linux__
#include "../procinfo.h"
#include <cstdlib>

using std::string;
using std::to_string;

string path_from_pid(process_t pid) {
  string path;
  string link = string("/proc/") + to_string(pid) + string("/exe");
  char *buffer = realpath(link.c_str(), NULL);
  path = buffer ? : "";
  free(buffer);
  return path;
}
#endif

procinfo/freebsd/procinfo.cpp

#ifdef __FreeBSD__
#include "../procinfo.h"
#include <sys/sysctl.h>
#include <cstddef>

using std::string;
using std::size_t;

string path_from_pid(process_t pid) {
  string path;
  size_t length;
  // CTL_KERN::KERN_PROC::KERN_PROC_PATHNAME(pid)
  int mib[4] = { CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME, pid };
  if (sysctl(mib, 4, NULL, &length, NULL, 0) == 0) {
    path.resize(length, '\0');
    char *buffer = path.data();
    if (sysctl(mib, 4, buffer, &length, NULL, 0) == 0) {
      path = string(buffer) + "\0";
    }
  }
  return path;
}
#endif

procinfo/procinfo.cpp

#include "procinfo.h"
#ifdef _WiN32
#include <process.h>
#endif
#include <unistd.h>
#include <cstddef>

using std::string;
using std::size_t;

process_t pid_from_self() {
  #ifdef _WIN32
  return _getpid();
  #else
  return getpid();
  #endif
}

process_t ppid_from_self() {
  #ifdef _WIN32
  return ppid_from_pid(pid_from_self());
  #else
  return getppid();
  #endif
}

string dir_from_pid(process_t pid) {
  string fname = path_from_pid(pid);
  size_t fp = fname.find_last_of("/\\");
  return fname.substr(0, fp + 1);
}

string name_from_pid(process_t pid) {
  string fname = path_from_pid(pid);
  size_t fp = fname.find_last_of("/\\");
  return fname.substr(fp + 1);
}

procinfo/procinfo.h

#ifdef _WiN32
#include <windows.h>
typedef DWORD process_t;
#else
#include <sys/types.h>
typedef pid_t process_t;
#endif
#include <string>

/* windows-only helper function */
process_t ppid_from_pid(process_t pid);

/* get current process process id */
process_t pid_from_self();

/* get parent process process id */
process_t ppid_from_self();

/* std::string possible_result = "C:\\path\\to\\file.exe"; */
std::string path_from_pid(process_t pid);

/* std::string possible_result = "C:\\path\\to\\"; */
std::string dir_from_pid(process_t pid);

/* std::string possible_result = "file.exe"; */
std::string name_from_pid(process_t pid);

This allows getting the full path to the executable of pretty much any process id, except on Windows there are some process's with security attributes which simply will not allow it, so wysiwyg, this solution is not perfect.

To address what the question was asking more precisely, you may do this:

procinfo.cpp

#include "procinfo/procinfo.h"
#include <iostream>

using std::string;
using std::cout;
using std::endl;

int main() {
  cout << dir_from_pid(pid_from_self()) << endl;
  return 0;
}

Build the above file structure with this command:

procinfo.sh

cd "${0%/*}"
g++ procinfo.cpp procinfo/procinfo.cpp procinfo/win32/procinfo.cpp procinfo/macosx/procinfo.cpp procinfo/linux/procinfo.cpp procinfo/freebsd/procinfo.cpp -o procinfo.exe

For downloading a copy of the files listed above:

git clone git://github.com/time-killer-games/procinfo.git

For more cross-platform process-related goodness:

https://github.com/time-killer-games/enigma-dev

See the readme for a list of most of the functions included.

Accordance answered 8/4, 2020 at 1:26 Comment(0)
M
3

There are several answers recommending using GetModuleFileName on Windows. These answers have some shortcomings like:

  • The code should work for both UNICODE and ANSI versions
  • The path can be longer than MAX_PATH
  • The GetModuleFileName function may fail and return 0.
  • GetModuleFileName may return a relative executable name instead of a full name
  • GetModuleFileName may return a short path like C:\GIT-RE~1\TEST_G~1\test.exe

Let me provide an improved version, which takes into account the abovementioned points:

#include <Windows.h>
#include <string>
#include <memory>
#include <iostream>

// Converts relative name like "..\test.exe" to its full form like "C:\project\test.exe".
std::basic_string<TCHAR> get_full_name(const TCHAR const* name)
{
    // First we need to get a length of the full name string
    const DWORD full_name_length{GetFullPathName(name, 0, NULL, NULL)};
    if (full_name_length == 0) {
        // GetFullPathName call failed. Maybe you want to throw an exception.
        return std::basic_string<TCHAR>{};
    }

    // Now, when we know the length, we create a buffer with correct size and write the full name into it
    std::unique_ptr<TCHAR[]> full_name_buffer{new TCHAR[full_name_length]};
    const DWORD res = GetFullPathName(name, full_name_length, full_name_buffer.get(), NULL);
    if (res == 0) {
        // GetFullPathName call failed. Maybe you want to throw an exception.
        return std::basic_string<TCHAR>{};
    }

    // The full name has been successfully written to the buffer.
    return std::basic_string<TCHAR>(full_name_buffer.get());
}

// Resolves short path like "C:\GIT-RE~1\TEST_G~1\test.exe" into its long form like "C:\git-repository\test_project\test.exe"
std::basic_string<TCHAR> get_long_name(const TCHAR const* name)
{
    // First we need to get a length of the long name string
    const DWORD long_name_length{GetLongPathName(name, 0, NULL)};
    if (long_name_length == 0) {
        // GetLongPathName call failed. Maybe you want to throw an exception.
        return std::basic_string<TCHAR>{};
    }

    // Now, when we know the length, we create a buffer with correct size and write the full name into it
    std::unique_ptr<TCHAR[]> long_name_buffer{new TCHAR[long_name_length]};
    const DWORD res = GetLongPathName(name, long_name_buffer.get(), long_name_length);
    if (res == 0) {
        // GetLongPathName call failed. Maybe you want to throw an exception.
        return std::basic_string<TCHAR>{};
    }

    // The long name has been successfully written to the buffer.
    return std::basic_string<TCHAR>(long_name_buffer.get());
}

std::basic_string<TCHAR> get_current_executable_full_name()
{
    DWORD path_buffer_size = MAX_PATH; // we start with MAX_PATH because it is most likely that 
                                       // the path doesn't exceeds 260 characters
    std::unique_ptr<TCHAR[]> path_buffer{new TCHAR[path_buffer_size]};

    while (true) {
        const auto bytes_written = GetModuleFileName(
            NULL, path_buffer.get(), path_buffer_size);
        const auto last_error = GetLastError();

        if (bytes_written == 0) {
            // GetModuleFileName call failed. Maybe you want to throw an exception.
            return std::basic_string<TCHAR>{};
        }

        if (last_error == ERROR_INSUFFICIENT_BUFFER) {
            // There is not enough space in our buffer to fit the path.
            // We need to increase the buffer and try again.
            path_buffer_size *= 2;
            path_buffer.reset(new TCHAR[path_buffer_size]);
            continue;
        }

        // GetModuleFileName has successfully written the executable name to the buffer.
        // Now we need to convert it to a full long name
        std::basic_string<TCHAR> full_name = get_full_name(path_buffer.get());
        return get_long_name(full_name.c_str());
    }
}

// Example of how this function can be used
int main()
{
#ifdef UNICODE
    // If you use UNICODE version of WinApi
    std::wstring exe_file_full_name = get_current_executable_full_name();
    std::wstring exe_folder_full_name = exe_file_full_name.substr(0, exe_file_full_name.find_last_of(L"\\"));
    std::wcout << exe_file_full_name << "\n"; // prints: C:\test_project\x64\Debug\test_program.exe
    std::wcout << exe_folder_full_name << "\n"; // prints: C:\test_project\x64\Debug
#else
    // If you use ANSI version of WinApi
    std::string exe_file_full_name = get_current_executable_full_name();
    std::string exe_folder_full_name = exe_file_full_name.substr(0, exe_file_full_name.find_last_of("\\"));
    std::cout << exe_file_full_name << "\n"; // prints: C:\test_project\x64\Debug\test_program.exe
    std::cout << exe_folder_full_name << "\n"; // prints: C:\test_project\x64\Debug
#endif
}
Mishap answered 17/4, 2022 at 16:59 Comment(2)
"GetModuleFileName can return a relative executable name instead of a full name" - I have never seen it return anything other than an absolute path.Marriageable
@RemyLebeau, I understand your confusion. Even the documentation on GetModuleFileName says Retrieves the fully qualified path for the file . However, in practice I have encountered that I was retrieving a path like "..\..\my_proj.exe". I also see that there is a similar comment describing the same problem but with GetModuleDirectory which is also supposed to return a full path.Mishap
B
2

As others mentioned, argv[0] is quite a nice solution, provided that the platform actually passes the executable path, which is surely not less probable than the OS being Windows (where WinAPI can help find the executable path). If you want to strip the string to only include the path to the directory where the executable resides, then using that path to find other application files (like game assets if your program is a game) is perfectly fine, since opening files is relative to the working directory, or, if provided, the root.

Bluma answered 7/3, 2019 at 14:48 Comment(0)
H
1

The following works as a quick and dirty solution, but note that it is far from being foolproof:

#include <iostream>

using namespace std ;

int main( int argc, char** argv)
{
    cout << argv[0] << endl ;
    return 0;
}
Hypochondriasis answered 6/10, 2009 at 21:58 Comment(4)
I've seen on other SO questions that this doesn't always work, and that argv[0] can contain the absolute path to the executable, just the file name of the executable, or any other rubbish.Colburn
One should never trust argv[0] if they're attempting to open 'support files' or the like. Argv is subject to change, and any caller that is evil can change the value of this. Avoid unless you're using it for logging, etc., NOT for constructing paths used to open files.Tweedy
this doesn't work on Windows. argv[0] won't have the full path. Only the .exe file. Please, do not try in a bash shell, try it in ths standard console and cout << argv[0] to reproduce.Sherilyn
@FreddyMartinezGarcia Well I would have tested it in Windows, so YMMV. It is whatever was used to launch the code. If you the executable in in the CWD sure you will only get the filename.Hypochondriasis
U
1

In case you need to handle unicode paths for Windows:

#include <Windows.h>
#include <iostream>

int wmain(int argc, wchar_t * argv[])
{
    HMODULE this_process_handle = GetModuleHandle(NULL);
    wchar_t this_process_path[MAX_PATH];

    GetModuleFileNameW(NULL, this_process_path, sizeof(this_process_path));

    std::wcout << "Unicode path of this app: " << this_process_path << std::endl;

    return 0;
}
Untouchable answered 7/6, 2018 at 19:40 Comment(0)
C
1

Here my simple solution that works in both Windows and Linux, based on this solution and modified with this answer:

#include <string>
using namespace std;
#if defined(_WIN32)
#include <algorithm> // for transform() in get_exe_path()
#define WIN32_LEAN_AND_MEAN
#define VC_EXTRALEAN
#include <Windows.h>
#elif defined(__linux__)
#include <unistd.h> // for getting path of executable
#endif // Windows/Linux

string replace(const string& s, const string& from, const string& to) {
    string r = s;
    int p = 0;
    while((p=(int)r.find(from, p))!=string::npos) {
        r.replace(p, from.length(), to);
        p += (int)to.length();
    }
    return r;
}
string get_exe_path() { // returns path where executable is located
    string path = "";
#if defined(_WIN32)
    wchar_t wc[260] = {0};
    GetModuleFileNameW(NULL, wc, 260);
    wstring ws(wc);
    transform(ws.begin(), ws.end(), back_inserter(path), [](wchar_t c) { return (char)c; });
    path = replace(path, "\\", "/");
#elif defined(__linux__)
    char c[260];
    int length = (int)readlink("/proc/self/exe", c, 260);
    path = string(c, length>0 ? length : 0);
#endif // Windows/Linux
    return path.substr(0, path.rfind('/')+1);
}
Chanachance answered 17/3, 2022 at 16:48 Comment(0)
C
0

For Windows, you have the problem of how to strip the executable from the result of GetModuleFileName(). The Windows API call PathRemoveFileSpec() that Nate used for that purpose in his answer changed between Windows 8 and its predecessors. So how to remain compatible with both and safe? Luckily, there's C++17 (or Boost, if you're using an older compiler). I do this:

#include <windows.h>
#include <string>
#include <filesystem>
namespace fs = std::experimental::filesystem;

// We could use fs::path as return type, but if you're not aware of
// std::experimental::filesystem, you probably handle filenames
// as strings anyway in the remainder of your code.  I'm on Japanese
// Windows, so wide chars are a must.
std::wstring getDirectoryWithCurrentExecutable()
{
    int size = 256;
    std::vector<wchar_t> charBuffer;
    // Let's be safe, and find the right buffer size programmatically.
    do {
        size *= 2;
        charBuffer.resize(size);
        // Resize until filename fits.  GetModuleFileNameW returns the
        // number of characters written to the buffer, so if the
        // return value is smaller than the size of the buffer, it was
        // large enough.
    } while (GetModuleFileNameW(NULL, charBuffer.data(), size) == size);
    // Typically: c:/program files (x86)/something/foo/bar/exe/files/win64/baz.exe
    // (Note that windows supports forward and backward slashes as path
    // separators, so you have to be careful when searching through a path
    // manually.)

    // Let's extract the interesting part:
    fs::path path(charBuffer.data());  // Contains the full path including .exe
    return path.remove_filename()  // Extract the directory ...
               .w_str();           // ... and convert to a string.
}
Condenser answered 26/1, 2018 at 2:36 Comment(0)
G
0

SDL2 (https://www.libsdl.org/) library has two functions implemented across a wide spectrum of platforms:

  • SDL_GetBasePath
  • SDL_GetPrefPath

So if you don't want to reinvent the wheel... sadly, it means including the entire library, although it's got a quite permissive license and one could also just copy the code. Besides, it provides a lot of other cross-platform functionality.

Geophilous answered 14/3, 2020 at 16:35 Comment(0)
D
0

I didn't read if my solution is already posted but on linux and osx you can read the 0 argument in your main function like this:

int main(int argument_count, char **argument_list) {
    std::string currentWorkingDirectoryPath(argument_list[currentWorkingDirectory]);
    std::size_t pos = currentWorkingDirectoryPath.rfind("/");      // position of "live" in str
    currentWorkingDirectoryPath = currentWorkingDirectoryPath.substr (0, pos);

In the first item of argument_list the name of the executable is integrated but removed by the code above.

Dogface answered 4/2, 2021 at 9:15 Comment(3)
This question is visited pretty often, so I'll say this to any future visitors: Do not trust the operating system!Quickstep
The names argument_count and argument_list are certainly more explicit than the conventional argc and argv, but they're likely to cause confusion for experienced C and C++ programmers.Loisloise
What is currentWorkingDirectory?Gaziantep
A
0

There's a C library dedicated to this: whereami.c.

#include <filesystem>
#include <string>

#include "whereami.h"

auto get_current_exe_path() -> std::string {
    int length = wai_getExecutablePath(NULL, 0, NULL);
    int dirname_length = 0;

    char* cstr = static_cast<char*>(malloc(length + 1));
    wai_getExecutablePath(cstr, length, &dirname_length);
    cstr[length] = '\0';

    std::string result(cstr);

    free(cstr);
    cstr = NULL;

    return result;
}

Supported platforms:

  • Windows
  • Linux
  • Mac
  • iOS
  • Android
  • QNX
  • Neutrino
  • FreeBSD
  • NetBSD
  • DragonFly BSD
  • SunOS
  • OpenBSD
Aloft answered 3/11, 2023 at 2:29 Comment(0)
M
-3

This was my solution in Windows. It is called like this:

std::wstring sResult = GetPathOfEXE(64);

Where 64 is the minimum size you think the path will be. GetPathOfEXE calls itself recursively, doubling the size of the buffer each time until it gets a big enough buffer to get the whole path without truncation.

std::wstring GetPathOfEXE(DWORD dwSize)
{
    WCHAR* pwcharFileNamePath;
    DWORD dwLastError;
    HRESULT hrError;
    std::wstring wsResult;
    DWORD dwCount;

    pwcharFileNamePath = new WCHAR[dwSize];

    dwCount = GetModuleFileNameW(
        NULL,
        pwcharFileNamePath,
        dwSize
    );

    dwLastError = GetLastError();

    if (ERROR_SUCCESS == dwLastError)
    {
        hrError = PathCchRemoveFileSpec(
            pwcharFileNamePath,
            dwCount
        );

        if (S_OK == hrError)
        {
            wsResult = pwcharFileNamePath;

            if (pwcharFileNamePath)
            {
                delete pwcharFileNamePath;
            }

            return wsResult;
        }
        else if(S_FALSE == hrError)
        {
            wsResult = pwcharFileNamePath;

            if (pwcharFileNamePath)
            {
                delete pwcharFileNamePath;
            }

            //there was nothing to truncate off the end of the path
            //returning something better than nothing in this case for the user
            return wsResult;
        }
        else
        {
            if (pwcharFileNamePath)
            {
                delete pwcharFileNamePath;
            }

            std::ostringstream oss;
            oss << "could not get file name and path of executing process. error truncating file name off path. last error : " << hrError;
            throw std::runtime_error(oss.str().c_str());
        }
    }
    else if (ERROR_INSUFFICIENT_BUFFER == dwLastError)
    {
        if (pwcharFileNamePath)
        {
            delete pwcharFileNamePath;
        }

        return GetPathOfEXE(
            dwSize * 2
        );
    }
    else
    {
        if (pwcharFileNamePath)
        {
            delete pwcharFileNamePath;
        }

        std::ostringstream oss;
        oss << "could not get file name and path of executing process. last error : " << dwLastError;
        throw std::runtime_error(oss.str().c_str());
    }
}
Mcwilliams answered 5/2, 2018 at 17:8 Comment(2)
What's the reason for using new and the (wrong) delete? Had you used a std::vector, your code wouldn't have exhibited undefined behavior.Eugenaeugene
Besides, GetModuleFileNameW doesn't set the last error code in case of success. That code is broken in so many ways. Don't use if you happen to stumble across this.Eugenaeugene
T
-4
char exePath[512];
CString strexePath;
GetModuleFileName(NULL,exePath,512);
strexePath.Format("%s",exePath);
strexePath = strexePath.Mid(0,strexePath.ReverseFind('\\'));
Tahitian answered 20/2, 2014 at 14:22 Comment(2)
That's Windows-only and uses MFC, so quite far from being cross-platform, sorry!Colburn
This isn't even the Windows way of doing it, either. Have a look at PathRemoveFileSpec() and related functions instead.Marriageable
W
-4

in Unix(including Linux) try 'which', in Windows try 'where'.

#include <stdio.h>

#define _UNIX

int main(int argc, char** argv)
{
        char cmd[128];
        char buf[128];
        FILE* fp = NULL;
#if defined(_UNIX)
        sprintf(cmd, "which %s > my.path", argv[0]);
#else
        sprintf(cmd, "where %s > my.path", argv[0]);
#endif
        system(cmd);
        fp = fopen("my.path", "r");
        fgets(buf, sizeof(buf), fp);
        fclose(fp);

        printf("full path: %s\n", buf);
        unlink("my.path");

        return 0;
}
Wrest answered 19/3, 2014 at 5:20 Comment(0)
E
-6

As of C++17:

Make sure you include std filesystem.

#include <filesystem>

and now you can do this.

std::filesystem::current_path().string()

boost filesystem became part of the standard lib.

if you can't find it try to look under:

std::experimental::filesystem
Elute answered 18/6, 2019 at 8:55 Comment(1)
This is not the path of the binary, it's the current working directory.Escobedo

© 2022 - 2025 — McMap. All rights reserved.