C # отримати ескіз із файлу через Windows api


75

Провідник Windows має можливість показу ескізів файлів. Ці мініатюри надаються основними та сторонніми розширеннями оболонки.

Я знаю, як розширити оболонку, щоб забезпечити мініатюри Windows.

Що я хочу зробити, це отримати мініатюру із будь-якого файлу в системі через оболонку за допомогою C #. Чи можливо це?

По суті, я пишу користувальницький браузер файлів, і хочу показати ескізи, і не можу проаналізувати кожен файл на планеті, щоб зробити власні ескізи.

Пояснення: Багато відповідей, здається, зосереджені навколо ескізів веб-сторінки або масштабування зображення. Але це зовсім не те, що я шукаю. Я хочу попросити Windows про ескіз цих типів файлів: .DOC, .PDF, .3DM, .DWG ... і ще близько десятка. Я не хочу самостійно аналізувати, обробляти та робити ескізи, оскільки Windows вже знає, як це робити.

Код, який я опублікував як відповідь, насправді працює ... можливо, його можна спростити і трохи очистити.


Будь ласка, поясніть, що вам потрібна мініатюра файлу, а не піктограма типу файлу (розширення). Що ви не хочете Як використовувати функцію SHGetFileInfo, щоб отримати піктограми, пов'язані з файлами .
AMissico

Уточнення на основі прийнятої відповіді - Брайан Гіллеспі хоче зображення мініатюр оболонки, а не зображення значка оболонки.
AMissico

Відповіді:


83

Натрапив на це сьогодні - йому кілька місяців, але мені це вдалося зробити (у Win7, витягуючи ескізи у файлах MPEG-4):

Код:

ShellFile shellFile = ShellFile.FromFilePath(pathToYourFile);
Bitmap shellThumb = shellFile.Thumbnail.ExtraLargeBitmap;

Сподіваюся, це допоможе!


2
Awesom, спасибі купи ... дивився на код вище думаючи боже, чи справді це повинно бути так важко. Працював ласощі! +1
Легка вага

1
@ Легка вага: Так, це повинно бути настільки важко, тому що на плакаті зазначено, що вони не хочуть використовувати сторонні розширення, і вони спеціально хочуть використовувати Shell.
AMissico

1
Використання Microsoft Code Code Pack є набагато простішим рішенням. Зміна прийнятого рішення.
Брайан Гіллеспі

Приємні речі. Це полегшило мені життя.
Елан Хассон,

4
Чи можна це зробити за допомогою потоку замість відображеного контуру?
Сем Джонс,

19

Ескізи Microsoft Office у SharePoint за адресою http://msdn.microsoft.com/en-us/library/aa289172(VS.71).aspx . це саме те, що ви хочете. (У мене не виникло проблем з перетворенням коду VB.NET на C # за допомогою http://www.developerfusion.com/tools/convert/vb-to-csharp/ .)

Щодо коду, який ви опублікували у своїй відповіді, Витяг ескізів за допомогою оболонки за адресою http://www.vbaccelerator.com/home/net/code/libraries/Shell_Projects/Thumbnail_Extraction/article.asp - це оригінальна стаття, з якої походить код. Я вважаю, що майже всі IExtractImageзразки (ви знайдете пошук) базуються на коді цієї статті, завдяки правилам іменування, коментарям тощо, які перенесені з оригіналу. Оскільки ця стаття датується квітнем 2003 року, вона містить деякі нестандартні (не- .NET) правила кодування. Я провів базове тестування, і є проблеми зі збиранням сміття, звільнення некерованих ресурсів та інші проблеми з очищенням. Тому я настійно рекомендую уникати коду в цій статті. Більше того, структурований код - це такий спосіб ускладнити обслуговування.

Існує чиста спрощена версія MSDN від липня 2005 року, яка називається Мініатюри Microsoft Office у SharePoint за адресою http://msdn.microsoft.com/en-us/library/aa289172(VS.71).aspx . Цей код та код статті мають спільні риси, що змушує мене вважати, що витяг ескізів із використанням оболонки є основою статті SharePoint . Версія VB.NET GetThumbnailImageігнорує longestEdgeпараметр, але версія C ++ використовує його і документує використання ORIGSIZEі QUALITYпрапорів. Крім того, код ілюструє, як використовувати .NET FreeCoTaskMemзамість оболонок IMallocта SHGetMalloc.

IExtractImageпрацює з файлами, папками та іншими об’єктами простору імен. Код MSDN працює із прихованими файлами, тоді як код vbAccelerator потрібно потім SHCONTF_INCLUDEHIDDENдодати до EnumObjectsвиклику. Крім того, vbAccelerator перераховує об'єкти папки оболонки, шукаючи вказаний файл, що видається марною тратою часу. Можливо, це було потрібно для пошуку правильної "відносної" PIDL, яка використовується для GetUIObjectOfдзвінка.


Мініатюра оболонки (незавершене виконання)

Повний зразок проекту за адресою http://cid-7178d2c79ba0a7e3.office.live.com/self.aspx/.Public/ShellThumbnail.zip .

Imports System.Runtime.InteropServices

Namespace Shell

    ''' <summary>
    ''' Generates a thumbnail of a folder's picture or a file's image.
    ''' </summary>
    ''' <remarks>This is the "Folder's Picture" and not the "Folder's Icon"! Use SHGetFileInfo to generate the thumbnail for a folder's icon or for a file that does not have a thumbnail handler.</remarks>
    ''' <reference>Microsoft Office Thumbnails in SharePoint at http://msdn.microsoft.com/en-us/library/aa289172%28VS.71%29.aspx.</reference>
    Public Class ShellThumbnail

        'TODO - Work out the details for image size and IEIFLAG handling.

#Region " Determining Thumbnail Size and Quality [documentation] "

        'http://support.microsoft.com/kb/835823
        'Determining Thumbnail Size and Quality
        'Browse to HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Explorer. Create or modify two DWORDs called ThumbnailSize and ThumbnailQuality. For ThumbnailSize set the value in pixels, with the default being 96. For ThumbnailQuality set the value as a number that represents the percentage quality between 50 and 100.


        'http://www.pctools.com/guides/registry/detail/1066/ (modified)
        '  User Key: [HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Explorer]
        'System Key: [HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\Explorer]
        'Value Name: ThumbnailSize, ThumbnailQuality
        ' Data Type: REG_DWORD (DWORD Value)
        'Value Data: Size in pixels (32-255), Quality Percentage (50-100)


        'Microsoft® Windows® XP Registry Guide 
        'Jerry Honeycutt 
        '09/11/2002 
        'Microsoft Press
        'http://www.microsoft.com/mspress/books/sampchap/6232.aspx#118
        '<H3><I><A name=118></A>Thumbnails</I></H3>The Thumbnails category controls the 
        'quality of thumbnails in Windows Explorer. Table 5-10 describes the values for 
        'Image Quality and Size. Create values that you don't see in the registry. The 
        'default value for <CODE>ThumbnailQuality</CODE> is <CODE>0x5A</CODE>. The 
        'default value for <CODE>ThumbnailSize</CODE> is <CODE>0x60</CODE>. Keep in mind 
        'that higher quality and larger thumbnails require more disk space, which is not 
        'usually a problem, but they also take longer to display. Changing the quality 
        'does not affect thumbnails that already exist on the file system.
        '<P><B>Table 5-10 </B><I>Values in Thumbnails</I>
        '<P>
        '<TABLE border=0 cellSpacing=1 cellPadding=4 width="100%">
        '<TBODY>
        '<TR>
        '<TD bgColor=#999999 vAlign=top><B>Setting</B></TD>
        '<TD bgColor=#999999 vAlign=top><B>Name</B></TD>
        '<TD bgColor=#999999 vAlign=top><B>Type</B></TD>
        '<TD bgColor=#999999 vAlign=top><B>Data</B></TD></TR>
        '<TR>
        '<TD bgColor=#cccccc 
        'vAlign=top><CODE><B>HKCU\Software\Microsoft\Windows\CurrentVersion\Explorer</B></CODE></TD>
        '<TD bgColor=#cccccc vAlign=top> </TD>
        '<TD bgColor=#cccccc vAlign=top> </TD>
        '<TD bgColor=#cccccc vAlign=top> </TD></TR>
        '<TR>
        '<TD bgColor=#cccccc vAlign=top><CODE>Image Quality</CODE></TD>
        '<TD bgColor=#cccccc vAlign=top><CODE>ThumbnailQuality</CODE></TD>
        '<TD bgColor=#cccccc vAlign=top><CODE>REG_DWORD</CODE></TD>
        '<TD bgColor=#cccccc vAlign=top><CODE>0x32 - 0x64</CODE></TD></TR>
        '<TR>
        '<TD bgColor=#cccccc vAlign=top><CODE>Size (pixels)</CODE></TD>
        '<TD bgColor=#cccccc vAlign=top><CODE>ThumbnailSize</CODE></TD>
        '<TD bgColor=#cccccc vAlign=top><CODE>REG_DWORD</CODE></TD>
        '<TD bgColor=#cccccc vAlign=top><CODE>0x20 - 0xFF</CODE></TD></TR></TBODY></TABLE></P>

#End Region

        Public Shared ReadOnly DefaultThumbnailSize As New System.Drawing.Size(96, 96)

        Public Const DefaultColorDepth As Integer = 32

        ''' <summary>
        ''' Used to request an image from an object, such as an item in a Shell folder.
        ''' </summary>
        ''' <param name="path">An absolute path to a file or folder.</param>
        Public Shared Function ExtractImage(ByVal path As String) As Bitmap
            Return ExtractImage(path, System.Drawing.Size.Empty, DefaultColorDepth, 0)
        End Function

        ''' <summary>
        ''' Used to request an image from an object, such as an item in a Shell folder.
        ''' </summary>
        ''' <param name="path">An absolute path to a file or folder.</param>
        ''' <param name="size"></param>
        Public Shared Function ExtractImage(ByVal path As String, ByVal size As System.Drawing.Size) As Bitmap
            Return ExtractImage(path, size, DefaultColorDepth, 0)
        End Function

        ''' <summary>
        ''' Used to request an image from an object, such as an item in a Shell folder.
        ''' </summary>
        ''' <param name="path">An absolute path to a file or folder.</param>
        ''' <param name="size"></param>
        ''' <param name="recommendedColorDepth">The recommended color depth in units of bits per pixel. The default is 32.</param>
        Public Shared Function ExtractImage(ByVal path As String, ByVal size As System.Drawing.Size, ByVal recommendedColorDepth As Integer) As Bitmap
            Return ExtractImage(path, size, recommendedColorDepth, 0)
        End Function

        ''' <summary>
        ''' Used to request an image from an object, such as an item in a Shell folder.
        ''' </summary>
        ''' <param name="path">An absolute path to a file or folder.</param>
        ''' <param name="size"></param>
        ''' <param name="recommendedColorDepth">The recommended color depth in units of bits per pixel. The default is 32.</param>
        Private Shared Function ExtractImage(ByVal path As String, ByVal size As System.Drawing.Size, ByVal recommendedColorDepth As Integer, ByVal flags As IEIFLAG) As Bitmap
            Dim oResult As Bitmap = Nothing

            Dim oDesktopFolder As IShellFolder = Nothing
            Dim hParentIDL As IntPtr
            Dim hIDL As IntPtr

            Dim oParentFolder As IShellFolder
            Dim hParentFolder As IntPtr
            Dim oExtractImage As IExtractImage
            Dim hExtractImage As IntPtr

            'Divide the file name into a path and file/folder name.
            Dim sFolderName As String = System.IO.Path.GetDirectoryName(path)
            Dim sBaseName As String = System.IO.Path.GetFileName(path)

            'Get the desktop IShellFolder.
            If SHGetDesktopFolder(oDesktopFolder) <> Missico.Win32.S_OK Then
                Throw New System.Runtime.InteropServices.COMException
            End If

            'Get the parent folder for the specified path.
            oDesktopFolder.ParseDisplayName(IntPtr.Zero, IntPtr.Zero, sFolderName, 0, hParentIDL, 0)
            oDesktopFolder.BindToObject(hParentIDL, IntPtr.Zero, ShellGUIDs.IID_IShellFolder, hParentFolder)
            oParentFolder = CType(Marshal.GetTypedObjectForIUnknown(hParentFolder, GetType(IShellFolder)), IShellFolder)

            'Get the file/folder's IExtractImage
            oParentFolder.ParseDisplayName(IntPtr.Zero, IntPtr.Zero, sBaseName, 0, hIDL, 0)
            oParentFolder.GetUIObjectOf(IntPtr.Zero, 1, New IntPtr() {hIDL}, ShellGUIDs.IID_IExtractImage, IntPtr.Zero, hExtractImage)

            'Free the pidls. The Runtime Callable Wrappers (RCW) should automatically release the COM objects.
            Marshal.FreeCoTaskMem(hParentIDL)
            Marshal.FreeCoTaskMem(hIDL)

            Marshal.FinalReleaseComObject(oParentFolder)
            Marshal.FinalReleaseComObject(oDesktopFolder)

            If hExtractImage = IntPtr.Zero Then

                'There is no handler for this file, which is odd. I believe we should default the file's type icon.
                Debug.WriteLine(String.Format("There is no thumbnail for the specified file '{0}'.", path), "ShellThumbnail.ExtractImage")

            Else

                oExtractImage = CType(Marshal.GetTypedObjectForIUnknown(hExtractImage, GetType(IExtractImage)), IExtractImage)

                'Set the size and flags
                Dim oSize As Missico.Win32.SIZE 'must specify a size
                Dim iFlags As IEIFLAG = flags Or IEIFLAG.IEIFLAG_ORIGSIZE Or IEIFLAG.IEIFLAG_QUALITY Or IEIFLAG.IEIFLAG_ASPECT

                If size.IsEmpty Then

                    oSize.cx = DefaultThumbnailSize.Width
                    oSize.cy = DefaultThumbnailSize.Height

                Else

                    oSize.cx = size.Width
                    oSize.cy = size.Height

                End If


                Dim hBitmap As IntPtr
                Dim sPath As New System.Text.StringBuilder(Missico.Win32.MAX_PATH, Missico.Win32.MAX_PATH)


                oExtractImage.GetLocation(sPath, sPath.Capacity, 0, oSize, recommendedColorDepth, iFlags)

                'if the specified path is to a folder then IExtractImage.Extract fails.

                Try

                    oExtractImage.Extract(hBitmap)

                Catch ex As System.Runtime.InteropServices.COMException

                    'clear the handle since extract failed
                    hBitmap = IntPtr.Zero

                    Debug.WriteLine(String.Format("There is no thumbnail for the specified folder '{0}'.", path), "ShellThumbnail.ExtractImage")

                Finally

                    Marshal.FinalReleaseComObject(oExtractImage)

                End Try

                If Not hBitmap.Equals(IntPtr.Zero) Then

                    'create the image from the handle
                    oResult = System.Drawing.Bitmap.FromHbitmap(hBitmap)

                    'dump the properties to determine what kind of bitmap is returned
                    'Missico.Diagnostics.ObjectDumper.Write(oResult)

                    'Tag={ }
                    'PhysicalDimension={Width=96, Height=96}
                    'Size={Width=96, Height=96}
                    'Width=96
                    'Height=96
                    'HorizontalResolution=96
                    'VerticalResolution=96
                    'Flags=335888
                    'RawFormat={ }
                    'PixelFormat=Format32bppRgb
                    'Palette={ }
                    'FrameDimensionsList=...
                    'PropertyIdList=...
                    'PropertyItems=...

                    Missico.Win32.DeleteObject(hBitmap) 'release the handle

                End If

            End If

            Return oResult
        End Function

    End Class

End Namespace

AMissico - дякую за це. Я не тестував ваш код, але ваші міркування здаються обґрунтованими, тому я прийму вашу відповідь як правильну, а не ту, яку я знайшов у EE.
Брайан Гіллеспі

Дякуємо за зразок проекту, AMissico. Вам слід розглянути можливість розміщення його на CodePlex або GoogleCode, щоб інші могли перевірити його та подати вдосконалення.
dthrasher

1
Крім того, я протестував ваш проект щодо іншого коду на vbaccelerator. Ваш виконує трохи швидше. (
Вірю

Підвищення продуктивності, ймовірно, пов'язане з відсутністю переліку кодів над об'єктами папки оболонки. Код по суті однаковий.
AMissico

2
@dthrasher, днями я помітив із журналів ftp, що проект завантажується кілька разів на тиждень. Після того, як я закінчую проект з домашніми тваринами, я планую створити тестовий проект і опублікувати код у CodeProject або CodePlex.
AMissico


3

Ось клас, який я знайшов в Інтернеті. Схоже, оригінальний код надійшов з http://www.experts-exchange.com/Programming/Languages/C_Sharp/Q_21789724.html , але я не бачу його для належної атрибуції. Я знайшов джерело тут: http://www.vbforums.com/showthread.php?t=527704

Ось клас із відповідними викликами COM, відтворений тут для нащадків:


using System;
using System.Diagnostics;
using System.Drawing;
using System.IO;
using System.Runtime.InteropServices;
using System.Text;

namespace RMA.Shell
{
  public class ShellThumbnail : IDisposable
  {

    [Flags]
    private enum ESTRRET
    {
      STRRET_WSTR = 0,
      STRRET_OFFSET = 1,
      STRRET_CSTR = 2
    }

    [Flags]
    private enum ESHCONTF
    {
      SHCONTF_FOLDERS = 32,
      SHCONTF_NONFOLDERS = 64,
      SHCONTF_INCLUDEHIDDEN = 128,
    }

    [Flags]
    private enum ESHGDN
    {
      SHGDN_NORMAL = 0,
      SHGDN_INFOLDER = 1,
      SHGDN_FORADDRESSBAR = 16384,
      SHGDN_FORPARSING = 32768
    }

    [Flags]
    private enum ESFGAO
    {
      SFGAO_CANCOPY = 1,
      SFGAO_CANMOVE = 2,
      SFGAO_CANLINK = 4,
      SFGAO_CANRENAME = 16,
      SFGAO_CANDELETE = 32,
      SFGAO_HASPROPSHEET = 64,
      SFGAO_DROPTARGET = 256,
      SFGAO_CAPABILITYMASK = 375,
      SFGAO_LINK = 65536,
      SFGAO_SHARE = 131072,
      SFGAO_READONLY = 262144,
      SFGAO_GHOSTED = 524288,
      SFGAO_DISPLAYATTRMASK = 983040,
      SFGAO_FILESYSANCESTOR = 268435456,
      SFGAO_FOLDER = 536870912,
      SFGAO_FILESYSTEM = 1073741824,
      SFGAO_HASSUBFOLDER = -2147483648,
      SFGAO_CONTENTSMASK = -2147483648,
      SFGAO_VALIDATE = 16777216,
      SFGAO_REMOVABLE = 33554432,
      SFGAO_COMPRESSED = 67108864,
    }

    private enum EIEIFLAG
    {
      IEIFLAG_ASYNC = 1,
      IEIFLAG_CACHE = 2,
      IEIFLAG_ASPECT = 4,
      IEIFLAG_OFFLINE = 8,
      IEIFLAG_GLEAM = 16,
      IEIFLAG_SCREEN = 32,
      IEIFLAG_ORIGSIZE = 64,
      IEIFLAG_NOSTAMP = 128,
      IEIFLAG_NOBORDER = 256,
      IEIFLAG_QUALITY = 512
    }

    [StructLayout(LayoutKind.Sequential, Pack = 4, Size = 0, CharSet = CharSet.Auto)]
    private struct STRRET_CSTR
    {
      public ESTRRET uType;
      [MarshalAs(UnmanagedType.ByValArray, SizeConst = 520)]
      public byte[] cStr;
    }

    [StructLayout(LayoutKind.Explicit, CharSet = CharSet.Auto)]
    private struct STRRET_ANY
    {
      [FieldOffset(0)]
      public ESTRRET uType;
      [FieldOffset(4)]
      public IntPtr pOLEString;
    }
    [StructLayoutAttribute(LayoutKind.Sequential)]
    private struct SIZE
    {
      public int cx;
      public int cy;
    }

    [ComImport(), Guid("00000000-0000-0000-C000-000000000046")]
    [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
    private interface IUnknown
    {

      [PreserveSig()]
      IntPtr QueryInterface(ref Guid riid, ref IntPtr pVoid);

      [PreserveSig()]
      IntPtr AddRef();

      [PreserveSig()]
      IntPtr Release();
    }

    [ComImportAttribute()]
    [GuidAttribute("00000002-0000-0000-C000-000000000046")]
    [InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown)]
    private interface IMalloc
    {

      [PreserveSig()]
      IntPtr Alloc(int cb);

      [PreserveSig()]
      IntPtr Realloc(IntPtr pv, int cb);

      [PreserveSig()]
      void Free(IntPtr pv);

      [PreserveSig()]
      int GetSize(IntPtr pv);

      [PreserveSig()]
      int DidAlloc(IntPtr pv);

      [PreserveSig()]
      void HeapMinimize();
    }

    [ComImportAttribute()]
    [GuidAttribute("000214F2-0000-0000-C000-000000000046")]
    [InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown)]
    private interface IEnumIDList
    {

      [PreserveSig()]
      int Next(int celt, ref IntPtr rgelt, ref int pceltFetched);

      void Skip(int celt);

      void Reset();

      void Clone(ref IEnumIDList ppenum);
    }

    [ComImportAttribute()]
    [GuidAttribute("000214E6-0000-0000-C000-000000000046")]
    [InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown)]
    private interface IShellFolder
    {

      void ParseDisplayName(IntPtr hwndOwner, IntPtr pbcReserved,
        [MarshalAs(UnmanagedType.LPWStr)]string lpszDisplayName,
        ref int pchEaten, ref IntPtr ppidl, ref int pdwAttributes);

      void EnumObjects(IntPtr hwndOwner,
        [MarshalAs(UnmanagedType.U4)]ESHCONTF grfFlags,
        ref IEnumIDList ppenumIDList);

      void BindToObject(IntPtr pidl, IntPtr pbcReserved, ref Guid riid,
        ref IShellFolder ppvOut);

      void BindToStorage(IntPtr pidl, IntPtr pbcReserved, ref Guid riid, IntPtr ppvObj);

      [PreserveSig()]
      int CompareIDs(IntPtr lParam, IntPtr pidl1, IntPtr pidl2);

      void CreateViewObject(IntPtr hwndOwner, ref Guid riid,
        IntPtr ppvOut);

      void GetAttributesOf(int cidl, IntPtr apidl,
        [MarshalAs(UnmanagedType.U4)]ref ESFGAO rgfInOut);

      void GetUIObjectOf(IntPtr hwndOwner, int cidl, ref IntPtr apidl, ref Guid riid, ref int prgfInOut, ref IUnknown ppvOut);

      void GetDisplayNameOf(IntPtr pidl,
        [MarshalAs(UnmanagedType.U4)]ESHGDN uFlags,
        ref STRRET_CSTR lpName);

      void SetNameOf(IntPtr hwndOwner, IntPtr pidl,
        [MarshalAs(UnmanagedType.LPWStr)]string lpszName,
        [MarshalAs(UnmanagedType.U4)] ESHCONTF uFlags,
        ref IntPtr ppidlOut);
    }
    [ComImportAttribute(), GuidAttribute("BB2E617C-0920-11d1-9A0B-00C04FC2D6C1"), InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown)]
    private interface IExtractImage
    {
      void GetLocation([Out(), MarshalAs(UnmanagedType.LPWStr)]
        StringBuilder pszPathBuffer, int cch, ref int pdwPriority, ref SIZE prgSize, int dwRecClrDepth, ref int pdwFlags);

      void Extract(ref IntPtr phBmpThumbnail);
    }

    private class UnmanagedMethods
    {

      [DllImport("shell32", CharSet = CharSet.Auto)]
      internal extern static int SHGetMalloc(ref IMalloc ppMalloc);

      [DllImport("shell32", CharSet = CharSet.Auto)]
      internal extern static int SHGetDesktopFolder(ref IShellFolder ppshf);

      [DllImport("shell32", CharSet = CharSet.Auto)]
      internal extern static int SHGetPathFromIDList(IntPtr pidl, StringBuilder pszPath);

      [DllImport("gdi32", CharSet = CharSet.Auto)]
      internal extern static int DeleteObject(IntPtr hObject);

    }

    ~ShellThumbnail()
    {
      Dispose();
    }

    private IMalloc alloc = null;
    private bool disposed = false;
    private Size _desiredSize = new Size(100, 100);
    private Bitmap _thumbNail;

    public Bitmap ThumbNail
    {
      get
      {
        return _thumbNail;
      }
    }

    public Size DesiredSize
    {
      get { return _desiredSize; }
      set { _desiredSize = value; }
    }
    private IMalloc Allocator
    {
      get
      {
        if (!disposed)
        {
          if (alloc == null)
          {
            UnmanagedMethods.SHGetMalloc(ref alloc);
          }
        }
        else
        {
          Debug.Assert(false, "Object has been disposed.");
        }
        return alloc;
      }
    }

    public Bitmap GetThumbnail(string fileName)
    {
      if (string.IsNullOrEmpty(fileName))
        return null;

      if (!File.Exists(fileName) && !Directory.Exists(fileName))
      {
        throw new FileNotFoundException(string.Format("The file '{0}' does not exist", fileName), fileName);
      }
      if (_thumbNail != null)
      {
        _thumbNail.Dispose();
        _thumbNail = null;
      }
      IShellFolder folder = null;
      try
      {
        folder = getDesktopFolder;
      }
      catch (Exception ex)
      {
        throw ex;
      }
      if (folder != null)
      {
        IntPtr pidlMain = IntPtr.Zero;
        try
        {
          int cParsed = 0;
          int pdwAttrib = 0;
          string filePath = Path.GetDirectoryName(fileName);
          folder.ParseDisplayName(IntPtr.Zero, IntPtr.Zero, filePath, ref cParsed, ref pidlMain, ref pdwAttrib);
        }
        catch (Exception ex)
        {
          Marshal.ReleaseComObject(folder);
          throw ex;
        }
        if (pidlMain != IntPtr.Zero)
        {
          Guid iidShellFolder = new Guid("000214E6-0000-0000-C000-000000000046");
          IShellFolder item = null;
          try
          {
            folder.BindToObject(pidlMain, IntPtr.Zero, ref iidShellFolder, ref item);
          }
          catch (Exception ex)
          {
            Marshal.ReleaseComObject(folder);
            Allocator.Free(pidlMain);
            throw ex;
          }
          if (item != null)
          {
            IEnumIDList idEnum = null;
            try
            {
              item.EnumObjects(IntPtr.Zero, (ESHCONTF.SHCONTF_FOLDERS | ESHCONTF.SHCONTF_NONFOLDERS), ref idEnum);
            }
            catch (Exception ex)
            {
              Marshal.ReleaseComObject(folder);
              Allocator.Free(pidlMain);
              throw ex;
            }
            if (idEnum != null)
            {
              int hRes = 0;
              IntPtr pidl = IntPtr.Zero;
              int fetched = 0;
              bool complete = false;
              while (!complete)
              {
                hRes = idEnum.Next(1, ref pidl, ref fetched);
                if (hRes != 0)
                {
                  pidl = IntPtr.Zero;
                  complete = true;
                }
                else
                {
                  if (_getThumbNail(fileName, pidl, item))
                  {
                    complete = true;
                  }
                }
                if (pidl != IntPtr.Zero)
                {
                  Allocator.Free(pidl);
                }
              }
              Marshal.ReleaseComObject(idEnum);
            }
            Marshal.ReleaseComObject(item);
          }
          Allocator.Free(pidlMain);
        }
        Marshal.ReleaseComObject(folder);
      }
      return ThumbNail;
    }

    private bool _getThumbNail(string file, IntPtr pidl, IShellFolder item)
    {
      IntPtr hBmp = IntPtr.Zero;
      IExtractImage extractImage = null;
      try
      {
        string pidlPath = PathFromPidl(pidl);
        if (Path.GetFileName(pidlPath).ToUpper().Equals(Path.GetFileName(file).ToUpper()))
        {
          IUnknown iunk = null;
          int prgf = 0;
          Guid iidExtractImage = new Guid("BB2E617C-0920-11d1-9A0B-00C04FC2D6C1");
          item.GetUIObjectOf(IntPtr.Zero, 1, ref pidl, ref iidExtractImage, ref prgf, ref iunk);
          extractImage = (IExtractImage)iunk;
          if (extractImage != null)
          {
            Console.WriteLine("Got an IExtractImage object!");
            SIZE sz = new SIZE();
            sz.cx = DesiredSize.Width;
            sz.cy = DesiredSize.Height;
            StringBuilder location = new StringBuilder(260, 260);
            int priority = 0;
            int requestedColourDepth = 32;
            EIEIFLAG flags = EIEIFLAG.IEIFLAG_ASPECT | EIEIFLAG.IEIFLAG_SCREEN;
            int uFlags = (int)flags;
            try
            {
              extractImage.GetLocation(location, location.Capacity, ref priority, ref sz, requestedColourDepth, ref uFlags);
              extractImage.Extract(ref hBmp);
            }
            catch (System.Runtime.InteropServices.COMException ex)
            {

            }
            if (hBmp != IntPtr.Zero)
            {
              _thumbNail = Bitmap.FromHbitmap(hBmp);
            }
            Marshal.ReleaseComObject(extractImage);
            extractImage = null;
          }
          return true;
        }
        else
        {
          return false;
        }
      }
      catch (Exception ex)
      {
        if (hBmp != IntPtr.Zero)
        {
          UnmanagedMethods.DeleteObject(hBmp);
        }
        if (extractImage != null)
        {
          Marshal.ReleaseComObject(extractImage);
        }
        throw ex;
      }
    }

    private string PathFromPidl(IntPtr pidl)
    {
      StringBuilder path = new StringBuilder(260, 260);
      int result = UnmanagedMethods.SHGetPathFromIDList(pidl, path);
      if (result == 0)
      {
        return string.Empty;
      }
      else
      {
        return path.ToString();
      }
    }

    private IShellFolder getDesktopFolder
    {
      get
      {
        IShellFolder ppshf = null;
        int r = UnmanagedMethods.SHGetDesktopFolder(ref ppshf);
        return ppshf;
      }
    }

    public void Dispose()
    {
      if (!disposed)
      {
        if (alloc != null)
        {
          Marshal.ReleaseComObject(alloc);
        }
        alloc = null;
        if (_thumbNail != null)
        {
          _thumbNail.Dispose();
        }
        disposed = true;
      }
    }
  }
}

3
щоб переглянути сторінку EE, скопіюйте URL-адресу, перейдіть до google, знайдіть її, клацніть результат, прокрутіть до кінця і знайдіть відповіді. Їм слід заборонити google ....
corymathews

Якщо у вас встановлена ​​панель інструментів Google, клацніть правою кнопкою миші на сторінці у відповідному домені та виберіть Інформація про сторінку -> Кешований знімок сторінки, щоб побачити, чим вони обслуговували Google, коли він там просканував.
Matthew Lock

1
Це ДУЖЕ складний спосіб використання GDI + в C #, оскільки обгортка C # для GDI + входить в рамки DotNet.
Eran Betzalel

Я вважаю, що вилучення мініатюр за допомогою оболонки за адресою vbaccelerator.com/home/net/code/libraries/Shell_Projects/ ... - це оригінальна стаття, з якої походить код. Я думав, що код виглядає знайомим із незвичного способу іменування Enum, такого як EIEIFLAG. Датується квітнем 2003 р.
AMissico

Ось чистая спрощена версія від липня 2005 року. Ескізи Microsoft Office у SharePoint за адресою msdn.microsoft.com/en-us/library/aa289172(VS.71).aspx . Цей код та код статті мають подібність. (Схоже, витяг ескізів із використанням оболонки є основою статті SharePoint.)
AMissico


0

Не зовсім те, що ви просили, але ось проект відкриття файлу Thumbs.db .
У статті Вікіпедії є додаткова інформація про те, де знайти мініатюри в Vista & 7.

Ось код для отримання зображення:

public byte[] GetThumbData(string filename)
{
    IStorageWrapper wrapper = new IStorageWrapper(_thumbDBFile, false);
    foreach(CatalogItem catItem in _catalogItems)
    {
        if (catItem.filename == filename)
        {
            string streamName = BuildReverseString(catItem.itemID);
            FileObject fileObject = wrapper.OpenUCOMStream(null, streamName);
            byte[] rawJPGData = new byte [fileObject.Length];
            fileObject.Read(rawJPGData, 0, (int)fileObject.Length);
            fileObject.Close();

            // 3 ints of header data need to be removed
            // Don't know what first int is.
            // 2nd int is thumb index
            // 3rd is size of thumbnail data.
            byte[] jpgData = new byte[rawJPGData.Length - 12];
            for (int index = 12; index < jpgData.Length; index++)
            {
                jpgData[index - 12] = rawJPGData[index];
            }
            return jpgData;
        }
    }
    return null;
}

public Image GetThumbnailImage(string filename)
{
    byte[] thumbData = GetThumbData(filename);
    if (null == thumbData)
    {
        return null;
    }
    MemoryStream ms = new MemoryStream(thumbData);
    Image img = Image.FromStream(ms);
    return img;
}

Посилання порушено, але хтось випадково знає шифрування pw у кеш-пам'яті великих пальців .db?
ThomasRones,

Виправлено посилання. Здається, немає пароля; Я додав відповідний код до своєї відповіді.
півкуля

0

Завдяки @Christian Nunciato, я хочу пояснити більше:

Після встановлення WindowsAPICodePack-Shell, ввівши наступну команду в консолі Nuget:

Install-Package WindowsAPICodePack-Shell -Version 1.1.1

Перш за все, ви повинні знати, що для цього класного рішення потрібно встановити відповідну програму на сервер, наприклад, якщо ви хочете, щоб оболонка генерувала мініатюру для файлів PDF, вам слід встановити програму зчитування PDF, як-от Acrobat Reader або Foxit PhantomPDF, або щось подібне їх на сервері, це означає, що сервер створить ескіз для файлів, які він їх розпізнає, і це має сенс.

По-друге , перевірте, для яких розширень файлів генерується ескіз, іншими словами, перевірте, чи мають розширення файлів .psd або .msi належні ескізи, а потім запобігайте запиту сервера для генерації ескізів для них (перевірте як на кінці клієнта, так і на сервері кінець).

По-третє , вам може знадобитися отримати ескіз через Ajax, особисто я пропоную вам в першу секунду надати файлам ескізи за замовчуванням на основі типу файлу, а потім запитати сервер для генерації ескізів, у цьому випадку ви можете використовувати наступний фрагмент коду (я отримую сформоване растрове зображення у форматі Base64 як зображення, яке насправді є рядком):

Код на стороні сервера:

[WebMethod]
public static string GetThumbnail(string path) {

    string thumb = string.Empty;

    path = @"D:\\" + path.Replace("/", "\\"); //Or Server.MapPath("/UploadedFiles");
    if (File.Exists(path))
    {
        try
        {
            Microsoft.WindowsAPICodePack.Shell.ShellFile shellFile = Microsoft.WindowsAPICodePack.Shell.ShellFile.FromFilePath(path);
            System.Drawing.Bitmap shellThumb = shellFile.Thumbnail.ExtraLargeBitmap;
            MemoryStream ms = new MemoryStream();
            shellThumb.Save(ms, System.Drawing.Imaging.ImageFormat.Jpeg);//Jpeg is preferred, Png would make a bigger file size
            thumb = Convert.ToBase64String(ms.ToArray());
        }
        catch(Exception ex)
        {

        }
    }
    return thumb;
}

Код на стороні клієнта:

    var url = "Default.aspx/GetThumbnail",
        path = "Images/1.jpg",//For instance
        extension = path.split(".").pop().toLowerCase(),
        allowedExtensions = ["jpg", "jpeg", "png", "gif", "pdf", "mp4"];
    
    if (allowedExtensions.includes(extension)){
        $.ajax({
            type: 'POST',
            contentType: "application/json; charset=utf-8",
            url: url,
            data: "{'path':'" + path + "'}",
            async: true,
            xhrFields: {
                withCredentials: true
            },
            success: function (response) {
                if (!$.isEmptyObject(response.d))
                    $(elem).find("img").attr("src", "data:image/jpg;base64," + response.d);
            },
            error: function (err) {
                console.error(err);
            }
        });
    }

Враховуйте, що оскільки повернутий рядок може бути довгим текстом, ви можете зіткнутися з помилкою:

Error during serialization or deserialization using the JSON JavaScriptSerializer. The length of the string exceeds the value set on the maxJsonLength property base64

у цьому випадку вам потрібно додати наступний код у файл web.config:

  <system.web.extensions>
       <scripting>
           <webServices>
               <jsonSerialization maxJsonLength="2147483647"/>
           </webServices>
       </scripting>
   </system.web.extensions>

maxJsonLength - цілочисельне значення, яке може бути Int32.MaxValue, докладніше ви можете прочитати тут .


Це працює для вас у Windows 10 або Windows Server 2016? Для мене це дає лише в Windows 7 очікувані результати для docx та pdf. Для зображень проблем немає.
пані Сміт

Я створив свого роду DMS (систему управління документами), використовуючи наведений вище код у Центрі обробки даних Windows Server 2016, тому він працює належним чином.
Мухаммед Мусаві

-3

Ви повинні використовувати вікна GDI + , щоб отримати ці ескізи, як це .


Хоча це коротка мила відповідь, вона робить не те, що мені потрібно. Він працює лише для .jpg, .gif, .png, .bmp та будь-яких інших файлів, що підтримуються в .NET. Якщо ви прочитаєте код, який ви назвали складним, ви виявите, що він насправді викликає метод IExtractImage у якомусь COM-об'єкті з GUID {BB2E617C-0920-11d1-9A0B-00C04FC2D6C1}. Це набагато більше, ніж просто масштабування зображення.
Брайан Гіллеспі,

Представлений мною компонент DotNet використовує GDI + безпосередньо з Win32API, а це означає, що цей компонент підтримує все, що підтримує ОС. Якщо кількість типів файлів, які підтримує ОС, не відповідає вашим потребам, вам слід шукати сторонній модуль для обробки ваших зображень.
Eran Betzalel

1
GDI + не підтримує, наприклад, PDF-файли або PSD Photoshop. Треті сторони можуть реєструвати обробники за допомогою оболонки; GDI + не розмовляє зі сторонніми COM-об'єктами.
Ян Бойд

Думаю, ви мене неправильно зрозуміли. Використовуйте GDI + / Win32API, якщо він підтримує потрібні вам типи файлів, якщо ні - використовуйте сторонні компоненти.
Еран Бецалель
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.