Не удалось загрузить миниатюры гидратированных файлов в папке Cloud File API.C#

Место общения программистов C#
Anonymous
Не удалось загрузить миниатюры гидратированных файлов в папке Cloud File API.

Сообщение Anonymous »

Я работаю над приложением, которое работает как OneDrive и использует Cloud File API для отображения файлов, размещенных в облаке, в папке на компьютере пользователя. Я пытаюсь улучшить свое приложение, получая миниатюры из локально кэшированных файлов для отображения в моем пользовательском интерфейсе. Таким образом, локальные изменения файлов могут быть показаны пользователю до того, как они будут загружены обратно в облако.
Проводник Windows может отображать миниатюры всех гидратированных файлов в моей папке. Проблема в том, что мой код не всегда может получить одни и те же миниатюры.
Некоторые подробности:

Мое приложение написано на C#, поэтому я использую P/Invoke для взаимодействия с оболочкой и получения миниатюр. Вот пример кода, в котором для простоты удалена обработка ошибок:

Код: Выделить всё

[ComImportAttribute()]
[GuidAttribute("bcc18b79-ba16-442f-80c4-8a59c30c463b")]
[InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown)]
public interface IShellItemImageFactory
{
HResult GetImage(
[In, MarshalAs(UnmanagedType.Struct)] SIZE size,
[In] SIIGBF flags,
[Out] out IntPtr phbm);
}

[DllImport("shell32.dll", CharSet = CharSet.Unicode, PreserveSig = false)]
[return: MarshalAs(UnmanagedType.Interface, IidParameterIndex = 2)]
public static extern IShellItemImageFactory SHCreateItemFromParsingName(
[In][MarshalAs(UnmanagedType.LPWStr)] string pszPath,
[In] IntPtr pbc,
[In][MarshalAs(UnmanagedType.LPStruct)] Guid riid);

[DllImport("gdi32.dll", EntryPoint = "DeleteObject")]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool DeleteObject(
[In] IntPtr hObject);

[Flags]
public enum SIIGBF
{
SIIGBF_RESIZETOFIT = 00,
SIIGBF_BIGGERSIZEOK = 01,
SIIGBF_MEMORYONLY = 02,
SIIGBF_ICONONLY = 04,
SIIGBF_THUMBNAILONLY = 08,
SIIGBF_INCACHEONLY = 10
}

public Bitmap GetThumbnail(string parsingName)
{
// obtain the IShellItemImageFactory interface
IShellItemImageFactory isiif = null;
Guid iid = typeof(IShellItemImageFactory).GUID;
isiif = SHCreateItemFromParsingName(parsingName, IntPtr.Zero, iid);
if (isiif == null) return null;

// extract memory bitmap
SIIGBF flags = SIIGBF.SIIGBF_BIGGERSIZEOK | SIIGBF.SIIGBF_THUMBNAILONLY;
IntPtr hBitmap = IntPtr.Zero;
HResult result = isiif.GetImage(new SIZE(256, 256), flags, out hBitmap);
Marshal.FinalReleaseComObject(isiif);
if (hBitmap == IntPtr.Zero) return null;

Bitmap bmp = Image.FromHbitmap(hBitmap);
DeleteObject(hBitmap);
return bmp;
}
Этот код вернет мне растровое изображение миниатюры. Этот код работает в 100% случаев за пределами моего корня синхронизации CFAPI в обычных папках.
Внутри корня синхронизации CFAPI он не всегда работает. Я пока не смог найти закономерности, когда это работает, а когда нет. Я наблюдаю такое же непоследовательное поведение при попытке использовать разные типы файлов (расширения), поэтому кажется, что оно не ограничено конкретными типами. Когда это не работает, я получаю HRESULT от вызова GetImage 0x8004B207, который, очевидно, преобразуется в WTS_E_NOSTORAGEPROVIDERTHUMBNAILHANDLER. Я бы не ожидал получить эту ошибку (или любую другую ошибку), поскольку миниатюры файлов уже отображаются в Проводнике.
Еще одна вещь, которую я пробовал, — это обойти командную консоль и создать миниатюры самостоятельно. Это выглядит примерно так:
  • Возьмите расширение файла, найдите его в HKEY_CLASSES_ROOT, найдите IThumbnailProvider CLSID
  • Найдите реализацию IThumbnailProvider по CLSID
  • Создать экземпляр IThumbnailProvider, передавая либо поток, либо файл в зависимости от того, что он поддерживает.
  • Вызов GetThumbnail для результирующего экземпляра IThumbnailProvider
Создавать такие миниатюры самостоятельно кажется немного схематичным. Оболочка также не кэширует результаты этого вызова. Если я создам такую ​​миниатюру, а затем попробую другой код, указанный выше, я не получу только что созданную миниатюру обратно из командной оболочки. Я хотел бы, чтобы оболочка кэшировала миниатюры, чтобы они создавались только один раз в зависимости от того, какой процесс попытается просмотреть файл первым.
Итак, вкратце у меня есть следующие вопросы:
  • Как мне программно получить миниатюру, которую Explorer уже показывает мне в графическом интерфейсе, чтобы я мог показать ее в своем собственном пользовательском интерфейсе?
  • Безопасно ли создавать мои миниатюры? собственные миниатюры непосредственно с помощью IThumbnailProvider типа файла?
  • Как заставить командную консоль кэшировать сгенерированные миниатюры, чтобы генерация происходила только один раз?

Вернуться в «C#»