Cross-platform "GetAppPath()"

December 15, 2011 —

Sometimes being able to get the path that contains the currently running executable is handy. I've got my own function that does this which works on Windows, Linux and now OS X (with the help of my new Hackintosh).

Here it is:

#include <stdio.h>
#include <string>
#ifdef _WIN32
#define WIN32_LEAN_AND_MEAN
#define WIN32_EXTRA_LEAN
#include <windows.h>
#elif __linux__
#include <stdio.h>
#elif __APPLE__
#include <stdlib.h>
#include <mach-o/dyld.h>
#include <sys/param.h>
#else
#error Unsupported platform
#endif
 
std::string g_appPath;
 
const std::string& GetAppPath()
{
    if (g_appPath.length() > 0)
        return g_appPath;
 
#ifdef _WIN32
    char pathBuffer[MAX_PATH + 1];
    DWORD result = GetCurrentDirectoryA(MAX_PATH, pathBuffer);
    if (!result)
        return g_appPath;
 
    std::string path = pathBuffer;
    g_appPath = path + "\\";
#elif __linux__
    std::string path = "";
    pid_t pid = getpid();
    char buffer[20] = { 0 };
    sprintf(buffer, "%d", pid);
    std::string link = "/proc/";
    link.append(buffer);
    link.append("/exe");
    char proc[512];
    int ch = readlink(link.c_str(), proc, 512);
    if (ch != -1)
    {
        proc[ch] = 0;
        path = proc;
        std::string::size_type t = path.find_last_of("/");
        path = path.substr(0, t);
    }
    g_appPath = path + "/";
#elif __APPLE__
    uint32_t size = MAXPATHLEN;
    char *pathBuffer = new char[size];
    int result = _NSGetExecutablePath(pathBuffer, &size);
    if (result == -1)
    {
        delete[] pathBuffer;
        pathBuffer = new char[size];
        result = _NSGetExecutablePath(pathBuffer, &size);
        if (result == -1)
            return g_appPath;
    }
    char *realPathBuffer = realpath(pathBuffer, NULL);
    if (realPathBuffer == NULL)
        return g_appPath;
 
    std::string appPath = realPathBuffer;
    delete[] pathBuffer;
    free(realPathBuffer);
 
    // _NSGetExecutablePath actually returns the path to and including the
    // currently running executable, but we just want the path containing the
    // executable.
    std::string::size_type pos = appPath.find_last_of('/');
    g_appPath = appPath.substr(0, pos + 1);
#endif
    return g_appPath;
}

As an example, if you compiled this and ran it where the compiled executable was located at /home/gered/getapppathtest, then this function would return /home/gered/ for all platforms.

Maybe not the cleanest or best version, but it works well enough for me. Enjoy.