This is an example to show how we can update Thumbnailer and refresh its contents if changes are made in the current Path. The Thumbnailer control will refresh its contents when files are created, renamed or removed from the current Path, as an user's action or the result of a drag and drop operation. Create the main form (Form1) and place the following components on it: - A Thumbnailer control called "Thumbnailer" Copy and paste to the Form the following code: ####################### CODE START ############################ Option Explicit Private Sub Form_Load() ' Take control of message processing by installing our message ' handling routine into the chain of message routines for Form1 StartWatch hWnd End Sub Private Sub Form_Unload(Cancel As Integer) ' Must return control to the system StopWatch hWnd End Sub ' Use this proc to update the control in case it needs to be refreshed. ' We set this proc PUBLIC so it can be called from WindowProc in the ' mShellNotify module. Public Sub UpdateControl(ByVal iEventID As Long, ByVal sInfoA As String, ByVal sInfoB As String) On Error Resume Next Dim sPathA As String Dim sPathB As String Dim sNameA As String Dim sNameB As String DoEvents ' Notification event Select Case iEventID ' CREATE ' sInfoA returns the Path(s) of the changed item(s) ' sInfoB is not used Case SHCNE_CREATE ' This line is used only for information Debug.Print "CREATE ", sInfoA ' Get the Path of the item sPathA = GetFilePath(sInfoA) ' Get the Name of the item sNameA = GetFileName(sInfoA) ' If the Path is the current one we view in Thumbnailer If UCase$(sPathA) = UCase$(Thumbnailer.Path) Then ' Add the item into the control Thumbnailer.AddItem sPathA, sNameA ' Manually refresh contents Thumbnailer.DrawItems End If ' DELETE ' sInfoA returns the Path(s) of the changed item(s) ' sInfoB is not used Case SHCNE_DELETE ' This line is used only for information Debug.Print "DELETE ", sInfoA ' Get the Path of the item sPathA = GetFilePath(sInfoA) ' Get the Name of the item sNameA = GetFileName(sInfoA) ' If the Path is the one we view in Thumbnailer If UCase$(sPathA) = UCase$(Thumbnailer.Path) Then ' Delete the item from the control Thumbnailer.DeleteItems sPathA & sNameA & vbNullChar & vbNullChar ' Manually refresh contents Thumbnailer.DrawItems End If ' RENAME ' sInfoA returns the previous name of the changed item ' sInfoB returns the new name of the changed item Case SHCNE_RENAMEITEM ' In an item is moved to another folder in the same drive, ' the system does not generate SHCNE_DELETE or SHCNE_CREATE ' messages. Instead, it generates a SHCNE_RENAMEITEM ' message, as it just renames the item. ' This is what also happens if an item is recovered from ' the Recycle-Bin. ' the sInfoB member contains the new name ' This line is used only for information Debug.Print "RENAME ", sInfoA, sInfoB ' Get the previous Path of the item sPathA = GetFilePath(sInfoA) ' Get the new Path of the item sPathB = GetFilePath(sInfoB) ' If the Paths are equal, then we have a true RENAME case If UCase$(sPathA) = UCase$(sPathB) Then ' If the Paths are also equal with the one we view ' in Thumbnailer then we can just call RenameItem If UCase$(sPathA) = UCase$(Thumbnailer.Path) Then ' Get the previous Name of the item sNameA = GetFileName(sInfoA) ' Get the new Name of the item sNameB = GetFileName(sInfoB) ' Rename the Thumbnailer's item Thumbnailer.RenameItem sNameA, sNameB ' Manually refresh contents Thumbnailer.DrawItems End If ' If the Paths are not equal, then we have a MOVE case Else ' If sPathA is the current control's Path, then we have ' a Move operation FROM the current path to outside If UCase$(sPathA) = UCase$(Thumbnailer.Path) Then ' Get the Name of the item is MOVED FROM path sNameA = GetFileName(sInfoA) ' Delete the moved item from the control Thumbnailer.DeleteItems sPathA & sNameA & vbNullChar & vbNullChar ' Manually refresh contents Thumbnailer.DrawItems ' If sPathB is the current control's Path, then we have ' a Move operation from outside TO the current path ElseIf UCase$(sPathB) = UCase$(Thumbnailer.Path) Then ' Get the Name of the item is MOVED TO path sNameB = GetFileName(sInfoB) ' Add the item into the control Thumbnailer.AddItem sPathB, sNameB ' Manually refresh contents Thumbnailer.DrawItems End If End If ' ATTRIBUTES CHANGE ' sInfoA returns the Path(s) of the changed item(s) ' sInfoB is not used Case SHCNE_ATTRIBUTES ' This line is used only for information Debug.Print "ATTRIBUTES ", sInfoA ' We have to update the control if we have set the control ' to show Thumbnails for files with specific attributes ' and sInfoA concerns any item in the current Path. ' NOT IMPLEMENTED IN THIS EXAMPLE End Select End Sub ######################## CODE END ############################# Create a BAS module (mShellNotify.bas). Copy and paste to the module the following code: ####################### CODE START ############################ Option Explicit Private iOldProc As Long ' Original proc handle Private iSHNotify As Long ' Shell change notification handle for the desktop folder Private iDesktopPidl As Long ' Desktop's pidl Private Const SEPARATOR = "\" Private Const GWL_WNDPROC = (-4) Private Const WM_SHNOTIFY = &H401 Private Const MAX_PATH = 260 Private Enum SHGFI_flags SHGFI_PIDL = &H8 ' pszPath is pidl, rtns BOOL SHGFI_DISPLAYNAME = &H200 ' isf.szDisplayName is filled, rtns BOOL End Enum ' Shell notification event IDs Public Enum SHCN_EventIDs SHCNE_RENAMEITEM = &H1 ' (D) A nonfolder item has been renamed. SHCNE_CREATE = &H2 ' (D) A nonfolder item has been created. SHCNE_DELETE = &H4 ' (D) A nonfolder item has been deleted. SHCNE_ATTRIBUTES = &H800 ' (D) The attributes of an item or folder have changed. SHCNE_ALLEVENTSD = &H807 ' &H1 Or &H2 Or &H4 Or &H800 SHCNE_INTERRUPT = &H80000000 ' The specified event occurred as a result of a system interrupt. ' It is stripped out before the clients of SHCNNotify_ see it. End Enum ' Notification flags Private Enum SHCN_ItemFlags SHCNF_IDLIST = &H0 SHCNF_TYPE = &HFF End Enum Private Type SHFILEINFO hIcon As Long iIcon As Long dwAttributes As Long szDisplayName As String * MAX_PATH szTypeName As String * 80 End Type Private Type PIDLSTRUCT pidl As Long ' 0 for Desktop bWatchSubFolders As Long End Type ' Notification returned info Private Type SHNOTIFYSTRUCT dwItem1 As Long dwItem2 As Long End Type Private Declare Function GetWindowLong Lib "user32" Alias "GetWindowLongA" _ (ByVal hWnd As Long, _ ByVal nIndex As Long) As Long Private Declare Function SetWindowLong Lib "user32" Alias "SetWindowLongA" _ (ByVal hWnd As Long, _ ByVal nIndex As Long, _ ByVal dwNewLong As Long) As Long Private Declare Function CallWindowProc Lib "user32" Alias "CallWindowProcA" _ (ByVal lpPrevWndFunc As Long, _ ByVal hWnd As Long, _ ByVal uMsg As Long, _ ByVal wParam As Long, _ ByVal lParam As Long) As Long Private Declare Function DefWindowProc Lib "user32" Alias "DefWindowProcA" _ (ByVal hWnd As Long, _ ByVal wMsg As Long, _ ByVal wParam As Long, _ ByVal lParam As Long) As Long Private Declare Sub MoveMemory Lib "kernel32" Alias "RtlMoveMemory" _ (pDest As Any, _ pSource As Any, _ ByVal dwLength As Long) Private Declare Function SHChangeNotifyRegister Lib "shell32" Alias "#2" _ (ByVal hWnd As Long, _ ByVal uFlags As SHCN_ItemFlags, _ ByVal dwEventID As SHCN_EventIDs, _ ByVal uMsg As Long, _ ByVal cItems As Long, _ lpps As PIDLSTRUCT) As Long Private Declare Function SHChangeNotifyDeregister Lib "shell32" Alias "#4" _ (ByVal hNotify As Long) As Boolean ' Retrieves the location of a special (system) folder. Private Declare Function SHGetSpecialFolderLocation Lib "shell32.dll" _ (ByVal hwndOwner As Long, _ ByVal nFolder As Long, _ pidl As Long) As Long ' Converts an item identifier list to a file system path. Private Declare Function SHGetPathFromIDList Lib "shell32.dll" Alias "SHGetPathFromIDListA" _ (ByVal pidl As Long, _ ByVal pszPath As String) As Long ' Retrieves information about an object in the file system. Private Declare Function SHGetFileInfo Lib "shell32" Alias "SHGetFileInfoA" _ (ByVal pszPath As Any, _ ByVal dwFileAttributes As Long, _ psfib As SHFILEINFO, _ ByVal cbFileInfo As Long, _ ByVal uFlags As SHGFI_flags) As Long ' Frees memory allocated by the shell (pidls) Private Declare Sub CoTaskMemFree Lib "ole32.dll" (ByVal pv As Long) ' Watch for shell changes Public Sub StartWatch(ByVal hWnd As Long) ' Subclass the Form1 and get the current proc handle iOldProc = SetWindowLong(hWnd, GWL_WNDPROC, AddressOf WindowProc) ' Set the watch messages routine SHNotify_Register hWnd End Sub ' Stop watching shell messages Public Sub StopWatch(ByVal hWnd As Long) ' Destroy our watch messages routine SHNotify_Unregister ' Give process back to the system and restore the original handle SetWindowLong hWnd, GWL_WNDPROC, iOldProc End Sub ' Registers the shell change notification Private Sub SHNotify_Register(hWnd As Long) Dim PS As PIDLSTRUCT ' If we don't already have a notification going If iSHNotify = 0 Then ' Get the pidl for the desktop folder. iDesktopPidl = GetDesktopPIDL(hWnd) ' If we have got the Desktop's PIDL If iDesktopPidl Then ' Fill the PIDLSTRUCT, we're watching ' desktop and all of the it's subfolders PS.pidl = iDesktopPidl PS.bWatchSubFolders = True ' Register the notification, specifying that we want the dwItem1 ' and dwItem2 members of the SHNOTIFYSTRUCT to be pidls. iSHNotify = SHChangeNotifyRegister(hWnd, _ SHCNF_TYPE Or SHCNF_IDLIST, _ SHCNE_ALLEVENTSD Or SHCNE_INTERRUPT, WM_SHNOTIFY, 1, PS) ' If we could not get the Desktop's PIDL Else ' Free resources CoTaskMemFree iDesktopPidl End If End If End Sub ' Unregisters the shell change notification Private Sub SHNotify_Unregister() ' If we have a registered notification handle If iSHNotify Then ' Unregister it. If the call is successful, zero the handle, ' free and zero the desktop's pidl. If SHChangeNotifyDeregister(iSHNotify) Then iSHNotify = 0 Call CoTaskMemFree(iDesktopPidl) iDesktopPidl = 0 End If End If End Sub ' Returns the Desktop's PIDL ' ' hWnd - handle of window that will own any displayed msg boxes ' ' Called from SHNotify_Register Private Function GetDesktopPIDL(hWnd As Long) As Long Dim iPIDL As Long ' Get the PIDL If SHGetSpecialFolderLocation(hWnd, 0, iPIDL) = 0 Then GetDesktopPIDL = iPIDL End If End Function ' Returns a path from an absolute iPIDL (relative to the desktop) ' on success, or an empty string otherwise. Private Function GetPathFromPIDL(ByVal iPIDL As Long) As String Dim sPath As String * MAX_PATH If SHGetPathFromIDList(iPIDL, sPath) Then GetPathFromPIDL = GetStrFromBufferA(sPath) End If End Function ' Returns the string before first null char encountered (if any) from an ANSII string. Private Function GetStrFromBufferA(ByVal sString As String) As String If InStr(sString, vbNullChar) Then GetStrFromBufferA = Left$(sString, InStr(sString, vbNullChar) - 1) Else ' If sString had no null char, the Left$ function ' above would return a zero length string (""). GetStrFromBufferA = sString End If End Function ' Returns the Path for a specified full file path and name. The string ' is always terminated by a "\" char. On error returns vbNullString. ' ' Called from Form1.UpdateControl Public Function GetFilePath(ByVal sPathName As String) As String On Error Resume Next Dim i As Long ' Error handling ' Remove any "\" char from the last position If Right$(sPathName, 1) = SEPARATOR Then sPathName = Left$(sPathName, Len(sPathName) - 1) End If For i = Len(sPathName) To 1 Step -1 If Mid$(sPathName, i, 1) = SEPARATOR Then GetFilePath = Left$(sPathName, i) Exit For End If Next End Function ' Returns the Filename for a specified full file path and name. ' On error returns vbNullString. ' ' Called from Form1.UpdateControl Public Function GetFileName(ByVal sPathName As String) As String On Error Resume Next Dim i As Long ' Error handling ' Remove any "\" char from the last position If Right$(sPathName, 1) = SEPARATOR Then sPathName = Left$(sPathName, Len(sPathName) - 1) End If For i = Len(sPathName) To 1 Step -1 If Mid$(sPathName, i, 1) = SEPARATOR Then GetFileName = Right$(sPathName, Len(sPathName) - i) Exit For End If Next End Function ' Custom procedure to process the messages Public Function WindowProc(ByVal hWnd As Long, ByVal uMsg As Long, ByVal wParam As Long, ByVal lParam As Long) As Long Static prevEventID As Long ' The previous event ID Static prevItem1 As String ' The previous Item1 info Static prevItem2 As String ' The previous Item2 info Dim sInfoA As String ' dwItem1 notification info Dim sInfoB As String ' dwItem2 notification info Dim SHN As SHNOTIFYSTRUCT ' Shell change notifications If uMsg = WM_SHNOTIFY Then DoEvents ' Fill the SHNOTIFYSTRUCT from it's pointer MoveMemory SHN, ByVal wParam, Len(SHN) ' lParam is the ID of the notication event Select Case lParam ' SHCNE_CREATE, SHCNE_DELETE ' SHN.dwItem1 is the PIDL(s) of the changed item(s) ' SHN.dwItem2 is not used Case SHCNE_CREATE, SHCNE_DELETE If SHN.dwItem1 Then ' Get the Item's Path from its PIDL sInfoA = GetPathFromPIDL(SHN.dwItem1) ' As duplicate notifications occur, we check the previous event ' If the notification is a new one If ((prevEventID <> lParam) Or (prevItem1 <> sInfoA)) Then ' Inform the app that the control may need to be refreshed Call Form1.UpdateControl(lParam, sInfoA, vbNullString) End If End If ' SHCNE_RENAMEITEM ' SHN.dwItem1 is the previous PIDL or name of the item. ' SHN.dwItem2 is the new PIDL or name of the item. Case SHCNE_RENAMEITEM If SHN.dwItem1 And SHN.dwItem2 Then ' Get the Item's Path from its PIDL sInfoA = GetPathFromPIDL(SHN.dwItem1) ' Get the Item's Path from its PIDL sInfoB = GetPathFromPIDL(SHN.dwItem2) ' As duplicate notifications occur, we check the previous event ' If the notification is a new one If ((prevEventID <> lParam) Or (prevItem1 <> sInfoA) Or (prevItem2 <> sInfoB)) Then ' Inform the app that the control may need to be refreshed Call Form1.UpdateControl(lParam, sInfoA, sInfoB) End If End If ' SHCNE_ATTRIBUTES ' SHN.dwItem1 is the PIDL(s) of the changed item(s) ' SHN.dwItem2 is not used Case SHCNE_ATTRIBUTES If SHN.dwItem1 Then ' Get the Item's Path from its PIDL sInfoA = GetPathFromPIDL(SHN.dwItem1) ' As duplicate notifications occur, we check the previous event ' If the notification is a new one If ((prevEventID <> lParam) Or (prevItem1 <> sInfoA)) Then ' Inform the app that the control may need to be refreshed Call Form1.UpdateControl(lParam, sInfoA, vbNullString) End If End If End Select ' Store the events details for duplicate notifications prevEventID = lParam prevItem1 = sInfoA prevItem2 = sInfoB End If ' System processes the message WindowProc = CallWindowProc(iOldProc, hWnd, uMsg, wParam, lParam) End Function ######################## CODE END ############################# Run the project, make any changes(rename, delete) to a file currently displayed in Thumbnailer or drag items from Thumbnailer and drop them anywhere with a Move action. Thumbnailer will refresh its contents to show any changes.