Page 1 of 1

How to handle stored folder paths?

Posted: Tue May 21, 2013 10:08 am
by victimofleisure
Hi, I'm the developer of WaveShop, which is already part of the collection and was mostly discussed here. My question is, how do other developers deal the problem of drive letters in stored folder settings?

For example WaveShop needs to store an output folder for recordings. The issue is that absolute paths may behave unexpectedly if the app is being run from a USB drive, because drive letter assignments vary from machine to machine. One user is working around it by entering a relative path for the output folder, e.g. ".\Recordings", which he intends to be a subfolder of the application folder. This is a flawed solution however, because ".\Recordings" is relative to WaveShop's current directory, and that current directory can easily change without him realizing it. Initially it's the application folder (though even this is not guaranteed), but if he uses File/Open to open a document in some other folder, the current directory changes to that folder (e.g. X:\foo), and his next attempt to record will likely fail because X:\foo\Recordings doesn't exist.

It seems to me that relative paths are a bad idea, and I proposed disallowing them but the user objected. I know portable apps are often packaged with a launcher that modifies the INI file before launching the app. Is this how such problems are solved? I enclose an image of the record options dialog below so you can more easily see the problem.

Cheers,

Chris

Image

Re: How to handle stored folder paths?

Posted: Tue May 21, 2013 10:43 am
by Ascend4nt
Relative paths should be considered relative to the main executable, not the current directory.

To get the executable directory, you can use Windows' GetModuleFileName and then strip the executable name from the end of the path (do a reverse search for the '\' character and cut off the rest). There's also a CRT function but I forget offhand what it is.

Re: How to handle stored folder paths?

Posted: Tue May 21, 2013 12:01 pm
by Napiophelios
victimofleisure wrote:I know portable apps are often packaged with a launcher that modifies the INI file before launching the app. Is this how such problems are solved?
No,no,no....that is what the guy you told that to will do when you dont rewrite your program to comply with his views :mrgreen:

Re: How to handle stored folder paths?

Posted: Tue May 21, 2013 2:08 pm
by victimofleisure
Ascend4nt wrote:Relative paths should be considered relative to the main executable, not the current directory.
Thanks for the reply. In my view this only correct behavior for a portable app however; for an installed app, the app folder is read-only from the user's POV (enforced in Vista and later via UAC etc). So this is a case where installed and portable behavior should differ: the portable version should detect relative paths and make them relative to the app folder, whereas the installed version should either make them relative to the user's profile (e.g. App Data or My Documents) or not allow relative paths at all.

Re: How to handle stored folder paths?

Posted: Tue May 21, 2013 5:44 pm
by Ascend4nt
victimofleisure, I'm aware of the Vista+ enforced guideline of saving settings outside of the program directory, however this shouldn't affect the way you view relative paths.

Any relative paths saved as part of an application's settings or entered into configuration settings within the program should be relative to the application, regardless of where it's installed or where it saves its settings. That's what users have come to expect. Additionally it allows you to run the program in one of two ways depending on where the program is run from (C:\Program Files\MyApp vs. E:\PortableApps\MyApp).

Installed apps should write to the AppData folder(s) and/or the Registry. Portable programs can just save everything in the same folder. To keep it simple, check whether the app is installed or not on startup, then direct settings to either %appdata%\MyApp or %exepath%\MyApp. And of course use %exepath% in combination with relative paths.

However, there is one scenario I can think of when relative paths get a little trickier: program parameters. When parsing the command line, if there's a parameter which has a relative path, you might look in both the executable path and the current directory. That's something you'd need to determine based on what type of parameter it is [input/output] and how intuitive it is to the user. Also if you use the current directory, save it as a first measure on startup, so that you can reuse it without worrying if the current directory has changed later.

Something more persistent [across sessions] is where relative paths should always originate from the executable location. I'd say 'IMO' but honestly this is what most every Windows user and programmer expects..

Re: How to handle stored folder paths?

Posted: Tue May 21, 2013 8:50 pm
by victimofleisure
Ascend4nt wrote:Any relative paths saved as part of an application's settings or entered into configuration settings within the program should be relative to the application, regardless of where it's installed or where it saves its settings.
Thanks for the advice. I have (hopefully) followed it correctly, by making relative folder paths relative to the exe folder for the portable version, and relative to the special (profile) folder App Data for the MSI version. This will satisfy portable users, while avoiding UAC trouble in the MSI version. It's still my opinion that in the MSI version, relative folder paths provide no advantage and are effectively a degenerate case, but whatever. I build and distribute different binary packages for portable vs. MSI, so it's no problem for their behavior to differ slightly.

I enclose the relevant function. Just to clarify, what I call "app folder" below, you're calling "exepath", but I think the difference is only semantic: it's the folder that results from stripping the filespec from the first command-line argument.

Code: Select all

void CWaveShopApp::MakeAbsolutePath(CString& Path) const
{
	if (PathIsRelative(Path)) {	// if path is relative
		CPathStr	AbsPath;
#ifdef PORTABLE_APP	// if portable app
		AbsPath = GetAppFolder();	// make path relative to app folder
#else	// not portable app; make path relative to app data in profile
		GetSpecialFolderPath(CSIDL_APPDATA, AbsPath);
#endif
		AbsPath.Append(Path);
		Path = AbsPath;
	}
}

Re: How to handle stored folder paths?

Posted: Wed May 22, 2013 12:06 am
by guinness
Ascend4nt wrote:There's also a CRT function but I forget offhand what it is.
PathRelativePathToW?

Re: How to handle stored folder paths?

Posted: Wed May 22, 2013 5:16 am
by Ascend4nt
victimofleisure, yes I meant 'app folder' by "%exepath%", sorry for the confusion. What you have looks fine, although I'd still argue relative paths from even "C:\Program Files\App" can be handled - you just need to check that the final path is writable. Also, you can use something like PathCanonicalize after combining paths to get rid of relative elements.
guinness wrote:
Ascend4nt wrote:There's also a CRT function but I forget offhand what it is.
PathRelativePathToW?
Nah, that's a Windows API function. CRT is the C runtime library. The function i was looking for is _get_wpgmptr, and is actually a Microsoft-specific extension to the CRT (anything starting with an underline is a compiler extension).