6

I am using Windows 8.1 with Windows Explorer. The image cache ("thumbs.db") is created when I visit a folder containing video, photo, or other such files. I have a problem with this because I have multiple folders with large videos and photos that are constantly changing. Every time I open a folder containing these files I have to wait several seconds until the thumbnail cache is created.

One workaround would be to disable the thumbnail cache. But I like to see thumbnails so this is not a solution for me. Instead, I would like to call a batch or another program every x-seconds that creates the Windows thumbnails recursively. I could then open the folders without delay.

How can I accomplish this?

5
  • This solution works for pictures in a single directory. I don't see why it won't work for sub directories as well.
    – Vinayak
    Feb 27, 2015 at 9:29
  • After adding files to a folder in a background process the cache needs to update. This will only be done after you visit the folder in Explorer. So I need a script that will update the cache of some folders (recursively) in a defined delay.
    – dude
    Feb 27, 2015 at 9:50
  • You could search for image and video formats from the root directory to get a listing of them all. As for scrolling through the results, that could be achieved with a simple AutoIt script that sends the Page Down key ({PGDOWN}) repeatedly in a loop.
    – Vinayak
    Feb 27, 2015 at 9:51
  • You could create an AutoIT script that does all of the above and schedule it to run, say every thirty minutes or so. Or you could manually trigger it.
    – Vinayak
    Feb 27, 2015 at 9:54
  • WinThumbsPreloader can do what is needed. I noticed that this tool works much more stable when ran from command line. It can go over folders recursively with -r flag, and generate windows thumbnails.
    – Sunny127
    Mar 2, 2019 at 0:56

4 Answers 4

5
+50

Here's a PowerShell script I wrote that should do what you are needing.

I took the logic from this thread: https://stackoverflow.com/questions/3555799/how-do-i-refresh-a-files-thumbnail-in-windows-explorer, and made it into a script that you can run as a scheduled task in windows.

You will need to have .net4.0 and PowerShell 3.0 installed to use it, otherwise you will have errors. At this point you probably have .net4.0, but you will likely need PowerShell 3.0

Save the following into a file named thumb_generate.ps1

param ([string]$path,[string]$ext)
function Refresh-Explorer 
{  
   $code = @'  
   [System.Runtime.InteropServices.DllImport("Shell32.dll")]   
   public static extern Int32 SHParseDisplayName([MarshalAs(UnmanagedType.LPWStr)] String pszName, IntPtr pbc, out IntPtr ppidl, UInt32 sfgaoIn, out UInt32 psfgaoOut);

   [System.Runtime.InteropServices.DllImport("Shell32.dll")]   
   private static extern int SHChangeNotify(int eventId, int flags, IntPtr item1, IntPtr item2);

   [System.Runtime.InteropServices.DllImport("Shell32.dll")]   
   private static extern int ILFree(IntPtr pidl);    

   public static void Refresh(string path)  {
    uint iAttribute;
    IntPtr pidl;
    SHParseDisplayName(path, IntPtr.Zero, out pidl, 0, out iAttribute);  
    SHChangeNotify(0x00002000, 0x1000, IntPtr.Zero, IntPtr.Zero); 
    ILFree(pidl); 
}  
'@  

    Add-Type -MemberDefinition $code -Namespace MyWinAPI -Name Explorer   
    [MyWinAPI.Explorer]::Refresh($path)  
} 

cls
if([System.String]::IsNullOrEmpty($path))
{
   Write-Host "Path cannot be empty."
   Write-Host "Example:  .\thumb_generate.ps1 -path ""C:\"" -ext ""jpg"""
   return
}
if([System.String]::IsNullOrEmpty($path))
{
   Write-Host "Extension cannot be empty."
   Write-Host "Example:  .\thumb_generate.ps1 -path ""C:\"" -ext ""jpg"""
   return
}

$fileExtension = "*." + $ext
Write-Host -ForegroundColor Green "---Thumbnail generation for Windows 7/8---"
Write-Host -ForegroundColor Green "----PowerShell 3.0 & .Net 4.0 required----"
Write-Host "Path: "  $path
Write-Host "Extension: " $fileExtension
Write-Host 
if (Test-Path $path) 
{
   Write-Host "Path Exists, begin generation thumbnails"

   $images = [System.IO.Directory]::EnumerateFiles($path,$fileExtension,"AllDirectories")
   Foreach($image in $images)
   {
       try
       {
          $file = New-Object System.IO.FileInfo($image)
          Write-Host $file.FullName
          $fStream = $file.Open([System.IO.FileMode]::Open,[System.IO.FileAccess]::Read,[System.IO.FileShare]::None)

          $firstbyte = New-Object Byte[] 1
          $result = $fStream.Read($firstbyte,0,1)
       }
       catch
       {
          Write-Host -ForegroundColor Red "An error occured on file: " + $file.FullName
       }
       $fStream.Close();
   }
    Refresh-Explorer
}
else
{
  "Path Doesn't Exist, Exiting..."
}

Then execute it from the PowerShell command line with the following parameters:

.\thumb_generate.ps1 -path "C:\PathToImages\"  -ext "jpg"

In reality, any file extension should work. It will recursively look down through all directories. The one drawback is only one file type at a time, but multiple jobs could be run that simply use a different file extension. Basically, the script opens each file and reads only the first byte, which is enough to force an update on the thumbs.db file.

Edit I altered the script to include the shell update portion posted above as well. It seems to be working on my system, though I don't have thousands of images to test against. This combines both the reading of the first few bytes of the file, followed by forcing a refresh of the thumbnails.

2
  • Thank you. I will give this a try. However, I need to run this from a cronjob in Windows so that I don't have to call the script manually. Is this compatible with Windows tasks?
    – dude
    Feb 28, 2015 at 16:50
  • Doesn't appear to work for mp4 files. Might be related to the particular codec being used though. Jul 27, 2020 at 20:36
2

I can think of two methods to speed-up thumbnail creation :

  1. Reduce the size of the thumbnail (more info)
  2. Write a program that periodically refreshes the thumbnails of new files

For the second method, I don't know of a product that does that, so you will need to write your own. Here is a useful reference : How do I refresh a file's thumbnail in Windows Explorer?.

The post suggests and includes the source of a C# program that accomplishes this by reading the first byte of the file. Windows refreshes the thumbnail when the program closes the file.

The accepted answer more simply just notifies Windows that the file has changed with a posted solution that doesn't need to read the file.

My version (untested) is :

public static void refreshThumbnail(string path)
{
    try
    {
        uint iAttribute;
        IntPtr pidl;
        SHParseDisplayName(path, IntPtr.Zero, out pidl, 0, out iAttribute);
        SHChangeNotify(
            (uint)ShellChangeNotificationEvents.SHCNE_UPDATEITEM,
            (uint)ShellChangeNotificationFlags.SHCNF_FLUSH,
            pidl,
            IntPtr.Zero);
        ILFree(pidl);

    }
    catch { }
}
6
  • Thanks for this great informations. How do I get your script/the linked script to run from a Windows console? Since there is no main()-function I am not sure if it would work.
    – dude
    Feb 27, 2015 at 9:48
  • This is also just the C function example.
    – Overmind
    Feb 27, 2015 at 9:54
  • Yes, this is only a code fragment to be used in a program that needs still to be written.
    – harrymc
    Feb 27, 2015 at 10:24
  • How does one check if the thumbnail was updated? Because I've tried to use the C# code from the example in the PowerShell and while I can call refreshThumbnail method, it doesn't seem to update thumbnails. I've used this tool to view thumbnail cache and it's not updated. F5 from Explorer works fine. Any ideas? Feb 27, 2015 at 15:00
  • An idea would be to read the first byte from the file, as in the article I linked-to, perhaps after and in addition to the SHChangeNotify call. Maybe this will convince Windows to do its work. If nothing works, then it's Explorer itself that updates thumbs.db, so the only solution would be to create a hidden instance of Explorer that periodically does F5 (autohotkey can do that).
    – harrymc
    Feb 27, 2015 at 15:30
1

Another program fragment that should refresh the thumbnail cache using the IThumbnailCache COM interface (C++). To my knowledge there isn't any precompiled tool for the task.

MSDN says: "If the flags parameter to IThumbnailCache::GetThumbnail includes the flag WTS_EXTRACT, and the thumbnail is not already cached, a thumbnail will be extracted and placed in the cache"

CoInitialize(NULL);

IThumbnailCache *cache;
HRESULT hr = CoCreateInstance(CLSID_LocalThumbnailCache, NULL, CLSCTX_INPROC, IID_PPV_ARGS(&cache));
if (SUCCEEDED(hr))
{
    <loop over files>
    {
        cache->GetThumbnail(<shell item for file>, <required size>, WTS_EXTRACT, NULL, NULL, NULL);
        cache->Release();
    }
}
-2

The problem is rather complex. The best thing to do is design an application specifically for this purpose.

Option 1:

Search for *.jpg, *.avi, etc and they should all be cached then.

Option 2a:

Create a batch script to manually create the thumbnails. Example (adapt to your needs):

mkdir thumbjunk
for f in *.jpg
do convert $f -thumbnail 100x80 thumbjunk/$f.gif

Recursively:

find * -prune -name '*.jpg' \
-exec convert '{}' -thumbnail 100x80 thumbjunk/'{}'.gif 

Option 2b:

This is for video files, ffmpeg is the tool.

Example:

ffmpeg -ss 00:01:00 -i testavi.avi -vf 'select=not(mod(n\,1000)),scale=320:240,tile=2x3' testavi.png

For option 2 you will have to respect the thumbs naming scheme in order for your generated output to be considered valid.

6
  • 2
    I think the OP wants the thumbnail cache updated, not generate separate thumbs. Also, you should mention that convert is an ImageMagick tool.
    – Vinayak
    Feb 27, 2015 at 9:41
  • Yes, forgot to mention, convert is an external tool. It has other alternatives too. I got used to it being integrated into my system.
    – Overmind
    Feb 27, 2015 at 9:44
  • Yes, I want to update the cache not recreate thumbnails in different folders.
    – dude
    Feb 27, 2015 at 9:45
  • Also, the script doesn't look like a batch script. How do you use $f in batch?
    – Vinayak
    Feb 27, 2015 at 9:45
  • 2
    @Overmind I mentioned that because this question is tagged windows so one would expect answers that work on Windows systems.
    – Vinayak
    Feb 27, 2015 at 10:01

Not the answer you're looking for? Browse other questions tagged .