42

I need to access some files with fstream in my C++ app on Windows. Those files are all located in subfolders of the folder where my exe file is located.

  • What is the easiest and more important: safest way to get the path to the folder of the current executable?
sean e
  • 11,792
  • 3
  • 44
  • 56
asas
  • 431
  • 1
  • 4
  • 4
  • Something more sophisticated than using argv[0] that is a parameter to int main (int argc, char *argv[]) and parsing it? – Minok Jan 08 '15 at 21:54

3 Answers3

59

Use GetModuleFileName to find out where your exe is running from.

WCHAR path[MAX_PATH];
GetModuleFileNameW(NULL, path, MAX_PATH);

Then strip the exe name from path.

Mofi
  • 46,139
  • 17
  • 80
  • 143
sean e
  • 11,792
  • 3
  • 44
  • 56
  • 2
    Is there any way to do this without the Win32 API? – asas Apr 15 '10 at 17:14
  • 3
    @asas: No, not an easy and safe way. – Adrian McCarthy Apr 15 '10 at 17:15
  • 2
    @asas: Writing a C++ Windows application without using Win32 API? Like using Qt instead? – sean e Apr 15 '10 at 17:15
  • 1
    sean e: Yes, without the Win32 API. It isn't a GUI app - it's an interpreter I currently write in MSVC which shouldn't be too hard to port - Win32 API makes it hard to port. – asas Apr 15 '10 at 17:16
  • 1
    @asas: ahh - no experience there. Might want to look at a portable library - maybe apache portable runtime library: http://apr.apache.org/ or the Netscape Portable Runtime: http://www.mozilla.org/projects/nspr/ – sean e Apr 15 '10 at 17:20
  • @asas You'd probably do well to have a platform-specific-functions subset of the code in a designated place. There are some things you just can't port without losing a lot of benefits, or even at all. – Yam Marcovic Sep 29 '14 at 08:34
  • 3
    I know this is really old, but isn't `GetModuleFileNameW(NULL, path, MAX_PATH)` enough? – Mukesh Sai Kumar Sep 18 '19 at 13:48
  • 1
    Works for me @MukeshSaiKumar – bluedog Feb 04 '20 at 03:09
33

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);

Update: PathRemoveFileSpec is deprecated in Windows 8. However the replacement, PathCchRemoveFileSpec, is available in Windows 8+ only. (Thanks to @askalee for the comment)

I think this code below might work, but I'm leaving the above code up there until the below code is vetted. I don't have a compiler set up to test this at the moment. If you have a chance to test this code, please post a comment saying if this below code worked and on what operating system you tested. Thanks!

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;

    DWORD length = GetModuleFileName( NULL, dest, destSize );
#if (NTDDI_VERSION >= NTDDI_WIN8)
    PathCchRemoveFileSpec(dest, destSize);
#else
    if (MAX_PATH > destSize) return NULL;
    PathRemoveFileSpec(dest);
#endif
    return dest;
}

mainProgram.cpp

TCHAR dest[MAX_PATH];
GetThisPath(dest, MAX_PATH);
Nate
  • 12,963
  • 4
  • 59
  • 80
  • 8
    +1 for `PathRemoveFileSpec`, didn't know that. – lambdas Jan 13 '14 at 05:39
  • 3
    It seems that PathRemoveFileSpec is deprecated: http://msdn.microsoft.com/en-us/library/windows/desktop/bb773748(v=vs.85).aspx, use PathCchRemoveFileSpec instead. – askalee Apr 25 '14 at 07:39
  • 2
    @askalee it seems worth being noted that it's supported only by Win8 (and above) desktop apps – wonko realtime May 08 '14 at 13:16
  • Was about to implement it when I realised that this will not work ABOVE Windows 8. E.g. Windows 8.1 is `NTDDI_WINBLUE`, not `NTDDI_WIN8`. You'll be wanting something like ` #if (NTDDI_VERSION >= NTDDI_WIN8)` – Warpspace Oct 24 '16 at 07:49
  • `PathCchRemoveFileSpec` don't working, l'm call from `#pragma comment(lib, "Pathcch.lib")`but I get an error `argument type "TCHAR *" is incompatible with the parameter type "PWSTR"` – GooliveR Apr 02 '18 at 19:17
  • @GooliveR A [`TCHAR *`](https://msdn.microsoft.com/en-us/library/office/cc842072.aspx) is a pointer to a 8 or 16 bit character array depending on your platform. A `PWSTR` is always 16 bit unicode character array pointer. You [may not be able to convert directly between them](https://stackoverflow.com/a/7448175/761771) – Nate Apr 03 '18 at 14:28
  • @Nate, Well, I'll try, tell me why this line: `DWORD length =`? After all, in your example, it does not check anything. – GooliveR Apr 03 '18 at 19:10
  • The original version works for me on win10. – SomethingSomething Dec 13 '21 at 22:20
  • @GooliveR WTF was past me thinking. I actually have no idea, and it's been so long I don't remember any context... :/ – Nate Dec 15 '21 at 04:16
  • @SomethingSomething thank you. – Nate Dec 15 '21 at 04:17
-1

By default, the directory that the exe is run from should be the starting location. So opening a file in a subfolder should be as easy as

fstream infile; 
infile.open(".\\subfolder\\filename.ext");

from within your program.

However, there is no real way to GUARANTEE this will always work unless you either use a framework that wraps the needed features (I'd look at boost), or using the Windows API directly such as GetModuleFileName (as sean e suggested)

KevenK
  • 2,975
  • 3
  • 26
  • 33
  • 3
    What? Have your ever heard of *run in*? You don't have to tell me that the path where the exe runs in is the default... – asas Apr 15 '10 at 17:30
  • With all due respect, I have no idea what you just tried to say. I'm not sure what you are referencing with *run in*. Also, text on a web page doesn't necessarily share the meanings of your innuendo and ellipses. So please explain what your second sentence is intended to mean. – KevenK Apr 16 '10 at 04:37
  • 3
    In case you misunderstand: Each process has it's own value representing the current directory. By default, this value should be set to the directory in which the executable file is located. You can check this directory using the API calls for `GetCurrentDirectory()` or set it using `SetCurrentDirectory()`. While I realize this isn't an exact solution to your problem (as I stated in the original text), the information is not wrong http://msdn.microsoft.com/en-us/library/aa363806.aspx. If you felt that's worth a down-vote, I'm not sure you understand the intended use of the voting system here. – KevenK Apr 16 '10 at 04:43
  • 2
    He is probably referring to the "Start in" option where you can specify the starting working directory in a Windows Start menu program link. – sean e Apr 16 '10 at 04:50
  • 1
    The answer is incomplete, but I don't think it deserves these downvotes. – Alexandre Pereira Nunes Apr 07 '15 at 11:38
  • 3
    In addition to shortcuts which have an explicitly specified working directory, the current working directory will be inherited from the parent process when launching programs from `cmd` for example. You really can't assume the CWD has anything to do with the executable image's location. – bcrist Apr 13 '15 at 04:26
  • 1
    Windows programs have the concept of "current directory" which isn't necessarily the same thing as which folder they're physically in. And the times the two directories don't match (e.g., when running in Visual Studio) are rare enough that you'll go crazy when you encounter them. And "run it" only leads to "works on my machine" (when I run it in a way that the physical location matches the current directory). So, yes, this answer is correct to suggest things are a little more complicated in Windows. – Max Lybbert Aug 25 '15 at 14:06
  • If you run a program from a command line (cmd, PowerShell, etc), I'm fairly sure its current directory will be the current directory from which you launched it, not the directory containing the executable. There is no "default", – Keith Thompson Aug 23 '21 at 21:01