Delphi TOpenDialog/TSaveDialog last used path - windows

Referring my question to this answer: https://stackoverflow.com/a/4016075/698266, in particular step 3 says "Otherwise, if the application has used an Open or Save As dialog box in the past, the path most recently used is selected as the initial directory."
Where does Windows save this information?
Note: by experimenting, it seems to be linked to the application file name without its path - i.e. the same executable copied in different directories "sees" the same last path information, while changing the exe file name makes the dialogs point to the user's Documents directory.
My actual interest is for testing purposes. I need to "reset" this information in order to test my application in conditions similar to a first run.

Windows XP uses HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Explorer\ComDlg32\LastVisitedMRU and the format of each item seems to be ExeFilename+Path with both strings zero terminated and in UTF-16LE format. The MRU list is stored as a string named MRUList.
Newer versions of Windows uses HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Explorer\ComDlg32\LastVisitedPidlMRU and HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Explorer\ComDlg32\LastVisitedPidlMRULegacy and the format seems to be ExeFilename+ItemIdList (ExeFilename in UTF-16LE and zero terminated). The MRU list seems to be a list of DWORDs in a binary value named MRUListEx and the list is terminated by 0xffffffff.
I would assume that the change happened in Vista because that is when the new IFileDialog was added. LastVisitedPidlMRULegacy is probably used when GetOpen/SaveFileName is called with a custom template and/or hook function.

I finally found the answer myself.
For Windows 10 (this may be different in different versions of Windows, as David pointed up) there's a list of values in the registry that keep track of the executable name and its associated last "visited" path.
The list can be found in this key:
HKCU\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\ComDlg32\LastVisitedPidlMRU
In order to reset the default open/save path for a particular program, you have to find the value whose data string (UNICODE) starts with your executable name and delete it. If you watch at the data string, you'll notice that the last used path is there, after the executable name.

Related

What is the internal format of the binary registry keys under LastVisitedPidlMRU and LastVisitedPidlMRULegacy?

The first part is the app exe name in null-terminated Unicode, which is easy enough. Past that, each level of the path is included, first in non-Unicode, then Unicode (except for the path root/drive, which is just non-Unicode), but separated by unknown data structures.
My underlying problem is that the .Net Reportviewer control's UI export button calls the common dialog, but the control doesn't expose any way of setting the initial folder. I'd like to reliably read/write these keys to manage default open/save behavior.
Example registry key dump (app is notepad.exe, path is "C:\Stuff\DLTemp\HattoriHanzo"):
"10"=hex:6e,00,6f,00,74,00,65,00,70,00,61,00,64,00,2e,00,65,00,78,00,65,00,00,\
00,14,00,1f,50,e0,4f,d0,20,ea,3a,69,10,a2,d8,08,00,2b,30,30,9d,19,00,2f,43,\
3a,5c,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,4c,00,31,00,\
00,00,00,00,87,48,c0,8a,10,00,53,74,75,66,66,00,38,00,08,00,04,00,ef,be,7b,\
44,5a,69,87,48,c0,8a,2a,00,00,00,1f,d8,01,00,00,00,ca,04,00,00,00,00,00,00,\
00,00,00,00,00,00,00,00,53,00,74,00,75,00,66,00,66,00,00,00,14,00,50,00,31,\
00,00,00,00,00,87,48,b1,95,10,00,44,4c,54,65,6d,70,00,00,3a,00,08,00,04,00,\
ef,be,1c,45,60,7e,87,48,b1,95,2a,00,00,00,61,5b,02,00,00,00,62,00,00,00,00,\
00,00,00,00,00,00,00,00,00,00,00,44,00,4c,00,54,00,65,00,6d,00,70,00,00,00,\
16,00,5e,00,31,00,00,00,00,00,87,48,ca,9a,10,00,48,41,54,54,4f,52,7e,31,00,\
00,46,00,08,00,04,00,ef,be,87,48,ab,90,87,48,ca,9a,2a,00,00,00,e3,e3,04,00,\
00,00,0e,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,48,00,61,00,74,00,74,\
00,6f,00,72,00,69,00,48,00,61,00,6e,00,7a,00,6f,00,00,00,18,00,00,00
You can decode the remaining bytes using the SHGetPathFromIDListW Windows API call.
For example, see the C# implementation of it at https://svcperf.codeplex.com/SourceControl/latest#src/Viewer/UIUtils/MruFileHelper.cs , but that class decodes OpenSavePidlMRU, which doesn't contain the executable name. You can do the same with the remaining bytes that you get from LastVisitedPidlMRU.

Recent pseudo-documents per application in Windows

If you look at Classic Shell Start menu, you see that it has a section for recent applications. Every item in the section can contain popup menu, usually used for displaying recent documents, opened earlier in the application. For instance, the 'Word' item contains all .doc and .docx files recently opened in Word. I know a way to build such list: get all links from the 'Recent' folder and filter out matched documents, by .exe name.
But Classic Shell Start menu also shows popup menu for programs like RDC. For RDC client, namely, it shows a list of recent connections. It seems there is a concept of pseudo-documents in Windows, but I have never heard of it. How to get such list for a random .exe file?
Regards,
The shell maintains the list of recent documents. SHAddToRecentDocs allows a program to add an item to the list. The item can be:
A path and filename
A PIDL representing the shell object
(Windows 7+) A SHARDAPPIDINFO or SHARDAPPIDINFOIDLIST structure that identifies a shell item, and associated application.
(Windows 7+) An IShellLink
The key idea from the list and documentation is that the item can be a PIDL, which can represent a fancy kind of 'file'. (What you called a pseudo-document. This is an oversimplification. Read the documentation.)
To obtain the recent files list, use SHGetFolderLocation specifying CSIDL_RECENT and use the returned PIDLIST_ABSOLUTE to iterate the shell items.
If the item is not a file the list can be retrieved through the COM IApplicationDocumentLists which requires an Application User Model ID. This excludes pinned items, for which there is no programmatic access for the same reason that there is no access to the start menu pin list.
There are a lot of caveats to this, which is best explained by the documentation:
SHAddToRecentDocs
Managing the File System
(edited to add information about the jump list, and missing IShellLink from the list.)

What are R for Windows' "Current Version" and "Install Path" registry entries used for?

Here are the two registry entries I'm asking about.**
HKEY_LOCAL_MACHINE\Software\R-core\R\Current Version
HKEY_LOCAL_MACHINE\Software\R-core\R\version\InstallPath
When R is launched from the command line (or from emacs, or statconnDCOM for that matter) it uses whatever version appears first in Windows' Path environment variable. By contrast, double clicking on a *.Rdata file uses the version pointed to by the relevant file association entry in HKEY_CLASSES_ROOT.
But when or by what are the two HKEY_LOCAL_MACHINE entries used?
EDIT: Brian Diggs points to a bundle of Windows *.bat files that do use (and modify) these registry entries, but I'm still interested in whether anything closer to 'core' R uses these. (I'm hoping the answer is 'no'.)
** As described in the R for Windows FAQ, these entries may be set either during installation (by clicking 'Save version number in registry') or later from the command line (by typing RSetReg.exe from within $RHOME\bin).
Major edit:
It looks like those registry entries are primarily there for use by external applications.
Here's why I think that.
grep'ing HKEY_LOCAL_MACHINE in the R sources turns up four hits in just three files. The files -- "extra.c", "RSetReg.c", and "rhome.c" -- are all located in R-2.15.0/src/gnuwin/ or its subdirectories.
The relevant occurrence appears to be that in R-2.15.0/src/gnuwin/rhome.c, where it is used by the C function get_R_HOME. That function is designed to
/* get R_HOME from environment or registry: used in embedded apps */
and it searches in the registry only if R_HOME if it has not yet been found in the "C environment space" or the "Windows API environment space".
get_R_HOME, in turn, only appears in two other files, "R-2.15.0/src/gnuwin/embeddedR.c", and "R-2.15.0/src/gnuwin/front-ends/rtest.c". (According to its resident readme file, the role of "R-2.15.0/src/gnuwin/front-ends/" is to make it possible to "Link[...] the R DLL into other applications".)
R's *NIX origins and emphasis on portability make it seem unlikely that anything close to R's core functionality would depend on registry entries. (This item's obviously way more speculative.)
Unless I hear otherwise, that's enough to convince me that the registry entries' only purpose is to provide pointers for external applications, particularly those that use an embedded instance of R.
One set of programs which use these are the R batchfiles which states
These programs set the version of R (and version of R Tools and miktex) by (1) examining environment variables (R_HOME, R_MIKTEX, R_TOOLS) or (2) if not set they look in the registry.

read known file extensions / types from the registry

I want to present the user with a list of known file extensions for him to pick. I know that these are stored in the Registry under HKEY_CLASSES_ROOT usually like this:
.txt -> (default)="txtfile"
where txtfile then contains the information about associated programs etc.
Unfortunately that place in the registry also stores lots of other keys, like the file types (e.g. txtfile) and entries like
CAPICOM.Certificates (whatever that is)
How do I determine which of the entries are file extensions? Or is there a different way to get these extensions like an API function?
(I don't think it matters, but I am using Delphi for the program.)
There is no guarantee that every keys preceded by a dot in HKEY_CLASSES_ROOT is intended for file association, but every file association requires creation of a key preceded by a dot. See MSDN on File Types topic.
AFAIK, the method I describe here conforms with how the Windows Set File Associations feature works to get a list of all known file types. It was based on my former observation when I delved into this subject.
To achieve that, you'll need to do intricate steps as follows:
Enumerating every keys preceded by a dot . , you can use RegQueryInfoKey() and RegEnumKeyEx() for this purpose.
In every keys preceded by a dot, look at the default value data:
a. If the default value is not empty, this is enough indication that the "preceding dot key" is intended for file association in all Windows NT version, then try to open the key name as mentioned by the value data, just says TheKeyNameMentioned.
a1) If there is subkeys shell\open\command under TheKeyNameMentioned, then test the existence of the path pointed by the default value of this key; if the path exists, there is a default application associated with the extension; if the path doesn't exists, the default application is unknown. To get the file extension description, look at the default value of TheKeyNameMentioned. To get the program description, first, test whether the following key contain a value-name equal to the EXE file path, that is HKCR\Local Settings\Software\Microsoft\Windows\Shell\MuiCache. If it is there, then look at the value data to get the file description; if it is not there, use GetFileVersionInfo() directly to get the file description.
a2) If there is no subkeys shell\open\command under TheKeyNameMentioned, then the default application is unknown. To get the file extension description, look at the default value of TheKeyNameMentioned.
b. On Windows Vista and later, when the point [a] fails, you need additional check. If the default value is empty, test whether the key has a subkey named OpenWithProgIDs.
If OpenWithProgIDs subkey exists, use RegEnumValue() to find the first encountered value name that meets the criteria, that is, the name of the value name must point to an existing key (just says TheKeyNameMentioned.) with the same name as the value name. If TheKeyNameMentioned exists, this is enough indication that the "preceding dot key" is intended for file association. Read point a1 and a2 for the next steps.
If OpenWithProgIDs subkey doesn't exist, the default application is unknown. To get the file extension description, look at the default value of TheKeyNameMentioned.
Hope that helps. :-)
For a command-line alternative, the assoc command-line program included in Windows shows registered file extensions.
c:\> assoc
.3g2=VLC.3g2
.3gp=VLC.3gp
.3gp2=VLC.3gp2
.3gpp=VLC.3gpp
...
I'm not sure which verb this looks for. Open perhaps? I'm also not sure which extensions will appear in this list. Perhaps the extensions of files that can open from the command line.
To then find out which executable is mapped to each file type, the ftype command will tell:
c:\> ftype VLC.3g2
VLC.3g2="c:\vlc.exe" --started-from-file "%1"
IMHO - all those registry subkeys starting with the dot (.) - are for file extensions.
For instance in your case .txt stands for the "txt" extension, whereas txtfile doesn't start with the dot.
I don't know the details, but it seems you could use the IQueryAssociations interface.

Increase number of characters in filename field of GetOpenFileName file selection dialog

Our app allows multiple files to be selected in a file selection dialog which is shown via the GetOpenFileName function (this question also applies to folks using CFileDialog, etc...)
There appears to be a limit to the number of characters that can be typed into the filename field (259 seems to be the magic number - not sure why).
We have tried changing the following members of the OPENFILENAME structure:
lpstrFile - point to our own buffer, sized at 4K bytes
nMaxFile - set to the size of lpstrFile (we are compiling ANSI, so this is effectively 4000
But these values appear to not increase the input width of the filename field in the dialog.
I am going to experiment with sending a EM_SETLIMITTEXT message to the control, but wanted to know if anyone else has a solution.
EDIT - solved this myself: solution I can't accept out my own answer, but here it is for posterity. If anyone else has a better solution, please post it - or feel free to mod up my solution so future searchers will find it at the top.
Turns out that the edit control (At least in my development environment) is a combo box, so EM_SETLIMITTEXT isn't appropriate.
Instead, I tracked down the combo box using GetDlgCtrl on the parent of the file open dialog (I do this in the OnInitDialog handler), cast it to CComboBox*, then call LimitText() to set the limit.
This could also be done by sending a CB_LIMITTEXT message to the control for those of you who are not working with CFileDialog. The appropriate value here is most likely the OPENFIILENAME.nMaxFile value that is passed in.
From Naming a File or Directory on MSDN:
In the Windows API (with some exceptions discussed in the following paragraphs), the maximum length for a path is MAX_PATH, which is defined as 260 characters.
Even if you could coerce longer strings out of the dialog, you may run into trouble down the line when using APIs that have been coded against MAX_PATH.
The docs go on to say:
The Windows API has many functions
that also have Unicode versions to
permit an extended-length path for a
maximum total path length of 32,767
characters. This type of path is
composed of components separated by
backslashes, each up to the value
returned in the
lpMaximumComponentLength parameter of
the GetVolumeInformation function. To
specify an extended-length path, use
the "\\?\" prefix. For example,
"\\?\D:\<very long path>". (The
characters < > are used here for
visual clarity and cannot be part of a
valid path string.)
I believe this is a hard limit that cannot be bypassed. The only time it should matter is when you want to select more than one file, since the limit is enough for the maximum file name length.
I have added an "All Files" button to these dialogs for opening all of the files in a folder; that's the only workaround I have found.

Resources