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?

dude
  • 153

4 Answers4

5

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.

Lee Harrison
  • 2,127
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 { }
}
harrymc
  • 498,455
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.

Overmind
  • 10,308