Sign in to follow this  
Lode

current working directory for running exe

Recommended Posts

Hi, My program loads some data from files that are in the same directory as its executable. It uses relative paths internally to find these files. If the program is run while the directory in the exe is the "current working directory", then everything goes fine. In Linux, this is ok only when running the program from a terminal inside that directory. In Windows, it works when doubleclicking the exe in explorer. However I now added a command line option to my program to open a file, and I tried in Windows explorer to do "Open with..." and then choose my program. The program can't start because it can't find its data files that are in the directory of its own exe. So I think the cause is that, if you do "Open With..." in Windows explorer, then it apparantly has the same effect as Linux has when running the program while having another current working directory. The relative paths to the data files don't work anymore... So basically this is a problem and I've got a few questions to be able to fix the problem: -is it indeed so that Windows's "open with" option runs the exe with a different current working directory, than the one in which the exe is? -what is the good way to load data files that belong to your program in Windows? -and what is the good way to do this in Linux? -can I keep the data files next to the exe for these good ways above? I prefer it that way for multiplatform reasons (imagine having the data files in some different folder on every OS) and to be able to easily install the program by unzipping it somewhere along with its exe and data files. -Is there a cross platform way to find out in which directory the executable/binary of your program that is running, is located? -I don't seem to find any settings in windows file associations to set a working directory, is there maybe a hidden setting for this or so? I know that what windows displays to the user related to file associations is very limited compared to what it internally can... Thanks! [Edited by - Lode on December 24, 2009 4:56:39 PM]

Share this post


Link to post
Share on other sites
As you've discovered, using relative paths in windows is rarely a good idea.

At the start of your windows app, you can call GetCommandLine(). The first argument in that string is (or should be) the fully qualified path to your app. Parse the string for your executable directory and prepend that directory string to your relative paths. I used a "canned" set of std::string's at the beginning of most of my apps to store "appPath," "resourcePath," etc., which I can then prepend to filenames, etc. E.g., string fileName = appPath; fileName.append("fileInAppDir.txt");

In windows, the user can create shortcuts with the option to start the app specifying any working directory. The GetCommandLine() method avoids the problems you've seen.

Share this post


Link to post
Share on other sites
Quote:
Original post by Buckeye
As you've discovered, using relative paths in windows is rarely a good idea.

At the start of your windows app, you can call GetCommandLine(). The first argument in that string is (or should be) the fully qualified path to your app. Parse the string for your executable directory and prepend that directory string to your relative paths. I used a "canned" set of std::string's at the beginning of most of my apps to store "appPath," "resourcePath," etc.

In windows, the user can create shortcuts with the option to start the app specifying any working directory. The GetCommandLine() method avoids the problems you've seen.


Hey, that's interesting, I checked the specification of GetCommandLine() on MSDN and it turns out to return the same as the "int argc, char* argv[]" arguments of the main function (which I use, it's an SDL application).

So basically, the first command line argument is always the full path to your exe on Windows? That's really handy!

So basically, I'd have to remove the last part (the filename of the exe) of that string and I'd have the path I want. This does sound a bit shady to me, is this method used in the industry?

Share this post


Link to post
Share on other sites
If the app is run from the command-line, that path is not the fully qualified path, but the command-line used to run the program. However, at that point the current working directory is the directory in the console, and SetCurrentDirectory works both with relative and absolute paths, so the resultant directory should still always be the directory the exe is in. I believe chdir works the same way on Unix.
You probably need some different code for Windows and Linux, unless you can use system("cd ..") or something.. if that even works.. I haven't tried it but it might. =)

To get the exe path you can use GetModuleFileName. I assume there's a similar one on Linux.

Share this post


Link to post
Share on other sites
Quote:
Original post by Lode
-is it indeed so that Windows's "open with" option runs the exe with a different current working directory, than the one in which the exe is?
I believe "Open With..." (and file associations in general) will start your program with the current working directory set to the directory where the file you're loading is. There are other things that can change the working directory as well (for example, GetOpenFileName will change it)

I do a similar thing as Buckeye in that I work out the path to the executable and then use fully-qualified paths to files that are relative the executable. I usually use GetModuleFileName to get the path, though (pass in NULL as the first argument to get the path to the executable).

Share this post


Link to post
Share on other sites
GetModuleFileName will give you the fully qualified path to the exe, from which you can extract the exe directory. As far as I know, relying on the full path to be passed as the first argument is non-standard and subject to change in future.

I use the following to locate my exe directory:


std::string ExecutablePath()
{
char c[MAX_PATH];
GetModuleFileName(GetModuleHandle(NULL),c,MAX_PATH);

std::string S=c;
return S.substr(0,S.find_last_of("\\/"))+"\\";
}


This is only for reading files though - writing to the exe directory is considered bad form now as your exe probably won't have permission and you should write to the app data directory, found with ShGetKnownFolderPath.

Share this post


Link to post
Share on other sites
Quote:
GetCommandLine() on MSDN and it turns out to return the same as the "int argc, char* argv[]" arguments of the main function (which I use, it's an SDL application).

Note that the statement is only true if the application uses the "main" (vs. WinMain) function to start the application. The lpCmdLine argument in WinMain does not include the program name. That's why I generalized to the GetCommandLine function.

However, it appears, as mentioned by Aardvajk, that GetModuleFileName() would be the more reliable function to use.

@Guinnie: for the reasons mentioned above, the current directory may or may not be the directory for the executable, even at the start of the app.

Share this post


Link to post
Share on other sites
Quote:
Original post by Erik Rufelt
Regular users can't write to the common app-data directory either, so to use that method you must make your installer create a sub-directory in the app-data folder, and change permissions for it.


Really? I didn't know that. How do you change the permissions? Can you post some code?

Share this post


Link to post
Share on other sites
I guess any executable that is run as administrator can change the permissions, but I'm not sure exactly what functions are used for it. Perhaps SetSecurityInfo can do it.

When creating an installer, the installer creator should have an option for it. I don't have much experience with it, but with Inno Setup for example it's easy to add the permissions, http://www.jcmiras.net/jcm/item/27/.
I found with Google that MSI installers can change it with the LockPermissions table (from Orca or whatever installer creator is used).

Share this post


Link to post
Share on other sites
Quote:
Original post by Erik Rufelt
I guess any executable that is run as administrator can change the permissions, but I'm not sure exactly what functions are used for it. Perhaps SetSecurityInfo can do it.

When creating an installer, the installer creator should have an option for it. I don't have much experience with it, but with Inno Setup for example it's easy to add the permissions, http://www.jcmiras.net/jcm/item/27/.
I found with Google that MSI installers can change it with the LockPermissions table (from Orca or whatever installer creator is used).


Cheers.

Share this post


Link to post
Share on other sites
Quote:
Original post by Erik Rufelt
Regular users can't write to the common app-data directory either


I don't think that's the case, this is the ACL I get on the ProgramData folder on 7 Ultimate and Server 2k8 SP2 which I'm sure I haven't modified.


Security Info for c:\ProgramData

Owned by: BUILTIN\Administrators

ACCESS RIGHTS
-----------------

NT AUTHORITY\SYSTEM has access 0x1f01ff and can:
Delete the Object
Read the Security Descriptor
Write/Amend the Object DACL
Write/Amend the Object Owner
Use Synchronization Functions on the Object

Access All Object Specific Permissions

BUILTIN\Administrators has access 0x1f01ff and can:
Delete the Object
Read the Security Descriptor
Write/Amend the Object DACL
Write/Amend the Object Owner
Use Synchronization Functions on the Object

Access All Object Specific Permissions

CREATOR OWNER has access 0x10000000 and can:
Request All Object Specific Permissions

BUILTIN\Users has access 0x1200a9 and can:
Read the Security Descriptor
Use Synchronization Functions on the Object

List Directory Contents
Read Extended Attributes and Properties
Traverse Directory

BUILTIN\Users has access 0x116 and can:

Create Subdirectory
Add File to Directory
Write Extended Attributes and Properties




Edit: The DACL seems more permissive than the one in XP, which does disallow write access for BUILTIN\Users but allows it for Authenticated Users instead.

Quote:

I guess any executable that is run as administrator can change the permissions, but I'm not sure exactly what functions are used for it

For files, the easiest way is a combination of filling out an EXPLICIT_ACCESS struct, GetNamedSecurityInfo to get the current ACL (you can specify NULL for all the other output parameters), SetEntriesInAcl to create a new one containing the new ACE and then SetNamedSecurityInfo to actually update the SD. There's also a sample.

[Edited by - adeyblue on December 25, 2009 2:08:20 PM]

Share this post


Link to post
Share on other sites
I see. It failed for me during some testing on Vista, but perhaps it was only for the guest user. I see that on my system there's "special permissions" which allow writing for Users here too..
It makes sense that the guest users shouldn't change shared application settings, but I used a library that require write access to it's files whenever the program is run.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this