Click here to Skip to main content
11,578,739 members (67,160 online)
Click here to Skip to main content
Articles » Languages » C# » Applications » Revisions

Tagged as

ScanX - a Registry Cleaner

, 19 Dec 2011 CPOL 79.2K 8.8K 196
Rate this:
Please Sign up or sign in to vote.
C#/WPF -ScanX: Creating a commercial quality registry cleaner
This is an old version of the currently published article.
Download scanx_source.zip - 2.05 MB

main.jpg

..and then he deleted something

..and his computer froze and never booted back up again. I love those stories about the registry; the critical box gets hosed because Billy decides to start editing the registry on the fly, network scripts that take down entire departments ..ignorance is bliss I suppose -but not always..

The registry though, is just a kind of heirarchal database, storing varied information on the operating system and installed components. In the beginning it was intended only to store configuration information for COM components, but later became a vehicle for peripheral application storage in an attempt to reduce reliance on .ini files, and now.. it seems we are moving in yet another direction, back towards modular design principles, with application data stored within their relative assemblies. That not withstanding, the registry will continue to be a core element in the windows operating system for some time to come.

So what does a registry cleaner do, and do I really need one? Primarily, what it does is path testing, wether it be paths in the file system, or links between elements within the registry itself. I realized this a few years ago, when it so happened that I needed a registry cleaner.. A friend had a particularly nasty virus on her computer, one that disabled the anti-virus, and set itself up with system level access. I managed to remove it, but after deleting its components, the networking no longer worked. It had inserted itself into the networking stack, and without knowing what or where these entries were, (in the registry), I needed a registry cleaner to fix it. The one I used cleared it right up, (though without purchasing it, I had to manually delete the entries.. which in turn led to me writing the first iteration of this application). It was at that point that I began examining the entries and trying to figure out how these applications worked, and understanding the complex relationship between various registry entries.

I'm not going to bore you with some long essay on how the registry works, there are plenty of tutorials on the internet, wikipedia has a good one. What I will do, is give you a mile high view of how this application works, if you want to understand it better, or expand on this application, some serious debugging and reading of the class notes will be required..

Accessing the Registry using API

At the heart of this application is the registry class cLightning, something I wrote in VB6 many years ago, and translated to C# as one of my first projects in this language. Most of the data types and registry api in advapi.dll are accessable with this class. There are also a number of useful methods like RunAs, ShellOpen, Rot13, and access escalation routines, (that won't work in Vista/Win7).

Enumerating through hundreds of thousands of registry keys requires a 'need for speed' approach, ergo the inbuilt registry methods are simply out of the question. There are individual methods for each data type:

 /// <summary>
/// Read Resource Descriptor data type
/// </summary>
/// <param name="RootKey">enum: root key</param>
/// <param name="SubKey">string: named subkey</param>
/// <param name="Value">string: named value</param>
/// <returns>string: value / empty on fail</returns>
public string ReadResourceDescriptor(ROOT_KEY RootKey, string SubKey, string Value)
{
    UIntPtr hKey = UIntPtr.Zero;
    uint pvSize = 1024;
    uint pdwType = (uint)VALUE_TYPE.REG_FULL_RESOURCE_DESCRIPTOR;
    byte[] bBuffer = new byte[1024];
    string sRet = String.Empty;

    try
    {
        if (RegOpenKeyEx(RootKey, SubKey, 0, KEY_READ, ref hKey) == ERROR_NONE)
        {
            if (RegQueryValueEx(hKey, Value, 0, ref pdwType, ref bBuffer[0], ref pvSize) == ERROR_NONE)
                for (int i = 0; i < (pvSize); i++)
                {
                    sRet += bBuffer[i].ToString();
                }
        }
        return sRet;
    }
    finally
    {
        if (hKey != UIntPtr.Zero)
            RegCloseKey(hKey);
    }
}

/// <summary>
/// Write a Resource Descriptor value
/// </summary>
/// <param name="RootKey">enum: root key</param>
/// <param name="SubKey">string: named subkey</param>
/// <param name="Value">string: named value</param>
/// <param name="Data">byte array: data</param>
/// <returns>bool</returns>
public bool WriteResourceDescriptor(ROOT_KEY RootKey, string SubKey, string Value, byte[] Data)
{
    UIntPtr hKey = UIntPtr.Zero;
    uint pdwType = (uint)VALUE_TYPE.REG_FULL_RESOURCE_DESCRIPTOR;

    try
    {
        if (RegOpenKeyEx(RootKey, SubKey, 0, KEY_WRITE, ref hKey) == ERROR_NONE)
        {
            if (RegSetValueEx(hKey, Value, 0, pdwType, ref Data[0], (Data.GetUpperBound(0) + 1)) == ERROR_NONE)
                return true;
        }
        return false;
    }
    finally
    {
        if (hKey != UIntPtr.Zero)
            RegCloseKey(hKey);
    }
}

..and methods for collecting keys and values into an array:

/// <summary>
/// Enumerate and collect keys
/// </summary>
/// <param name="RootKey">enum: root key</param>
/// <param name="SubKey">string: named subkey</param>
/// <returns>ArrayList</returns>
public ArrayList EnumKeys(ROOT_KEY RootKey, string SubKey)
{
    uint keyLen = 255;
    uint index = 0;
    int ret = 0;
    long lastWrite = 0;
    UIntPtr hKey = UIntPtr.Zero;
    StringBuilder keyName = new StringBuilder(255);
    ArrayList keyList = new ArrayList();

    try
    {
        if (RegOpenKeyEx(RootKey, SubKey, 0, KEY_ENUMERATE_SUB_KEYS, ref hKey) != ERROR_NONE)
            return keyList;
        do
        {
            keyLen = 255;
            ret = RegEnumKeyEx(hKey, index, keyName, ref keyLen, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, out lastWrite);
            if (ret == ERROR_NONE)
            {
                keyList.Add(keyName.ToString());
            }
            index += 1;
        }
        while (ret == 0);
        return keyList;
    }
    finally
    {
        if (hKey != UIntPtr.Zero)
            RegCloseKey(hKey);
    }
}

There are also routines that delete keys and values, test existence, test for empty values etc.

Beating a Path..

By far the most challenging portion of this project was file and directory path testing. This is because there are literally half a dozen different ways to list the same path. Unicode, psuedo paths, truncated, prefixed, postfixed.. an absolute nightmare. As you can imagine, it took some tweaking to eliminate false positives, first by testing the path with a reg expression, then if the file could not be found going through a number path conversions and extraction methods:

private static Regex _regPath = new Regex(
    @"([?a-z A-Z]:.*\\)([?\w.]+)", RegexOptions.IgnoreCase |
        RegexOptions.CultureInvariant |
        RegexOptions.IgnorePatternWhitespace |
        RegexOptions.Compiled
    );

private string CleanPath(string Path)
{
    Match mc = _regPath.Match(Path);

    // test fast way first
    if (mc.Success && FileExists(mc.Groups[0].Value))
    {
        return mc.Groups[0].Value;
    }
    else
    {
        // extract path upon failure of regexp
        return ExtractPath(Path);
    }
}


private string ExtractPath(string Path)
{
    string sp = Path.ToUpper();

    // test path first
    if (!FileExists(sp) && IsFileCandidate(sp))
    {
        // trim to drive root
        if (sp.Substring(1, 1) != CHR_COLAN)
        {
            sp = sp.Substring(sp.IndexOf(CHR_COLAN) - 1);
        }
        // truncate leading path
        if (sp.Substring(3).Contains(STR_PATH))
        {
            sp = sp.Substring(sp.IndexOf(STR_PATH, 3) - 1);//PathFilter(path);
        }
        // find and trim to extension
        foreach (string s in _aExtensions)
        {
            if (sp.Contains(s))
            {
                sp = sp.Substring(0, sp.IndexOf(s) + s.Length);
                break;
            }
        }
        // get the long path
        if (sp.Contains(CHR_TILDE))
        {
            sp = GetLongName(sp);
        }
    }
    return sp;
}

There's actually a number of other methods not shown, used as safety valves to ensure the correct path is tested, something you should step through if interested..

Scan Steps

There are 12 phases, and a number of subscans in the scanning process, as taken from the class notes in cRegScan:

Custom Controls

Purpose: Test for valid software class registration and command paths (CLSID)
Scan 1 - CLSID
1) valid registration - HKEY_CLASSES_ROOT\CLSID\..\InProcSvr32(+)
2) typelib paths - HKEY_CLASSES_ROOT\..
3) appid paths - HKEY_CLASSES_ROOT\CLSID\... Val-AppID <-> HKEY_CLASSES_ROOT\AppID
Scan 2 - Interface
4) type lib paths - HKEY_CLASSES_ROOT\Interface\TypeLib <-> CLSID\TypeLib
5) interface paths - HKEY_CLASSES_ROOT\Interface\...\ProxyStubClsid32 <-> CLSID
Scan 3 - TypeLib
6) empty keys - HKEY_CLASSES_ROOT\TypeLib\..\HELPDIR
7) typelib paths - HKEY_CLASSES_ROOT\TypeLib\..\..\win32
Scan 4 - File Extensions
8) empty ext keys - HKEY_CLASSES_ROOT\...

User Software

Purpose: Test for valid software paths
Location: HKEY_CURRENT_USER\Software
Method: Collect all software keys from \Software branch, and scan for valid path entries

System Software

Purpose: Scan 1-5 -System Software Test for valid id registrations
1) test class names - HKEY_CLASSES_ROOT\.xxx DefVal <-> HKEY_CLASSES_ROOT\..
2) test clsid - HKEY_CLASSES_ROOT\..\CLSID DefVal <-> HKEY_CLASSES_ROOT\CLSID
3) object menu path - HKEY_CLASSES_ROOT\..\shell\edit\command
4) object open path - HKEY_CLASSES_ROOT\..\shell\open\command
5) object icon path - HKEY_CLASSES_ROOT\..\DefaultIcon

Fonts

Location: HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Fonts
References: From HKLM -> fonts folder
Method: Path testing for valid occurence.

Help Files

Location: HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\Help
Reference: From HKLM -> Help registration
Method: Path testing for valid occurence.

Shared DLLs

Location: HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\SharedDLLs
Reference: From HKLM -> path test
Method: Path testing for valid occurence.

Startup Entries

Location: 1) HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Run
Location: 2) HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\RunOnce
Location: 3) HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\RunOnceEx
References: From HKLM -> path test
Method: Path testing for valid occurence.

Installed Software

Location: 1) HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall
References: From HKLM -> path test
Method: Path testing for valid occurence of uninstall strings.

Virtual Devices

Location: 1) HKEY_LOCAL_MACHINE\SYSTEM\ControlSet\Control\VirtualDeviceDrivers
References: From HKLM -> fix for 16bit VDM value type mismatch
Method: Value type testing for VDM bug.

History and Start Menu

References: From HKLM -> scan for valid link paths
Method: Value type testing for valid entry
Location: HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Explorer\MenuOrder\Start Menu\Programs
Location: HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Explorer\FileExts\...\OpenWithList | OpenWithProgids
Location: HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Explorer\ComDlg32\OpenSaveMRU
Location: HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Explorer\UserAssist\...\Count

Deep Scan

References: From HKLM -> scan for valid link paths
Method: Value type testing for valid entry
Location: HKEY_LOCAL_MACHINE\SOFTWARE
Location: HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Installer\UserData\...\Products

MRU Lists

References: From HKCU -> scan for valid link paths
Method: Value type searchLocations: HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Explorer\RecentDocs
Locations: HKEY_CURRENT_USER\Software\Microsoft\Internet Explorer\TypedURLs
Locations: HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Explorer\RunMRU
Locations: HKEY_CURRENT_USER\Software\Microsoft\Search Assistant\ACMru\5603
Locations: HKEY_CURRENT_USER\Software\Microsoft\Search Assistant\ACMru\5001
Locations:HKEY_CURRENT_USER\Software\Microsoft\Search Assistant\ACMru\5647
Locations: HKEY_CURRENT_USER\"Software\Microsoft\Windows\CurrentVersion\Applets\Wordpad\Recent File List"
Locations: HKEY_CURRENT_USER\"Software\Microsoft\Windows\CurrentVersion\Applets\Regedit\Favorites"
Locations: HKEY_CURRENT_USER\"Software\Microsoft\Windows\CurrentVersion\Applets\Regedit"
Locations: HKEY_CURRENT_USER\"Software\Microsoft\Windows\CurrentVersion\Applets\Paint\Recent File List"
Locations: HKEY_CURRENT_USER\"Software\Microsoft\Windows\CurrentVersion\Explorer\ComDlg32\LastVisitedMRU"
Locations: HKEY_CURRENT_USER\"Software\Microsoft\Windows\CurrentVersion\Explorer\Wallpaper\MRU"
Locations: HKEY_CURRENT_USER\"Software\Microsoft\MediaPlayer\Player\RecentFileList"
Locations: HKEY_CURRENT_USER\"Software\Microsoft\MediaPlayer\Player\RecentURLList"

Yes a little cryptic I know.. but basically all routines, (except MRU, which is kind of a hueristic scan for MRU lists), are doing path testing in various areas of the registry. The control scan is the most complicated, it tests for paths to shell/edit/default/icon paths and ids under a CLSID subkey, it also tests for valid type, proxy and interface registration UIDs associated with that class. The best way to get a handle on what this all does, is just spending time both stepping through the methods, and examining the paths in regedit to try and make the logical connections..

UAC and Virtualization

In yet another patch meant to address 30 years of security complacency, we now have file and registry Virtualization. Essentially it just redirects write requests to more secure areas of the registry or file system. This shouldn't effect the application scan though, because we are running under the administrator credentials. This is just one of those apps that has to run as administrator, because many areas of the registry are now write protected under Vista/W7. This is accomplished by changing the app.manifest requestedExecutionLevel to:

<requestedExecutionLevel level="requireAdministrator" uiAccess="false" />

The application will also need to be published with an
external compiler, as the VS publisher will throw an error with this setting, (apparently by design).

Visuals

progress.jpg

The circular progress bar was adapted from a Silverlight example by Colin Eberhardt and made into a nifty little user control.

items.jpg

The progressbar in the listitems was fashioned by overlapping image brushes:

 <ProgressBar Maximum="10" Minimum="1" Value="{Binding Scope}" Width="72" Height="14" Margin="2">
    <ProgressBar.Template>
        <ControlTemplate>
            <Grid>
                <Rectangle Name="PART_Track" HorizontalAlignment="Left" VerticalAlignment="Top" Margin="1" Height="14" Width="66">
                    <Rectangle.Fill>
                        <ImageBrush ImageSource="..\Images\starsbg.png" Stretch="None" AlignmentX="Left" AlignmentY="Top" />
                    </Rectangle.Fill>
                </Rectangle>
                <Rectangle Name="PART_Indicator" HorizontalAlignment="Left" VerticalAlignment="Top"  Margin="1" Height="14">
                    <Rectangle.Fill>
                        <ImageBrush ImageSource="..\Images\stars.png" Stretch="None" AlignmentX="Left" AlignmentY="Top" />
                    </Rectangle.Fill>
                </Rectangle>
            </Grid>
        </ControlTemplate>
    </ProgressBar.Template>
</ProgressBar>

The rest is just style and brushes..

Final Thoughts..

Though I have tested this on several different machines, (Vista/W7), with no problems or surprises, remember, it is beta.. though I think even in a worst case scenario, you might have to reinstall an application, (doubtful).. just don't let Billy play with your Regedit ;0)

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

John Underhill
Network Administrator vtdev.com
Canada Canada
Network and programming specialist. Started in C, and have learned about 14 languages since then. Cisco programmer, and lately writing a lot of C# and WPF code, (learning Java too). If I can dream it up, I can probably put it to code. My software company, (VTDev), is on the verge of releasing a couple of very cool things.. keep you posted.

You may also be interested in...

Comments and Discussions


Discussions posted for the Published version of this article. Posting a message here will take you to the publicly available article in order to continue your conversation in public.
 
QuestionNice UI Pin
Member 1131567322-Jun-15 11:58
memberMember 1131567322-Jun-15 11:58 
AnswerRe: Nice UI Pin
John Underhill23-Jun-15 7:05
memberJohn Underhill23-Jun-15 7:05 
QuestionACTIVATION PAGE Pin
Member 1161884018-Apr-15 16:06
memberMember 1161884018-Apr-15 16:06 
AnswerRe: ACTIVATION PAGE Pin
John Underhill20-Apr-15 4:55
memberJohn Underhill20-Apr-15 4:55 
QuestionFor WinForm? Pin
Member 877285017-Jun-14 22:42
memberMember 877285017-Jun-14 22:42 
GeneralMy vote of 5 Pin
Аslam Iqbal23-Feb-14 18:30
memberАslam Iqbal23-Feb-14 18:30 
QuestionHelp required Pin
Faiz 9920-Feb-14 15:49
memberFaiz 9920-Feb-14 15:49 
QuestionHeap Corruption when building and executing on x64 machines Pin
davidovicnikola27-Dec-13 15:39
memberdavidovicnikola27-Dec-13 15:39 
AnswerRe: Heap Corruption when building and executing on x64 machines Pin
Steppenwolfe27-Dec-13 16:51
memberSteppenwolfe27-Dec-13 16:51 
GeneralRe: Heap Corruption when building and executing on x64 machines Pin
davidovicnikola10-Jan-14 3:14
memberdavidovicnikola10-Jan-14 3:14 
Questionhii Pin
Member 103165574-Oct-13 20:10
memberMember 103165574-Oct-13 20:10 
QuestionIs it possible that.. Pin
Member 1027809229-Sep-13 12:03
memberMember 1027809229-Sep-13 12:03 
GeneralMy vote of 5 Pin
m_harriss22-Mar-13 10:09
memberm_harriss22-Mar-13 10:09 
Questioncan't download zip file Pin
m_harriss17-Mar-13 9:23
memberm_harriss17-Mar-13 9:23 
AnswerRe: can't download zip file Pin
Steppenwolfe17-Mar-13 9:50
memberSteppenwolfe17-Mar-13 9:50 
GeneralRe: can't download zip file Pin
m_harriss19-Mar-13 21:00
memberm_harriss19-Mar-13 21:00 
GeneralRe: can't download zip file Pin
m_harriss20-Mar-13 22:55
memberm_harriss20-Mar-13 22:55 
QuestionUsing Code in Commercialware Pin
Member 821674014-Dec-12 2:07
memberMember 821674014-Dec-12 2:07 
AnswerRe: Using Code in Commercialware Pin
Steppenwolfe14-Dec-12 8:00
memberSteppenwolfe14-Dec-12 8:00 
GeneralRe: Using Code in Commercialware Pin
Member 821674014-Dec-12 15:46
memberMember 821674014-Dec-12 15:46 
QuestionLove Your Project!! Please Help Pin
Dale 20124-Jul-12 22:52
memberDale 20124-Jul-12 22:52 
QuestionBuild Error Pin
mamirok1-Jul-12 5:53
membermamirok1-Jul-12 5:53 
AnswerRe: Build Error Pin
Steppenwolfe3-Jul-12 11:05
memberSteppenwolfe3-Jul-12 11:05 
QuestionExcellent. Thanks! Pin
kartalyildirim3-Mar-12 23:24
memberkartalyildirim3-Mar-12 23:24 
GeneralMy vote of 5 Pin
Akram El Assas30-Jan-12 21:07
memberAkram El Assas30-Jan-12 21:07 
GeneralRe: My vote of 5 Pin
Steppenwolfe31-Jan-12 3:51
memberSteppenwolfe31-Jan-12 3:51 
Questionwell thought Pin
niger5730-Jan-12 10:26
memberniger5730-Jan-12 10:26 
AnswerRe: well thought Pin
Steppenwolfe30-Jan-12 10:48
memberSteppenwolfe30-Jan-12 10:48 
GeneralMy vote of 5 Pin
tigercont29-Jan-12 22:04
membertigercont29-Jan-12 22:04 
GeneralRe: My vote of 5 Pin
Steppenwolfe30-Jan-12 4:48
memberSteppenwolfe30-Jan-12 4:48 
QuestionUsing your source code in my project Pin
ub3rst4r29-Jan-12 20:40
memberub3rst4r29-Jan-12 20:40 
AnswerRe: Using your source code in my project Pin
Steppenwolfe30-Jan-12 4:47
memberSteppenwolfe30-Jan-12 4:47 
GeneralMy vote of 5 Pin
Wimmo29-Jan-12 12:15
memberWimmo29-Jan-12 12:15 
GeneralRe: My vote of 5 Pin
Steppenwolfe29-Jan-12 13:50
memberSteppenwolfe29-Jan-12 13:50 
GeneralMy vote of 5 Pin
theanil28-Jan-12 8:55
membertheanil28-Jan-12 8:55 
GeneralRe: My vote of 5 Pin
Steppenwolfe29-Jan-12 6:44
memberSteppenwolfe29-Jan-12 6:44 
QuestionThanks all.. Pin
Steppenwolfe25-Jan-12 11:34
memberSteppenwolfe25-Jan-12 11:34 
GeneralMy vote of 5 Pin
Abinash Bishoyi15-Jan-12 13:03
memberAbinash Bishoyi15-Jan-12 13:03 
GeneralRe: My vote of 5 Pin
Steppenwolfe16-Jan-12 3:44
memberSteppenwolfe16-Jan-12 3:44 
Questioncould be your 1st win in monthly comp, I hope you win Pin
Sacha Barber13-Jan-12 23:43
mvpSacha Barber13-Jan-12 23:43 
AnswerRe: could be your 1st win in monthly comp, I hope you win Pin
Steppenwolfe14-Jan-12 11:33
memberSteppenwolfe14-Jan-12 11:33 
GeneralRe: could be your 1st win in monthly comp, I hope you win Pin
Sacha Barber14-Jan-12 20:55
mvpSacha Barber14-Jan-12 20:55 
GeneralMy vote of 5 Pin
Mihai MOGA13-Jan-12 7:03
memberMihai MOGA13-Jan-12 7:03 
GeneralRe: My vote of 5 Pin
Steppenwolfe13-Jan-12 13:01
memberSteppenwolfe13-Jan-12 13:01 
QuestionMy Vote of 5 Pin
Gandalf - The White12-Jan-12 20:37
memberGandalf - The White12-Jan-12 20:37 
AnswerRe: My Vote of 5 Pin
Steppenwolfe13-Jan-12 13:00
memberSteppenwolfe13-Jan-12 13:00 
QuestionMy vote of 5 Pin
Jaganathan Bantheswaran11-Jan-12 2:00
memberJaganathan Bantheswaran11-Jan-12 2:00 
AnswerRe: My vote of 5 Pin
Steppenwolfe11-Jan-12 12:09
memberSteppenwolfe11-Jan-12 12:09 
GeneralMy vote of 5 Pin
CS20119-Jan-12 23:52
memberCS20119-Jan-12 23:52 
GeneralRe: My vote of 5 Pin
Steppenwolfe10-Jan-12 9:25
memberSteppenwolfe10-Jan-12 9:25 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Terms of Use | Mobile
Web03 | 2.8.150603.1 | Last Updated 19 Dec 2011
Article Copyright 2011 by John Underhill
Everything else Copyright © CodeProject, 1999-2015
Layout: fixed | fluid