Introduction
This is my first submission to CodeProject so please excuse for any amateur formatting or other editorial lapses. I wanted to do something with my new installation of Visual Studio 2005 and .NET 2.0 runtime, and I came up with this idea I call PruneRecentDocs. This application will allow you to selectively delete the entries as presented in the My Recent Documents view.
Background
The Windows OS basically allows for either single file deletion or wholesale removal of the previous file history. I wanted something that would allow for selective pruning of this fairly long list. If you have not deleted the history of recent file accesses on your computer then you will have hundreds of entries stored in the
HKCU\Software\Microsoft\Windows\CurrentVersion\Explorer\RecentDocs
registry entry. Somewhere, there is an optional registry setting that can control the depth of these entries, but unless set, I suspect this list will grow without bounds. But, I am not a registry expert so I welcome your corrections and feedback on this issue.
If you run
RegEdit.exe
and look at
HKCU\Software\Microsoft\Windows\CurrentVersion\Explorer\RecentDocs
, you will see something like this:
And if you go to Start -> My Recent Documents, you will see something like this:
From the My Recent Documents view, you can only right click the mouse and delete one file at time. The default number of files displayed in the popup list is 15. However, if you delete one or more of these displayed files and if you have more files stored in the RecentDocs registry, then upon the next popup display, you will be presented with the next 15 MRU files. Attempting to delete all these files via this mechanism is very tedious.
Using the code
The class diagram view as shown in Visual Studio 2005:
Some highlights of the code design and classes
I made use of .NET generics to greatly simplify the table lookup and correlation operations that would have otherwise required much more coding using simple
for
loops. The first class of interest is
RecentDocsFormat
. This class takes a registry key as the constructor parameter and then parses the contents into members of the class. The registry key values under
RecentDocs
are of type
REG_BINARY
. The key values consist of a set of values with numerical names such as "0", "1", "2", etc. followed by the key value
MRUListEx
which is easy to decode being an ordered list of
Uint32
entries representing the Most Recent Used files and terminated by the value 0xFFFF. I could not find a definitive declaration of the numerical named key values but the first portion of the binary data is a Unicode string of the filename.
The
RegTools
class has
static
functions that assist in the manipulation of binary registry key values used in the
RecentDocs
key and its subkeys. This is where the .NET 2.0 generic
Dictionary<T,T>
and
List<T,T>
were helpful.
The constructor for RecentDocsFormat
This constructor will decode the binary values of the given registry key and construct
Dictionary
cross mappings between the MRU numerical names and the user friendly file names contained within the key value:
public RecentDocsFormat(RegistryKey key)
FolderKey = key;
FileToIndexMapping = new Dictionary<String, String>();
IndexToFileMapping = new Dictionary<String, String>();
MruListEx = new List<string>();
sFileNames = new List<string>();
byte[] Bytes = (byte[])FolderKey.GetValue("MRUListEx");
for (int index = 0; index < Bytes.Length /
sizeof(UInt32); index++)
UInt32 val = RegTools.GetMruEntry(index, ref Bytes);
string sVal = val.ToString();
MruListEx.Add(sVal);
List<string> slist = RegTools.ExtractKeyValues(FolderKey);
foreach (string s in slist)
object obj = FolderKey.GetValue(s);
string filename =
RegTools.ExtractUnicodeStringFromBinary(obj);
FileToIndexMapping.Add(filename, s);
IndexToFileMapping.Add(s, filename);
sFileNames.Add(filename);
sIndexNames = RegTools.ExtractKeyValues(FolderKey);
How to delete a filename reference from RecentDocs
RecentDocsFormat
has several methods but the ultimate action taken in this application is to prune a filename reference from the various (more than one) registry keys. The RecentDocsFormat
method, DeleteThisFile(string filename)
, accomplishes this. Note the use of the Dictionary
key lookup such as String indexName = FileToIndexMapping[sFilename];
:
public void DeleteThisFile(String sFilename)
String indexName = FileToIndexMapping[sFilename];
FolderKey.DeleteValue(indexName);
MruListEx.Remove(indexName);
RegTools.RebuildMruList(FolderKey, MruListEx);
FileToIndexMapping.Remove(sFilename);
IndexToFileMapping.Remove(indexName);
sFileNames.Remove(sFilename);
sIndexNames.Remove(indexName);
catch (ArgumentException)
A non standard ERD
This Entity Relationship like diagram does not conform to any particular or popular methodology but it helped me write the code. Starting with the RecentDocs registry key, we construct the top level RecentDocsFormat
instance. Next, we extract the registry subkeys under RecentDocs and create a Dictionary<String, RecentDocsFormat>
with the filename extension as the dictionary key, returning an instance of RecentDocsFormat
(which encapsulates access to the registry key values). When it is time to delete a file, three targets are updated:
the top level RecentDocsFormat
,
the entry in the associated filename extension subkey, and
the Windows shortcut stored in the special folder accessed via MyRecentDocsFolder = Environment.GetFolderPath(Environment.SpecialFolder.Recent);
:
Wildcard matching
This application extends the simple single filename selection with the wildcard option:
There is nothing fancy about this code. If you select *.txt, for example, it will enumerate over the key values looking for a match on the file extension.
Context menus and PInvoke
You can also right click on an item and that will popup a context sensitive menu:
This menu was constructed using a MouseDown
event handler on the ListView
class and a Click
event handler on the TooStripMenuItem
class. The first menu item will launch the associated application for this file. It does this by calling the shell function ShellExecute
via PInvoke to open the shortcut link. The OS will do the rest, finding the file source and launching the application:
listView1.MouseDown += new MouseEventHandler(listView1_MouseDown);
LaunchToolStripMenuItem.Name = "LaunchToolStripMenuItem";
LaunchToolStripMenuItem.Size = new System.Drawing.Size(119, 22);
LaunchToolStripMenuItem.Text = "Launch";
LaunchToolStripMenuItem.ToolTipText =
"Launch associated application for this file";
LaunchToolStripMenuItem.Click +=
new System.EventHandler(this.LaunchStripMenuItem_Click);
void listView1_MouseDown(object sender, MouseEventArgs e)
if (e.Button == MouseButtons.Right)
listView1.ContextMenuStrip.Show(listView1,
new Point(e.X, e.Y));
ListItemSelected = listView1.HitTest(e.X, e.Y);
private void LaunchStripMenuItem_Click(object sender,
EventArgs e)
ListViewItem item = ListItemSelected.Item;
if (item != null)
ShellApi.ShellExecute(IntPtr.Zero, "open",
item.Text + ".lnk", "", "",
ShellApi.ShowCommands.SW_SHOWNOACTIVATE);
Shell call via PInvoke
This class will allow the .NET application to call the shell32.sll function ShellExecute
via PInvoke. I would like to express my appreciation for the site PINVOKE.NET for an excellent reference on how to use PInvoke:
class ShellApi
public enum ShowCommands : int
SW_HIDE = 0,
SW_SHOWNORMAL = 1,
SW_NORMAL = 1,
SW_SHOWMINIMIZED = 2,
SW_SHOWMAXIMIZED = 3,
SW_MAXIMIZE = 3,
SW_SHOWNOACTIVATE = 4,
SW_SHOW = 5,
SW_MINIMIZE = 6,
SW_SHOWMINNOACTIVE = 7,
SW_SHOWNA = 8,
SW_RESTORE = 9,
SW_SHOWDEFAULT = 10,
SW_FORCEMINIMIZE = 11,
SW_MAX = 11
[DllImport("shell32.dll")]
public static extern IntPtr ShellExecute(
IntPtr hwnd,
string lpOperation,
string lpFile,
string lpParameters,
string lpDirectory,
ShowCommands nShowCmd);
Overall experience using Visual Studio 2005 for the first time
I found the Visual Studio 2005 IDE with IntelliSense to be wonderfully productive. Since I am new to most of the .NET 2.0 classes, I basically took a guess on what classes I could use for a particular feature and started typing. As the IntelliSense presented the choices matching my guesses, I could examine the types and parameters of the methods and quickly determine what is available.
ClickOnce deployment, signing, and automatic downloading of new versions
I have not done much with these features, but Visual Studio 2005 makes a really good canned solution for allowing the application to check a URL for updates and for downloading the latest version. I was able to simply choose a few options in the Publish pane, and publish this to a web site. If you navigate to the publish URL, you can automatically install the application. During installation, it will check for the prerequisite software such as the .NET 2.0 runtime and the Windows Installer 3.1. After installation, whenever you run the application, it will check for updates and optionally download the new version. When you use the Control Panel to uninstall this application, it will even allow you to rollback to a previous version or simply remove all versions for you. I don't know if this would be used by professional developers for retail software, but overall it was impressive in its scope and simplicity.
History
2006-02-06: 1.0.0.0
Initial release.
2006-02-09 - 1.1.0.0.
Added context sensitive menus.
Added ability to launch the associated file application via PInvoke.
Fixed several exception handling errors.
During working hours is a real time firmware developer for custom ASIC's in embedded systems. Writes C/C++/C# code in his sleep and is moderately proficient in Java, Python, hardware description languages and other obscure domain specific languages. Deals with nano/microsecond data propagation delay paths, cache line hit/miss ratios, multicore asic design, etc.
However for fun likes to dabble in the new technologies of the day and deals at higher level software engineering aspects.
This is a very nice idea. Instead of selectively deleting, you can add a "delete all but checked" action wherein the checked MRU items list is saved in the program until unchecked by the user and all but the checked items are deleted. It can be a tiny utility from the tray.
Sign In·
View Thread
Your code is available for HK_C_U\software\microsoft\windows\currentversion\explorer\recentdocs
but does not work for
HK_C_U\software\microsoft\windows\currentversion\explorer\comdlg32\OpenSavePidlMRU\*
Why ?
Can you find pathname and filename from the second key?
thanks.
vb6pr <pr314159@bamboo.lu>
Sign In·View Thread
I will look into this issue and hopefully post either a reply or an update in the future.
Thank you for bringing this to my attention as I was unaware of this region of the registry. I guess since it does not seem to play a direct role in the My Recent Documents feed then I was not aware nor interested in this section.
Sign In·View Thread
Thanks for the compliment. You can find the full path by Right-Clicking the file and choose "Properties". A standard Windows dialog box will appear showing the file properties. If then choose the Shortcut tab, you will see the full path name in the Target window.
Sign In·View Thread
You can download (free) Visual Studio 2005 here:
http://msdn2.microsoft.com/en-us/express/aa700756.aspx[^]
Sign In·View Thread
C:\Documents and Settings\user\Recent
since I spent some time, but not a lot searching trying to make windows stop utilizing \Recent\ I couldn't find anything, so I just permed it deny all.
if I don't have a good reason to use it, then why waste a little HDD writing time allowing windows to utilize this dir, I say
Sign In·View Thread