October 26th, 2026
likemind blown5 reactions

Puzzling out why a program doesn’t work when run from a batch file, but works when double-clicked from Explorer

A customer asked for help writing a batch file to launch a program. For some reason, the program displayed an error message when the batch file ran it, whereas it worked fine when they double-clicked the program from Explorer.

I immediately suspected that they were not setting the current directory.

Some time ago, I noted that when you double-click a file in Explorer, Explorer sets the current directory of the program to the directory containing the thing that you double-clicked. This is done to accomodate programs that look for ancillary files in the current directory. The example I had was a hypothetical LitWare Writer program that, when given an ABC.LIT file, looks for related files like ABC.LTC (the table of contents) and ABC.LDC (the custom dictionary) in the current directory.

This also happens if you double-click the .exe file: The current directory is set to the directory that contains the executable.

My guess is that the customer’s program looks for ancillary files relative to its current directory, but the batch file ran the program by its full path without changing the current directory to match the program’s path.

The customer confirmed that explicitly changing the directory fixed the program. (More specifically, they used pushd to change the directory and then popd to restore the original directory.)

Bonus chatter: It didn’t come to it, but my second idea would have been to check whether the program had set a custom entry under App Paths to ask for additional directories to be added to the PATH environment variable when the program is run.

Topics

Author

Raymond has been involved in the evolution of Windows for more than 30 years. In 2003, he began a Web site known as The Old New Thing which has grown in popularity far beyond his wildest imagination, a development which still gives him the heebie-jeebies. The Web site spawned a book, coincidentally also titled The Old New Thing (Addison Wesley 2007). He occasionally appears on the Windows Dev Docs Twitter account to tell stories which convey no useful information.

10 comments

Sort by :
  • Shawn Van Ness

    I was expecting some weird case of a GUI app trying to alloc its own console window.. but failing when it already inherited one.

    I think I’ve seen that happen, but I can’t remember the details off hand.

  • LB

    I long for a day when a modern operating system does not have any concept of a “current directory”. Global mutable state with security implications. At least UWP fixed the “mutable” and “security” parts. Still don’t know why they thought Windows Terminal needed to be able to change the classical working directory global instead of using their own…

    • Henke37 2 days ago

      It’s process scope, so it is debatable if you want to call it global or not.

      • LB 2 days ago

        From the perspective of multiple threads running in the process, the working directory could be changed at any time by some other thread, and the Win32 API function for retrieving the current working directory warns about this.

  • Yexuan Xiao · Edited

    There are many ways to specify the absolute path of an exe before starting it, but doing so for a dll linked via a lib is much harder, or even infeasible.
    Windows lacks a way to specify dll load paths in the manifest (for example, searching for a dll relative to itself). This means that when exes is in different directories, the same dll must also be copied into those directories. This prevents many dlls from being shared. Hardlinks are one workaround, but I don't consider it ideal. Windows does have such mechanisms, such as mui and side-by-side, but they...

    Read more
  • Jonathan Harston

    This is why my programs, when they start, ask the system for the path to the file that’s been run, and then uses that to reference other files it expects in the same directory.
    GetModuleFileName to path
    Drop leaf from path
    load path+”otherfile”
    load path+”anotherfile”
    load path+”someotherfile”
    etc.

    I thought this was obvious, it’s been obvious to me since the early 1980s, it never occured to me to do it any other way.

    • Todd Bandrowsky 6 hours ago · Edited

      For my MSIX apps in the MS Store, I've just gone like the other posters said and have my application aware of its own environment. My startup basically says, "what's my current exe, am I in debug, or not, and let's go find out where our data lives in localappdata.
      <code>

      Read more
      • LB

        @Todd Bandrowsky: Your code has some issues, for example on Windows the std::filesystem stuff is all wchar_t internally, so there's no reason to use GetModuleFileNameA instead of GetModuleFileNameW if you're just going to hand the result directly to std::filesystem, it just results in needless lossy conversions. Also, the 500 byte butter should really be a loop with a dynamic buffer that you can grow if GetModuleFileName reports that your buffer is too small, in case the user has enabled long paths and installed your app in some deep path. You also have a memory leak related to that explicit CoTaskMemFree...

        Read more
    • Frédéric B. 3 days ago

      Over time, I would have thought more and more programs would be carefully designed to look for files in their own directory rather than the current one, but ASP.NET Core projects disabused me of this notion.