Skip to content

Win32 Tip: Set Working Directory to App Directory

It is common practice to use relative paths for file IO in game applications. Some games write saves and logs to the user’s home directory using an absolute path, but the game’s resources generally reside in the subdirectories beneath the application, if not on removable media such as a CD or DVD. Usually, relative paths work fine. Where you run into trouble, though, is when the user starts the game from the command-line outside of the application directory. When that happens, the working directory is the directory where the command prompt is currently pointing to and all of your relative file paths will be broken.

On Linux systems, it might not be uncommon for people to run apps from the command line. On Windows, I think it’s a safe assumption that it’s a rare event. But you can run into problems with Windows from the desktop. When you create a desktop or Start menu shortcut to your app during installation, you can (and should) configure the shortcut to use the app’s working directory when it is activated. Unfortunately, users will sometimes inadvertantly screw up the shortcut properties and break things for you. One of your goals as a game developer should be to take whatever preventative measures you can to reduce future support costs. The solution in this case is to programmatically set the working directory when the game starts up, before calling any resource loading code.

In order to set the working directory to the app directory, you first have to know where the app is. One way to do this is to fetch the first command line argument passed to the main function (argv[0]). This solution doesn’t work on all platforms, but as long as it works on the platform(s) you target then it’s a simple way to do it. You need to strip the file name off of the end of the path and you’re golden. This is the approach I used during development of a few C applications over the past few years. When creating an application that has a ‘WinMain’ entry point rather than ‘main’,  you have to call the Win32 API function GetCommandLine for it to work, as the command line parameter passed to WinMain does not contain the app path. Unfortunately, there’s a major gotcha with this approach.

I tend to do most of my C development on Windows with makefiles, gcc (MingW), gdb, MSYS, and a good text editor. The result is that I wind up testing my applications from the MSYS command prompt rather than the Windows console (a.k.a. ‘DOS’ box). MSYS behaves like Linux - when the app starts up you’ll be getting the full path to the executable in argv[0] (or as the first token in the string returned by GetCommandLine). When running from the Windows console, you get the file name only. This means that, on Windows, you cannot rely on the command line to determine the full app path. But all is not lost.

The Win32 API can help you here, specifically the function GetModuleFileName. Give this function a module handle and it will return the fully qualified path to that file, provided that the module was loaded by the current process. In Win32 terminology, a module is normally a DLL or an executable. So all you need to do is to fetch the module handle for your application and pass it to GetModuleFileName. Actually, the function provides a shortcut for you - instead of passing a module handle just pass 0 (or NULL). The result is that you will have the fully qualified path to your executable.

When the term ‘fully-qualified’ is used in reference to a file path, that means that the file name is part of the path. So before setting the working directory, you’ll need to strip the file name from the string returned by GetModuleFileName. Once that’s done, pass the result to SetCurrentDirectory and you’re done. Here’s some example C code:


// this will store the fully-qualified path
char appPath[MAX_PATH] = "";

// fetch the path of the executable
GetModuleFileName(0, appPath, sizeof(appPath) - 1)

// get a pointer to the last occurrence of the windows path separator
char *appDir = strrchr(appPath, '\\');
if(appDir)
{
    // SetCurrentDirectory expects the path string to be null-terminated and for
    // the last character to be a backslash. However, if the backslash is absent,
    // it will be added by SetCurrentDirectory. So to add the null terminator, it's
    // probably best to go ahead and increment to the next position and add it
    // there rather than overwriting the backslash:
    ++appDir;

    // always expect the unexpected - this shouldn't be null but one never knows
    if(appDir)
    {
        // null terminate the string
        *appDir = 0;

        // set the executable's directory as the working directory
        SetCurrentDirectory(appPath);
    }
}

This little code snippet will ensure that your game’s working directory is always the application directory, no matter from where it is launched. On other platforms you may be able to get away with argv[0], or perhaps some platform-specific API calls, to determine the app path.This will ensure that your relative paths won’t break. Of course, nothing is 100% guaranteed. You can bet that someone will do something silly, such as deleting the resource files or moving them to another directory. Of course, you’ll be prepared to fail gracefully in that case, right?.

Technorati Tags: , , ,

{ 2 } Comments

  1. Nathan Blomquist | March 4, 2008 at 9:36 pm | Permalink

    Great little post!

    One problem:
    The call to SetCurrentDirectory() should have appPath passed in, not appDir. strrchr() returns a pointer to the last position of the character in the original string. Therefore setting it to 0 (zero) results in an empty string beginning at appDir. Passing appPath to SetCurrentDirectory works because appDir is set to 0 (zero), which is really just pointing into appPath!

    – Nate

  2. gdmike | March 30, 2008 at 10:34 am | Permalink

    Nice catch! Corrected in the post.

Post a Comment

Your email is never published nor shared. Required fields are marked *