Click here to Skip to main content
12,702,886 members (31,319 online)
Click here to Skip to main content
Add your own
alternative version


114 bookmarked

A File Checksum Shell Menu Extension DLL

, 23 May 2008 LGPL3
Rate this:
Please Sign up or sign in to vote.
Create a File Checksum Shell Menu Extension using ATL and Crypto++


A File Checksum is used to verify the integrity of a file. Checksums can detect both transmission errors and tampering. To detect transmission errors, we would use a CRC (cyclic redundancy check) [1]. To detect tampering, we generally choose a cryptographic Hash due to the desirable property of Collision Resistance [2].

This article presents two Shell Extension DLLs which can be used to create file checksums and verify file checksums. The article is based on Michael Dunn's Guide to Writing Shell Extensions, Part I [4], Tom Archer's Using the Clipboard, Part I: Transferring Simple Text [5], and Crypto++. This article will discuss the following topics:

Microsoft offers a command line file checksum tool published under Knowledge Base article Q841290, Availability and Description of the File Checksum Integrity Verifier Utility [6]. The Microsoft tool supports MD5 and SHA1.

Shell Extension DLL Usage

Should the reader desire to use the Extension DLLs, he or she should download and install as described below. Once the DLLs are registered, they are available as Context Menus when one right clicks a file (or multiple files) in Windows Explorer.

  • Download the DLL
  • Unpack to C:\Windows\System\
  • Register the DLL
    • regsvr32.exe C:\Windows\System\CtxCreateHash.Dll
    • regsvr32.exe C:\Windows\System\CtxVerifyHash.Dll

Windows Vista and User Account Control

To manually run regsvr32.exe under Vista with User Account Control enabled, navigate to the Command Prompt from the Start menu. Right click the Command Prompt and chose Run As Administrator as shown in Figure 1.

Run As Administrator
Figure 1: Run As Administrator

Neglecting to run regsvr32 with elevated privileges will usually result in the error, "DllRegisterServer failed with error code 0x80070005," while success is the familiar succeeded shown below.

DllRegisterServer Success
Figure 2: DllRegisterServer Success

Visual C++ Runtime

The latest versions of the compiled DLLs are built and linked with Visual Studio 2008. If required, download the Microsoft Visual C++ 2008 Redistributable Package. A classic example of requiring the package is regsvr32 failing to register the DLLs with the error, "This application has failed to start because the application configuration is incorrect. Reinstalling the application may fix this problem"

Checksum Creation

After selecting a files or files, if the user selects Create Checksum, the following hashes are created and placed on the clipboard:

  • MD5
  • RIPE MD-128, RIPE MD-160, RIPE MD-256, RIPE MD-320
  • SHA-1, SHA-224, SHA-256, SHA-384, SHA-512
  • Whirlpool

A Message Box is then displayed with the digest of the hashed file or files. A truncated version is displayed to keep the message box size manageable.

Truncated Message Box

Figure 3: Truncated Message Box

The full checksums are placed on the Windows Clipboard for pasting. Pasting into Notepad from the clipboard reveals the full text.

Clipboard Text

Figure 4: Clipboard Text

Checksum Verification

Verifying a file's checksum is equally trivial. Navigate to the web page or document where the checksum resides, highlight, and copy to the clipboard.

Copy to the Clipboard

Figure 5: Copy Checksums to the Clipboard

Navigate to the files, select, right click, and chose Verify Checksum.

Verify Checksums

Figure 6: Verify Checksums

Supported verification algorithms are:

  • CRC32
  • MD2, MD4, MD5
  • RIPE MD-128, RIPE MD-160, RIPE MD-256, RIPE MD-320
  • SHA-1, SHA-224, SHA-256, SHA-384, SHA-512
  • Whirlpool

A message box will be presented similar to below. The message box will group files in two categories: verified and unverified. A verified file will display the message "Verified Checksum" with a digest of the checksum. An unverified file will under the heading "Unverified Checksum"

Verify Checksum Message Box

Figure 7: Verify Checksum Message Box

When verifying, the DLL searches for matching hash values in order from strongest to weakest (SHA-512 to CRC32). The match algorithm terminates on a first match, so only the strongest hash is displayed.

Compiling and Integrating Crypto++

The samples provided use various Crypto++ Symmetric Ciphers. Crypto++ can be downloaded from Wei Dai's Crypto++ page. For compilation and integration issues, visit Integrating Crypto++ into the Microsoft Visual C++ Environment. This article is based upon basic assumptions presented in the previously mentioned article. For those who are interested in other C++ Cryptographic libraries, please see Peter Gutmann's Cryptlib or Victor Shoup's NTL.

Windows Clipboard APIs

Please see Tom Archer's Using the Clipboard, Part I: Transferring Simple Text for an in depth discussion on the subject. Sample 1 demonstrates enumerating the Clipboard's data formats. Sample 2 demonstrates reading text from the Clipboard. Finally, sample 3 demonstrates writing Unicode text to the Clipboard.

Writing Clipboard Text

Figure 8: Writing Clipboard Text

Creating a Shell Context Menu Extension DLL

Creating a shell context menu is based extensively on Michael Dunn's Guide to Writing Shell Extensions, Part I. Please refer to Michael's article for the discussion. Michael's techniques works well for inserting a single menu item, or multiple menu items for Windows 2000.


Unlike Michael's article, we will have to use a slightly different technique when we call InsertMenu on Windows XP, Server 2003, and Vista. This is due to Composition. Composition is a technique that Windows XP and above use to group context menu items together. In Figure 9 we see two examples of composition. Adobe is providing two DLLs which are grouped together, and the checksum DLLs are being grouped together.

Figure 9: Composition

For a discussion of Composition, see Raymond Chen's series on The Old New Thing. The two chapters of interest are Chapter 10: Composite Extensions - Groundwork and Chapter 11: Composite Extensions - Composition. Keep in mind that Chen's sample code is presented from the shell's perspective rather than our perspective. Due to the side effects of composition in Windows XP, Server 2003, and Vista, we must insert menu items as follows:

InsertMenu( hmenu, uMenuIndex, MF_STRING | MF_BYPOSITION,
  uidFirstCmd+1, _T("Create Checksums") );

// Return that we consumed two IDs rather than 1.
// This is because we skipped ID 0 (uidFirstCmd), and
// added ID 1 (uidFirstCmd+1)
// This means we must handle Command 1 in InvokeCommand(),
// rather than Command 0

If we added two menu items, our routine would be as follows:

InsertMenu( hmenu, uMenuIndex, MF_STRING | MF_BYPOSITION,
  uidFirstCmd+1, _T("Item 1") );
InsertMenu( hmenu, uMenuIndex, MF_STRING | MF_BYPOSITION,
  uidFirstCmd+2, _T("Item 2") );

// Return that we consumed three IDs rather than one or two.
// This is because we skipped ID 0 (uidFirstCmd), and
// added ID 1 (uidFirstCmd+1) and added ID 2 (uidFirstCmd+2)
// This means we must handle Command 1 and Command 2
// in InvokeCommand(), rather than Command 0

The shell's algorithm which determines what items should be composted (and how they are arranged) is not available. But it appears that it depends on:

  • Registry string name of the DLL
  • Number of leading characters which match in a string compare

It appears the algorithm resides somewhere in the Shell's Shell32!_imp__DSA_InsertItem function. The rule of thumb observed is if two or more characters match in the registry string, the items will be composted.

Crypto++ ChannelSwitch Class

Retrieving from the disk multiple times can become a bottleneck for performance. This is especially true if we are reading the same file continuously. To overcome the multiple disk read issue, one can use the Crypto++ ChannelSwitch class. The ChannelSwitch class allows a user to stream data to multiple HashFilter based objects after a single disk read. More correctly, one can push data to any BufferedTransformation derived object. This is trivial when performing string (in memory) based operations; however disk based operations tax response times. One would not want to read a 1 MB file 6 times to send it to 6 different consumer objects. Though one could author code to perform the buffering and delegation, the simplest solution is the ChannelSwitch.

ChannelSwitch Overview

Figure 10: ChannelSwitch Overview

ChannelSwitch offers AddDefaultRoute(BufferedTransformation& destination) function to add a destination. HashFilter derives from BufferedTransformation:

HashFilter Inheritance Diagram
Figure 11: HashFilter Inheritance

The abbreviated use of the ChannelSwitch object in the Create Checksum Shell Extension DLL is depicted below.

Create Checksum ChannelSwitch Usage

Figure 12: CreateChecksum DLL ChannelSwitch

An abridged use of the ChannelSwitch object in the Verify Checksum Shell Extension DLL is depicted below.

Verify Checksum ChannelSwitch Usage

Figure 13: VerifyChecksum DLL ChannelSwitch

The code to accomplish multiple hashing based on a ChannelSwitch is shown below. Two hashes are created: MD5 and SHA-1. Wei provides the sample code in test.cpp of the Crypto++ library.

#include "channels.h"   // ChannelSwitch
#include "filters.h"    // HashFilter
#include "hex.h"        // HexEncoder
#include "md5.h"        // MD5
#include "sha.h"        // SHA-1, SHA-256, SHA-512
int main( )
    MD5 hashMD5;
    SHA1 hashSHA1;

    HashFilter filterMD5(hashMD5);    
    HashFilter filterSHA1(hashSHA1);

    std::auto_ptr<ChannelSwitch> channel(new ChannelSwitch );


Earlier, fciv.exe (KB 841290) was mentioned which creates and verifies MD5 and SHA-1 checksums. By changing the StringSource to a FileSource, the reader could have a new and improved fciv.exe less the XML encoding.

The output of the Sample 4 (using MD5, SHA-1, RIPEMD-160, and SHA-256) is shown below.

Sample 4

Figure 14: Sample 4

Command Line Debugging

If debugging using Visual Studio is too difficult or inconvenient, cdb.exe can be used. It is the command line equivalent of WinDbg. CDB can be downloaded from Microsoft in the Debugging Tools for Windows package. Some of the more useful commands for this exercise are shown below.

Attaching to Explorer

To attach to Windows Explorer, issue cdb -pid < pid>, where pid is the process identification number of Explorer. Once attached, Explorer will be suspended since the debugger is awaiting input. Enter g to run.

Attach To Explorer
Figure 15: Attach To Explorer

When Explorer is running, enter CTRL-C to break back into the debugger. Once finished in the debugger, press g again. Finally, ALT-Tab does work with Explorer suspended. It will be useful in getting the debugger window forward when Explorer cannot repaint (and is hiding a view).

Frozen Explorer or Debugger

This section is reiterated from above since it is so important. If Explorer is frozen when attempting to work on the desktop, type g in the debugger. If the debugger is not the windows with focus, use ALT-Tab to bring the debugger forward. Finally, if you cannot enter commands into the debugger, enter CTRL-C to break.

Loaded Modules

Once attached to Explorer, we need to know if our DLL is loaded so we can determine the breakpoint location and set the breakpoint. We can issue a load (ld) specify a full path name, of right click a file to coax Explorer into loading it.


Issuing x ctx*!*hash* will display symbol locations the two DLLs. The final expression (*hash*) refines the results. This is too much information, since Crypto++ is full of symbols such as these.

To further refine the location search, issue x ctx*!*querycontextmenu. Now that the location is known, issue the breakpoint. Exercise Explorer. bl lists breakpoints. When the breakpoint fires, enter p once to step out of DebugBreak() and into the function. To step into a function, enter t.

Locating Functions of Interest
Figure 16: Locating Functions of Interest

Managing an Unstable Windows Explorer

This portion of the article is again based on Michael Dunn's Guide to Writing Shell Extensions, Part I. Should the reader find the treatment too superficial, please refer the Michael's article. Michael details topics such as preparing the Windows environment for debugging Windows Explorer to allowing the Context Menu DLL to execute in light of Group Policy.

Before debugging, kill the Explorer process. When prompted for a Debug Executable, specify C:\Windows\Explorer.exe. When debugging begins, Visual Studio will start an instance of Explorer.


Figure 17: explorer.exe

Save your work frequently. Most problems only required restarting Explorer, or an occasional soft reboot. To restart Explorer, open Task Manager and End the Explorer process.

End Explorer Process

Figure 18: End Explorer Process

Next, switch to the Task tab, and click New Task.

New explorer.exe Task

Figure 19: New explorer.exe Task

If the reader builds the DLL, but the DLL cannot be opened to be written during linking, verify no instances of Explorer.exe are running. If no instances are present, restart Explorer.

Explorer Instances

Figure 20: Explorer Instances

Character Set Considerations

The Context Menu DLLs are written using wide characters since the NT family is dominating the landscape. The Crypto++ library is narrow, and the Windows API can be either.

Checksum Dll Design Overview

Figure 21: Checksum DLL Design Overview

Data transfer with respect to Crypto++ is generally from Crypto++ to the DLL. To facilitate the transfer, the DLL calls StringWiden() on the narrow data from Crypto++.

The same is not true for the Windows API. If _UNICODE and UNICODE are defined, no conversion takes place. When using SBCS and MBCS, StringWiden() and StringNarrow() are employed when moving strings between Windows and the DLL.

The conversions will be handled by way of the standard c++ library's widen() and narrow(). Those using Visual C++ 7.0 and above have more flexibility in use of standard library conversion routines.

// Courtesy of Tom Widmer (VC++ MVP)
std::wstring StringWiden( const std::string& narrow ) { 
    std::wstring wide;
    wide.resize( narrow.length() );
    typedef std::ctype<wchar_t> CT;
    CT const& ct = std::_USE(std::locale(), CT);
    // Non Portable
    // Iterators should not be used as pointers (works in VC++ 6.0)
    // wideness( narrow.begin(), narrow.end(), wide.begin() );
    // Portable
    // ct.widen(&narrow[0], &narrow[0] + narrow.size(), &wide[0]);
    // Portable
    ct.widen(, + narrow.size(),;    
    return wide;

Create and Verify Routines

Sample 4 (Channel Switch) and Sample 5 (CtxTest) provides most of the code required for the DLL. This section will discuss the remaining items of interest. The create and verify routines share common code. This would include populating the list of files and the Fly By help.


Initialize() has been expanded as follows. Notes that files is a vector of wide string. Once all of the files have been extracted, the vector is sorted based on name.

STDMETHODIMP CCreateHash::Initialize( LPCITEMIDLIST pidlFolder,
                                      LPDATAOBJECT pDataObj, HKEY hProgID )
    pidlFolder;     // Suppress Warning C4100
    hProgID;        // Suppress Warning C4100
    HDROP     hDrop;
    // Sanity check - make sure there is at least one filename.
    UINT uNumFiles = DragQueryFile( hDrop,
                         static_cast<UINT>(-1), NULL, 0 );    
    if( 0 == uNumFiles )
        { return E_INVALIDARG; }
    HRESULT hr = S_OK;
    // The author has encountered situations where
    //   MAX_PATH was a bit too small...
    TCHAR file[ MAX_PATH * 2 + 1 ];
    // Loop through all the files that were selected.
    for(UINT i = 0; i < uNumFiles; i++)
        DragQueryFile( static_cast<HDROP>( stg.hGlobal ),
                       i, file, MAX_PATH * 2 );
        // If the file name is a directory, silently skip
        //   We should not encounter this...
        if (::GetFileAttributes( file ) & FILE_ATTRIBUTE_DIRECTORY)
            { continue; }
        // Add the file name to the end of the list.
        #ifdef UNICODE
          files.push_back( file );
          files.push_back( StringWiden( file ) );
    std::sort( files.begin(), files.end() );
    return hr;

Create InvokeCommand

   // If lpVerb really points to a std::string,
   //   ignore this function call and bail out.
   if ( 0 != HIWORD( pCmdInfo->lpVerb ) ) { return E_INVALIDARG; }

   unsigned int i = 0, j = 0;

   // Friendly Name: 'MD5'
   std::vector< std::wstring > hashnames;  
   // Hash: 47FD4214F5775826FB20FEC1091987A1 
   std::vector< std::wstring > hashvalues;

   std::wstring ClipboardText;
   std::wstring MsgBoxMessage;   

   MsgBoxMessage = L"The following was placed on the Windows Clipboard:\r\n\r\n";

   // We must look for Command 1 now
   switch( LOWORD( pCmdInfo->lpVerb ) )
      case 1:
         for( i = 0; i < files.size(); i++ )
            CalculateFileHashes( files[ i ], hashnames, hashvalues );
            ClipboardText += FileName( files[ i ] ) + L"\r\n";
            MsgBoxMessage += FileName( files[ i ] ) + L"\r\n";
            for( j = 0; j < hashvalues.size(); j++ )
               // Clipboard Text is easy...
               //   Just dump the information
               ClipboardText += hashnames[ j ] + L": " + hashvalues[ j ] + L"\r\n";


            } // End - Keep the Message Box size reasonable
         } // for( j = 0; j < hashnames.size(); j++ )
         // Pretty Print
         if( i + 1 < files.size() )
            ClipboardText += L"\r\n";
            if( files.size() <= 2 )
               MsgBoxMessage += L"\r\n"; 
         } // for( i = 0; i < files.size(); i++ )
         SetClipboardText( pCmdInfo->hwnd, ClipboardText );
         #ifdef _UNICODE
            MessageBox( pCmdInfo->hwnd, MsgBoxMessage.c_str(),
                        _T("File Checksum Results"), MB_ICONINFORMATION );
            MessageBox( pCmdInfo->hwnd, StringNarrow( MsgBoxMessage ).c_str(),
                        _T("File Checksum Results"), MB_ICONINFORMATION );
         return S_OK;
   return E_INVALIDARG;

Verify InvokeCommand

Verify Checksum InvokeCommand() uses the same basic code and logic as Create Checksum. The difference in this code is the introduction of two additional vectors: vector< wstring > verifiedfiles and vector< wstring > unverifiedfiles for book keeping.

    unsigned int i = 0, j = 0;
    bool found = false;
    wstring ClipboardText;
    wstring MsgBoxMessage;
    vector< wstring > hashnames;
    vector< wstring > hashvalues;
    vector< wstring > verifiedfiles;
    vector< wstring > verifiedhashes;
    vector< wstring > unverifiedfiles;

    // If lpVerb really points to a std::string,
    //   ignore this function call and bail out.
    if ( 0 != HIWORD( pCmdInfo->lpVerb ) ) { return E_INVALIDARG; }

    HWND hwnd = pCmdInfo->hwnd;

    // If there is no text on the Clipboard,
    //   inform the user and bail out.
    if( false == GetClipboardText( ClipboardText ) )
          MessageBox( hwnd, _T("There is not text on the Clipboard."),
                      _T("File Checksum Verifier"), MB_ICONWARNING );
        return S_OK;
    // We must respond to Command 1 now.
    switch( LOWORD( pCmdInfo->lpVerb ) )
        case 1:
            for( i = 0; i < files.size(); i++ )
                // Hash found on the Clipboard?
                found = false;
                CalculateFileHashes( files[ i ], hashnames, hashvalues );
                // Start at size(): this has the effect of 
                //   verifying strongest (SHA-256) to weakest (MD4)
                for( j = 0; j < hashvalues.size() ; j++ )
                    if( true == Find( hashvalues[ j ], ClipboardText ) )
                        found = true;
                        verifiedfiles.push_back( files[ i ] );
                        // Concatenate 'MD5: ' and
                        //   '82A80BE6F1E0E7766FAC3CA661089EE4', etc
                        verifiedhashes.push_back( hashnames[ j ] + L": " +
                                                  Snip( hashvalues[ j ] ) );
                        j = hashvalues.size();  // Break the loop
                } // for( j = 0; j < hashvalues.size(); j++ )
                if( false == found )
                    unverifiedfiles.push_back( files[ i ] );
            } // for( i = 0; i < files.size(); i++ )


            return S_OK;
    return E_INVALIDARG;

This version of InvokeCommand() maintains parallel arrays for simplicity. The arrays are created for each file in the files vector. The arrays can be visualized as follows.

Hash Name - Hash Value Parallel Array
Figure 22: Hash Name - Hash Value Parallel Array

The arrays are created with CreateFileHashes(). The function prototype is below. Both Create and Verify use the function. Create simply dumps it to the Clipboard, while Verify walks the hashvalues vector (from strongest to weakest), searching for the hash value on the Clipboard.

bool CalculateFileHashes( const std::wstring& filename,
                          std::vector< std::wstring >& hashnames,
                          std::vector< std::wstring >& hashvalues )

InvokeCommand() is now presented below. Most of the logic is dominated by maintaining two strings - one for the Message Box, and the other for the Clipboard. Recall that the extra code for the Message Box is required to keep the size of the Message Box manageable.


When choosing a hash, one should choose at least a 160 bit hash. Note that hash length does not necessarily equate to strength. For example, RIPEMD-128 is as cryptographically strong as RIPEMD-256; RIPEMD-160 is as cryptographically strong as RIPEMD-320. RIPEMD-256 and RIPEMD-320 simply generate more entropy for a given Message M. The reader should refer to Optional Extensions to 256 and 320 Hash Results: RIPEMD-256 and RIPEMD-320 for details.

Taking from the NIST Website:

There are five (5) FIPS-approved algorithms for generating a condensed representation of a message (message digest): SHA-1, SHA-224, SHA-256, SHA-384, and SHA-512
March 15, 2006: The SHA-2 family of hash functions (i.e., SHA-224, SHA-256, SHA-384 and SHA-512) may be used by Federal agencies for all applications using secure hash algorithms. Federal agencies should stop using SHA-1 for digital signatures, digital time stamping and other applications that require collision resistance as soon as practical, and must use the SHA-2 family of hash functions for these applications after 2010. After 2010, Federal agencies may use SHA-1 only for the following applications: hash-based message authentication codes (HMACs); key derivation functions (KDFs); and random number generators (RNGs). Regardless of use, NIST encourages application and protocol designers to use the SHA-2 family of hash functions for all new applications and protocols.

Finally, taking from the RIPE MD website:

A 128-bit hash result does not offer sufficient protection anymore. A brute force collision search attack on a 128-bit hash result requires 264 or about 2 x 1019 evaluations of the function. In 1994 Paul van Oorschot and Mike Wiener showed that this brute-force job can be done in less than a month with a $10 million investment ("Parallel Collision Search with Applications to Hash Functions and Discrete Logarithms," 2nd ACM Conference on Computer and Communications Security, ACM Press, 1994, pp. 210-218). This cost is expected to halve every 18 months.

Visual Sudio 2005 and Platform SDK

Should the reader encounter the following issue during compilation:

error C2787: 'IContextMenu' : no GUID has been associated with this object

The reader can add the following to stdafx.h:

#ifndef IContextMenu
  struct __declspec(uuid("000214e4-0000-0000-c000-000000000046")) IContextMenu;

Doug Harrison, Microsoft MVP, has additional fixes outlined in Missing IContextMenu.


File checksums are an often overlooked Cryptographic tool. With these DLLs, one can easily incorporate the Checksum functionality into his or her documents or website.



  • Wei Dai for Crypto++ and his invaluable help on the Crypto++ mailing list
  • Dr. Brooke Stephens who laid my Cryptographic foundations


  • 05.24.2008 Added Vista regsvr32.exe Error
  • 05.24.2008 Tested Upgrade to Visual C++ 9.0 (VS2008)
  • 03.07.2008 Added VS2008 regsvr32.exe Error
  • 12.01.2007 Added Windows XP and Above Composting Issue
  • 08.03.2007 Added note on IContextMenu and Visual Studio 2005
  • 08.03.2007 Upgraded from Visual C++ 7.1 to Visual C++ 8.0
  • 05.31.2007 Upgraded from Visual C++ 6.0 to Visual C++ 7.1
  • 05.31.2007 Verified Compatibility with Crypto++ 5.5.1
  • 05.31.2007 Removed HAVAL in CtxVerifyHash.dll
  • 05.31.2007 Added Additional SHA-2 Hashes in CtxVerifyHash.dll
  • 05.31.2007 Added Whirlpool (512) Hash in CtxVerifyHash.dll
  • 01.09.2007 Added Reference to NIST Hash Choices
  • 12.19.2006 Added Section Crypto++ String and File Hashing
  • 12.17.2006 Added CRC32 to Verify Shell Extension DLL
  • 12.16.2006 Update Article Graphics
  • 12.15.2006 Updated ChannelSwitch
  • 12.14.2006 Initial Release


    • MD5: D967CF24BEC8BF403B0F274B7908876E
    • RIPEMD-128: CF073CB397C912EE3395C63F5CCF93FE
    • RIPEMD-160: 324123B69596608381147F0F60C343B3F5C4B007
    • RIPEMD-256: 5A5A18771EAAF7A2E60C83D2FC8EC507389532A1E7F80FD2C38D5E949899A07F
    • RIPEMD-320: C8A01A5A8BA0323CEB25DA4FC45E155E47ACC5BFF7481AECF0C1E4250E5CF07DF7885A9CFD3D8E23
    • SHA-1: 53CFD2F493846CC49EC5209CA7E4C9939D371172
    • SHA-224: 70A8F1066BEAE431E4462E226A4293DB323A096EDE5163A47B83E64C
    • SHA-256: 75DA22C742752DBC73AB421F8A48BCCEC4BDF5486DFF16EB9395C6F499DF9221
    • RIPEMD-128: ABEF419E395A7A21CE9D65A5419842D4
    • RIPEMD-160: 22EF853EAE308F7B07CA674CC50BE6D9929F3649
    • RIPEMD-256:
    • 19CC4D1BFD0B92B9E7D48CB7CE14C0C3F094E8E15EC8EB61C89632C5032D474C
    • RIPEMD-320: D06012A5A9EA47436AFD247B90D179081B64C62CEF3BA20DF35DB85021FFBB386BA7962A289BB224
    • SHA-1: B54562A1EC047F31C75027C396CE44DFE6F2020D
    • SHA-224: CB413BAD801E4C9F8043BAF294FA29F0616F449B42E453B9EE1E5941
    • SHA-256: D10E2C892487CC6A17D5C673DE52974F7F0DB701342DD46B1821FD479DBCEC4F
    • RIPEMD-256: 82BC7E086B8EDA7C1A8C5310F82F31DDEA13BF192482A6D1F362831E8FC2F23A
    • SHA-256: 1602BD0FF62E05F4B77428C5943FE37CB7CCE6367C5D79CCC004E125A2C6266A
    • CRC32: BD50E874
    • MD5: 0CAE59412D3272C12A4985930F313DC1
    • SHA-1: 68274EDD7A70792057A379048F1FBA6E58048F7E
    • RIPEMD-160: 7434F4A3498A642A53FCC3AF6081718BE18FCE28
    • SHA-256: 3DA6D8C6D29DE726B96838273C5E9D7C558F725BF6574345C054E7FD1325DF42


This article, along with any associated source code and files, is licensed under The GNU Lesser General Public License (LGPLv3)


About the Author

Jeffrey Walton
Systems / Hardware Administrator
United States United States
No Biography provided

You may also be interested in...

Comments and Discussions

GeneralMy vote of 5 Pin
gndnet28-Dec-10 0:33
membergndnet28-Dec-10 0:33 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    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
Web02 | 2.8.170118.1 | Last Updated 24 May 2008
Article Copyright 2006 by Jeffrey Walton
Everything else Copyright © CodeProject, 1999-2017
Layout: fixed | fluid