How to automatically create Windows thumbnails recursively? [duplicate]
Solution 1:
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.
Solution 2:
I can think of two methods to speed-up thumbnail creation :
- Reduce the size of the thumbnail (more info)
- 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 { }
}
Solution 3:
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();
}
}