Visual Basic Browse/ PIDL / CSIDL Routines
SHGetFolderPath: Retrieve Windows Shell Folders (Best Practice)
     
Posted:   Saturday May 22, 1999
Updated:   Monday December 26, 2011
     
Applies to:   VB4-32, VB5, VB6
Developed with:   VB6, Windows NT4
OS restrictions:   None
Author:   VBnet - Randy Birch
     
Related:   BROWSE:
SHBrowseForFolder: Browse Folders Dialog Overview
SHBrowseForFolder: Browse for Folders Dialog
SHBrowseForFolder: Browse for Folders Callback Overview
SHBrowseForFolder: Browse for Folders New UI Features
SHBrowseForFolder: Pre-selecting Folders using a Browse Callback

CSIDL / Folders:
SHGetFolderPath: Overview of Shell and ComCtrl Versions, CSIDL Values
SHGetFolderPath: Retrieve Windows Shell Folders (Best Practice)

SHGetSpecialFolderLocation: Retrieve Windows Shell Folder
SHGetKnownFolderPath: Retrieve Windows Known (Shell) Folders under Vista
Pure VB: Using the Shell Application Object to Retrieve Windows Shell Folders

   
 Prerequisites
Windows XP, Windows 2000 or Windows NT4 and Win9x with appropriate DLL upgrades provided as part of service packs or IE releases.

This page may appear wider than normal on low-resolution systems to accommodate the CSIDL descriptions. It will appear fine pasted into a VB project.

SHGetFolderPath is a superset of SHGetSpecialFolderPath included with earlier versions of the Windows shell. SHGetFolderPath is implemented in a redistributable DLL, SHFolder.dll, that also simulates many of the new shell folders on older platforms such as Windows 95, Windows 98, and Windows NT 4.0. In degrading, this DLL always calls the current platform's version of this function to assure success, and if that fails, it will try to simulate the appropriate behaviour.

Microsoft recommends the use of SHGetFolderPath as a Best Practice (over other similar available APIs and manual methods (such and the environment variables) for the following reasons:

  • SHGetFolderPath promotes storage to administrator-chosen locations
  • SHGetFolderPath reduces user frustration by finding and saving their files without the need to request that the user locate a system folder
  • SHGetFolderPath reduces application errors associated with reading and writing files to system areas
  • SHGetFolderPath allows applications to run properly in secure environments

Description
When you code to save to a user's documents folder, can you be sure that the folder's path is "c:\My Documents"?

Popular shell folders such as "My Documents" are virtual folders that may or may not point to a storage location with the same name. Not only can users easily change the name of the My Documents folder, but through the registry, with tools such as TweakUI, via network policies, and even through options in later versions of Windows itself, users or network admins can specify an alternate path to the physical folder. Plus, while "My Documents" is the English name, what's it called on a Hebrew or French system? Can you accommodate all the possible names for all the system shell folders?

In the managed environment document storage usually lives on network shares. Yet those shares may change at any time. The installation path and folder names for Windows itself can also be specified by the user during setup. By hard-coding full paths like "C:\My Documents" or "C:\Windows" into your application you fail to consider the user's specific installation or the network administrator's requirements. The net result is you end up requesting this from the user by either having them type in or locate the system folders for you, adding the extra step of confirming their existence before accessing the folder.

By using SHGetFolderPath to retrieve the path to special shell folders each and every time folder identification is required, your application will be able to dynamically cope with the user's potentially changing environment, across differing operating systems. SHGetFolderPath, in conjunction with the new CSIDLs (logical directory indicators), will easily retrieve the correct location for various types of data.  CSIDLs provide a unique system-independent way to identify special folders. There are CSIDLs for almost every important shell folder - CSIDL_PERSONAL, CSIDL_COMMON_DOCUMENTS, CSIDL_FAVORITES and CSIDL_TEMPLATES are just a few of the ones more commonly used. This page, as well as others at VBnet (above), provide listings of CSIDLs for common locations.

And while it is possible to obtain folder paths by other means (user intervention, drive searches), remember that an administrator can change the path pointed to by these logical folders at any time. This means that, unless you query for the logical folder using a call to SHGetFolderPath, you may have out of date path information.

Note that some constants represent non-file system elements (e.g. Drives, Nethood) which are part of Windows explorer namespace. These elements do not return file system paths and so may not return a value to the textbox when selected.

Considerations
System support for SHGetFolderPath is not native on Windows 98 (original version), Windows 95 (all versions), and Windows NT 4.0 (SP5 and earlier). These down level clients require the installation of new system files prior to use as mentioned above.

The illustrations shows the constant name, type (either a virtual or true file system path), any special considerations, and the typical results of the call. The actual path according to the currently logged-on user is reflected in the text box. Other options can be specified as the call's flags member.

 BAS Module Code
None.

 
 Form Code
To a project form add a text box (Text1) and a list box (List1), along with three check boxes (Check1-Check3). Add the following to the form:

Option Explicit
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
' Copyright 1996-2011 VBnet/Randy Birch, All Rights Reserved.
' Some pages may also contain other copyrights by the author.
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
' Distribution: You can freely use this code in your own
'               applications, but you may not reproduce 
'               or publish this code on any web site,
'               online service, or distribute as source 
'               on any media without express permission.
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
Private Enum CSIDL_VALUES
    CSIDL_DESKTOP = &H0
    CSIDL_INTERNET = &H1
    CSIDL_PROGRAMS = &H2
    CSIDL_CONTROLS = &H3
    CSIDL_PRINTERS = &H4
    CSIDL_PERSONAL = &H5
    CSIDL_FAVORITES = &H6
    CSIDL_STARTUP = &H7
    CSIDL_RECENT = &H8
    CSIDL_SENDTO = &H9
    CSIDL_BITBUCKET = &HA
    CSIDL_STARTMENU = &HB
    CSIDL_MYDOCUMENTS = &HC
    CSIDL_MYMUSIC = &HD
    CSIDL_MYVIDEO = &HE
    CSIDL_DESKTOPDIRECTORY = &H10
    CSIDL_DRIVES = &H11
    CSIDL_NETWORK = &H12
    CSIDL_NETHOOD = &H13
    CSIDL_FONTS = &H14
    CSIDL_TEMPLATES = &H15
    CSIDL_COMMON_STARTMENU = &H16
    CSIDL_COMMON_PROGRAMS = &H17
    CSIDL_COMMON_STARTUP = &H18
    CSIDL_COMMON_DESKTOPDIRECTORY = &H19
    CSIDL_APPDATA = &H1A
    CSIDL_PRINTHOOD = &H1B
    CSIDL_LOCAL_APPDATA = &H1C
    CSIDL_ALTSTARTUP = &H1D
    CSIDL_COMMON_ALTSTARTUP = &H1E
    CSIDL_COMMON_FAVORITES = &H1F
    CSIDL_INTERNET_CACHE = &H20
    CSIDL_COOKIES = &H21
    CSIDL_HISTORY = &H22
    CSIDL_COMMON_APPDATA = &H23
    CSIDL_WINDOWS = &H24
    CSIDL_SYSTEM = &H25
    CSIDL_PROGRAM_FILES = &H26
    CSIDL_MYPICTURES = &H27
    CSIDL_PROFILE = &H28
    CSIDL_SYSTEMX86 = &H29
    CSIDL_PROGRAM_FILESX86 = &H2A
    CSIDL_PROGRAM_FILES_COMMON = &H2B
    CSIDL_PROGRAM_FILES_COMMONX86 = &H2C
    CSIDL_COMMON_TEMPLATES = &H2D
    CSIDL_COMMON_DOCUMENTS = &H2E
    CSIDL_COMMON_ADMINTOOLS = &H2F
    CSIDL_ADMINTOOLS = &H30
    CSIDL_CONNECTIONS = &H31
    CSIDL_COMMON_MUSIC = &H35
    CSIDL_COMMON_PICTURES = &H36
    CSIDL_COMMON_VIDEO = &H37
    CSIDL_RESOURCES = &H38
    CSIDL_RESOURCES_LOCALIZED = &H39
    CSIDL_COMMON_OEM_LINKS = &H3A
    CSIDL_CDBURN_AREA = &H3B
    CSIDL_COMPUTERSNEARME = &H3D
    CSIDL_FLAG_PER_USER_INIT = &H800
    CSIDL_FLAG_NO_ALIAS = &H1000
    CSIDL_FLAG_DONT_VERIFY = &H4000
    CSIDL_FLAG_CREATE = &H8000
    CSIDL_FLAG_MASK = &HFF00
End Enum

Private Const SHGFP_TYPE_CURRENT = &H0 'current value for user, verify it exists
Private Const SHGFP_TYPE_DEFAULT = &H1

Private Const MAX_LENGTH = 260
Private Const S_OK = 0
Private Const S_FALSE = 1

Private Declare Function lstrlenW Lib "kernel32" _
  (ByVal lpString As Long) As Long

Private Declare Function SHGetFolderPath Lib "shfolder.dll" _
   Alias "SHGetFolderPathA" _
  (ByVal hwndOwner As Long, _
   ByVal nFolder As Long, _
   ByVal hToken As Long, _
   ByVal dwReserved As Long, _
   ByVal lpszPath As String) As Long
   

'General Declarations code (form)
Private Declare Function SendMessage Lib "user32" Alias "SendMessageA" _
  (ByVal hwnd As Long, _
   ByVal wMsg As Long, _
   ByVal wParam As Long, _
   lParam As Any) As Long

Private Const LB_SETTABSTOPS As Long = &H192


Private Sub Form_Load()

   ReDim TabArray(0 To 2) As Long

   TabArray(0) = 146
   TabArray(1) = 176
   TabArray(2) = 213
      
  'Clear existing tabs and set the list tabstop
   Call SendMessage(List1.hwnd, LB_SETTABSTOPS, 0&, ByVal 0&)
   Call SendMessage(List1.hwnd, LB_SETTABSTOPS, 4&, TabArray(0))

   With List1
      .AddItem "CSIDL_DESKTOP" & _
                vbTab & "virtual" & _
                vbTab & vbTab & ""
      .ItemData(.NewIndex) = CSIDL_DESKTOP
      
      .AddItem "CSIDL_INTERNET" & _
                vbTab & "virtual" & _
                vbTab & vbTab & "Internet Explorer (icon on desktop)"
      .ItemData(.NewIndex) = CSIDL_INTERNET
      
      .AddItem "CSIDL_PROGRAMS" & _
                vbTab & "file" & _
                vbTab & vbTab & "Start Menu\Programs"
      .ItemData(.NewIndex) = CSIDL_PROGRAMS
      
      .AddItem "CSIDL_CONTROLS" & _
                vbTab & "virtual" & _
                vbTab & vbTab & "My Computer\Control Panel"
      .ItemData(.NewIndex) = CSIDL_CONTROLS
      
      .AddItem "CSIDL_PRINTERS" & _
                vbTab & "virtual" & _
                vbTab & vbTab & "My Computer\Printers"
      .ItemData(.NewIndex) = CSIDL_PRINTERS
      
      .AddItem "CSIDL_PERSONAL" & _
                vbTab & "file" & _
                vbTab & vbTab & "My Documents"
      .ItemData(.NewIndex) = CSIDL_PERSONAL
      
      .AddItem "CSIDL_FAVORITES" & _
                vbTab & "file" & _
                vbTab & vbTab & "\Favorites"
      .ItemData(.NewIndex) = CSIDL_FAVORITES
      
      .AddItem "CSIDL_STARTUP" & _
                vbTab & "file" & _
                vbTab & vbTab & "Start Menu\Programs\Startup"
      .ItemData(.NewIndex) = CSIDL_STARTUP
      
      .AddItem "CSIDL_RECENT" & _
                vbTab & "file" & _
                vbTab & vbTab & "\Recent"
      .ItemData(.NewIndex) = CSIDL_RECENT
      
      .AddItem "CSIDL_SENDTO" & _
                vbTab & "file" & _
                vbTab & vbTab & "\SendTo"
      .ItemData(.NewIndex) = CSIDL_SENDTO
      
      .AddItem "CSIDL_BITBUCKET" & _
                vbTab & "virtual" & _
                vbTab & vbTab & "\Recycle Bin"
      .ItemData(.NewIndex) = CSIDL_BITBUCKET
      
      .AddItem "CSIDL_STARTMENU" & _
                vbTab & "file" & _
                vbTab & vbTab & "\Start Menu"
      .ItemData(.NewIndex) = CSIDL_STARTMENU
      
      .AddItem "CSIDL_MYDOCUMENTS" & _
                vbTab & "virtual" & _
                vbTab & vbTab & "\My Documents\"
      .ItemData(.NewIndex) = CSIDL_MYDOCUMENTS
      
      .AddItem "CSIDL_MYMUSIC" & _
                vbTab & "file" & _
                vbTab & vbTab & "\My Documents\My Music"
      .ItemData(.NewIndex) = CSIDL_MYMUSIC
      
      .AddItem "CSIDL_MYVIDEO" & _
                vbTab & "file" & _
                vbTab & vbTab & "\My Documents\My Video"
      .ItemData(.NewIndex) = CSIDL_MYVIDEO
      
      .AddItem "CSIDL_DESKTOPDIRECTORY" & _
                vbTab & "file" & _
                vbTab & vbTab & "\Desktop"
      .ItemData(.NewIndex) = CSIDL_DESKTOPDIRECTORY
      
      .AddItem "CSIDL_DRIVES" & _
                vbTab & "virtual" & _
                vbTab & vbTab & "My Computer"
      .ItemData(.NewIndex) = CSIDL_DRIVES
      
      .AddItem "CSIDL_NETWORK" & _
                vbTab & "virtual" & _
                vbTab & vbTab & "Network Neighborhood"
      .ItemData(.NewIndex) = CSIDL_NETWORK
      
      .AddItem "CSIDL_NETHOOD" & _
                vbTab & "file" & _
                vbTab & vbTab & "\nethood (may dupe My Network Places)"
      .ItemData(.NewIndex) = CSIDL_NETHOOD
      
      .AddItem "CSIDL_FONTS" & _
                vbTab & "virtual" & _
                vbTab & vbTab & "windows\fonts"
      .ItemData(.NewIndex) = CSIDL_FONTS
      
      .AddItem "CSIDL_TEMPLATES" & _
                vbTab & "file" & _
                vbTab & vbTab & "\templates"
      .ItemData(.NewIndex) = CSIDL_TEMPLATES
      
      .AddItem "CSIDL_COMMON_STARTMENU" & _
                vbTab & "file" & _
                vbTab & vbTab & "\Start Menu"
      .ItemData(.NewIndex) = CSIDL_COMMON_STARTMENU
      
      .AddItem "CSIDL_COMMON_PROGRAMS" & _
                vbTab & "file" & _
                vbTab & vbTab & "\Programs"
      .ItemData(.NewIndex) = CSIDL_COMMON_PROGRAMS
      
      .AddItem "CSIDL_COMMON_STARTUP" & _
                vbTab & "file" & _
                vbTab & vbTab & "\Startup"
      .ItemData(.NewIndex) = CSIDL_COMMON_STARTUP
      
      .AddItem "CSIDL_COMMON_DESKTOPDIRECTORY" & _
                vbTab & "file" & _
                vbTab & vbTab & "\Desktop"
      .ItemData(.NewIndex) = CSIDL_COMMON_DESKTOPDIRECTORY
      
      .AddItem "CSIDL_APPDATA" & _
                vbTab & "file" & _
                vbTab & "v4.71" & vbTab & "\Application Data"
      .ItemData(.NewIndex) = CSIDL_APPDATA
      
      .AddItem "CSIDL_PRINTHOOD" & _
                vbTab & "file" & _
                vbTab & vbTab & "\PrintHood"
      .ItemData(.NewIndex) = CSIDL_PRINTHOOD
      
      .AddItem "CSIDL_LOCAL_APPDATA" & _
                vbTab & "file" & _
                vbTab & "v5.0" & _
                vbTab & "\Local Settings\Applicaiton Data (non roaming)"
      .ItemData(.NewIndex) = CSIDL_LOCAL_APPDATA
      
      .AddItem "CSIDL_ALTSTARTUP" & _
                vbTab & "file" & _
                vbTab & vbTab & "nonlocalized startup program group"
      .ItemData(.NewIndex) = CSIDL_ALTSTARTUP
      
      .AddItem "CSIDL_COMMON_ALTSTARTUP" & _
                vbTab & "file" & _
                vbTab & "NT only" & _
                vbTab & "nonlocalized Startup group for all users"
      .ItemData(.NewIndex) = CSIDL_COMMON_ALTSTARTUP
      
      .AddItem "CSIDL_COMMON_FAVORITES" & _
                vbTab & "file" & _
                vbTab & "NT only" & _
                vbTab & "all user's favorite items"
      .ItemData(.NewIndex) = CSIDL_COMMON_FAVORITES
      
      .AddItem "CSIDL_INTERNET_CACHE" & _
                vbTab & "file" & _
                vbTab & "v4.72" & _
                vbTab & "temporary Internet files"
      .ItemData(.NewIndex) = CSIDL_INTERNET_CACHE
      
      .AddItem "CSIDL_COOKIES" & _
                vbTab & "file" & _
                vbTab & "NT only" & _
                vbTab & "Internet cookies"
      .ItemData(.NewIndex) = CSIDL_COOKIES
      
      .AddItem "CSIDL_HISTORY" & _
                vbTab & "file" & _
                vbTab & "NT only" & _
                vbTab & "Internet history items"
      .ItemData(.NewIndex) = CSIDL_HISTORY
      
      .AddItem "CSIDL_COMMON_APPDATA" & _
                vbTab & "file" & _
                vbTab & "v5.0" & _
                vbTab & "\Application Data"
      .ItemData(.NewIndex) = CSIDL_COMMON_APPDATA
      
      .AddItem "CSIDL_WINDOWS" & _
                vbTab & "file" & _
                vbTab & vbTab & "v5.0" & "Windows directory or SYSROOT"
      .ItemData(.NewIndex) = CSIDL_WINDOWS
      
      .AddItem "CSIDL_SYSTEM" & _
                vbTab & "file" & _
                vbTab & vbTab & "v5.0" & "GetSystemDirectory()"
      .ItemData(.NewIndex) = CSIDL_SYSTEM
      
      .AddItem "CSIDL_PROGRAM_FILES" & _
                vbTab & "file" & _
                vbTab & "v5.0" & vbTab & "C:\Program Files"
      .ItemData(.NewIndex) = CSIDL_PROGRAM_FILES
      
      .AddItem "CSIDL_MYPICTURES " & _
                vbTab & "file" & _
                vbTab & "v5.0" & vbTab & "\My Documents\My Pictures"
      .ItemData(.NewIndex) = CSIDL_MYPICTURES
      
      .AddItem "CSIDL_PROFILE" & _
                vbTab & "file" & _
                vbTab & "v5.0" & _
                vbTab & "\"
      .ItemData(.NewIndex) = CSIDL_PROFILE
      
      .AddItem "CSIDL_SYSTEMX86" & _
                vbTab & "file" & _
                vbTab & vbTab & "x86 system directory on RISC"
      .ItemData(.NewIndex) = CSIDL_SYSTEMX86
      
      .AddItem "CSIDL_PROGRAM_FILESX86" & _
                vbTab & "file" & _
                vbTab & vbTab & "x86 Program Files folder on RISC"
      .ItemData(.NewIndex) = CSIDL_PROGRAM_FILESX86
      
      .AddItem "CSIDL_PROGRAM_FILES_COMMON" & _
                vbTab & "file" & _
                vbTab & "v5.0" & _
                vbTab & "C:\Program Files\Common"
      .ItemData(.NewIndex) = CSIDL_PROGRAM_FILES_COMMON
      
      .AddItem "CSIDL_PROGRAM_FILES_COMMONX86" & _
                vbTab & "file" & _
                vbTab & vbTab & _
                "x86 Program Files Common folder on RISC"
      .ItemData(.NewIndex) = CSIDL_PROGRAM_FILES_COMMONX86
      
      .AddItem "CSIDL_COMMON_TEMPLATES" & _
                vbTab & "file" & _
                vbTab & vbTab & "\Templates"
      .ItemData(.NewIndex) = CSIDL_COMMON_TEMPLATES
      
      .AddItem "CSIDL_COMMON_DOCUMENTS" & _
                vbTab & "file" & _
                vbTab & vbTab & "\Documents"
      .ItemData(.NewIndex) = CSIDL_COMMON_DOCUMENTS
      
      .AddItem "CSIDL_COMMON_ADMINTOOLS" & _
                vbTab & "file" & _
                vbTab & "v5.0" & _
                vbTab & "\Start Menu\Programs\Administrative Tools"
      .ItemData(.NewIndex) = CSIDL_COMMON_ADMINTOOLS
      
      .AddItem "CSIDL_ADMINTOOLS " & _
                vbTab & "file" & _
                vbTab & "v5.0" & _
                vbTab & "\Start Menu\Programs\Administrative Tools"
      .ItemData(.NewIndex) = CSIDL_ADMINTOOLS
      
      .AddItem "CSIDL_CONNECTIONS" & _
                vbTab & "virtual" & _
                vbTab & vbTab & "Network and dial-up connections folder"
      .ItemData(.NewIndex) = CSIDL_CONNECTIONS
      
      .AddItem "CSIDL_COMMON_MUSIC" & _
                vbTab & "file" & _
                vbTab & vbTab & "My Music folder for all users"
      .ItemData(.NewIndex) = CSIDL_COMMON_MUSIC
      
      .AddItem "CSIDL_COMMON_PICTURES" & _
                vbTab & "file" & _
                vbTab & vbTab & "My Pictures folder for all users"
      .ItemData(.NewIndex) = CSIDL_COMMON_PICTURES
      
      .AddItem "CSIDL_COMMON_VIDEO" & _
                vbTab & "file" & _
                vbTab & vbTab & "My Video folder for all users"
      .ItemData(.NewIndex) = CSIDL_COMMON_VIDEO
      
      .AddItem "CSIDL_RESOURCES" & _
                vbTab & "file" & _
                vbTab & vbTab & "System resource directory"
      .ItemData(.NewIndex) = CSIDL_RESOURCES
      
      .AddItem "CSIDL_RESOURCES_LOCALIZED" & _
                vbTab & "file" & _
                vbTab & vbTab & "Localized resource directory"
      .ItemData(.NewIndex) = CSIDL_RESOURCES_LOCALIZED
      
      .AddItem "CSIDL_COMMON_OEM_LINKS" & _
                vbTab & "file" & _
                vbTab & vbTab & "Links to OEM specific apps for all users"
      .ItemData(.NewIndex) = CSIDL_COMMON_OEM_LINKS
      
      .AddItem "CSIDL_CDBURN_AREA" & _
                vbTab & "file" & _
                vbTab & vbTab & "\Local Settings\Application Data\Microsoft\CD Burning"
      .ItemData(.NewIndex) = CSIDL_CDBURN_AREA
      
      .AddItem "CSIDL_COMPUTERSNEARME" & _
                vbTab & "virtual" & _
                vbTab & vbTab & "Computers Near Me folder"
      .ItemData(.NewIndex) = CSIDL_COMPUTERSNEARME
   
   End With
   
End Sub


Private Sub List1_Click()

   Dim csidl As Long
   
   If List1.ListIndex > -1 Then
   
      csidl = List1.ItemData(List1.ListIndex)
   
      If csidl > -1 Then
      
        'show the folder's current path
         Text1.Text = GetFolderPath(csidl, SHGFP_TYPE_CURRENT)
         
      End If

   End If

End Sub


Private Function GetFolderPath(csidl As Long, SHGFP_TYPE As Long) As String

   Dim buff As String
   Dim dwFlags As Long
  
  'fill buffer with the specified folder item
   buff = Space$(MAX_LENGTH)
   
   If Check1.Value = vbChecked Then dwFlags = dwFlags Or CSIDL_FLAG_PER_USER_INIT
   If Check2.Value = vbChecked Then dwFlags = dwFlags Or CSIDL_FLAG_NO_ALIAS
   If Check3.Value = vbChecked Then dwFlags = dwFlags Or CSIDL_FLAG_DONT_VERIFY
   
   If SHGetFolderPath(Me.hwnd, _
                      csidl Or dwFlags, _
                      -1, _
                      SHGFP_TYPE, _
                      buff) = S_OK Then
                      
       GetFolderPath = TrimNull(buff)
       
   End If
   
End Function


Private Function TrimNull(startstr As String) As String

   TrimNull = Left$(startstr, lstrlenW(StrPtr(startstr)))
   
End Function
 Comments
Run and select an item from the list.  If the selected item represents a physical folder, and your system supports the call, the folder path will be displayed in the textbox.

 
 

PayPal Link
Make payments with PayPal - it's fast, free and secure!

 
 
 
 

Copyright 1996-2011 VBnet and Randy Birch. All Rights Reserved.
Terms of Use  |  Your Privacy

 

Hit Counter