2012-04-30 86 views
1

我已經使用下面的代碼在ASP.Net應用程序內創建圖像的縮略圖。在HttpHandler中通過Windows API生成圖像的縮略圖

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; 
     } 
    } 
    } 
} 

我已經創建了一個HTTP處理程序,從那裏我這樣調用

using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Web; 
using System.Drawing; 
using System.IO; 
using RMA.Shell; 
using System.Drawing.Imaging; 
namespace DemoApp 
{ 
    /// <summary> 
    /// Summary description for ImageHandler 
    /// </summary> 
    public class ImageHandler : IHttpHandler 
    { 

     public void ProcessRequest(HttpContext context) 
     { 
      byte[] buffer = null; 
      string imgUrl = context.Request.Params["url"].ToString();    
      if (!string.IsNullOrEmpty(imgUrl)) 
      { 
       ShellThumbnail thumb = new ShellThumbnail(); 
       Bitmap bmp = thumb.GetThumbnail(context.Server.MapPath(imgUrl));     
       using (MemoryStream stram = new MemoryStream()) 
       { 
        bmp.Save(stram, ImageFormat.Jpeg);      
        buffer = stram.ToArray(); 
        bmp.Dispose(); 
        thumb.Dispose(); 
       } 
       HttpCachePolicy cachePilicy = context.Response.Cache; 
       cachePilicy.SetAllowResponseInBrowserHistory(false); 
       cachePilicy.SetCacheability(HttpCacheability.NoCache); 
       cachePilicy.SetExpires(DateTime.Now.AddDays(-1)); 
       context.Response.Clear(); 
       context.Response.ContentType = "Image/jpeg"; 
       context.Response.OutputStream.Write(buffer, 0, buffer.Length); 
       buffer = null; 
       context.Response.OutputStream.Close(); 
       context.Response.End(); 
      } 
     } 
     public bool IsReusable 
     { 
      get 
      { 
       return true; 
      } 
     } 
    } 
} 

的GetThumbnail()方法,我有一個關於這個代碼

  1. 有些懷疑這怎麼GetThumbnail()方法有效嗎?它使用Windows thumnail緩存ie.thumb.db?或者它爲每個呼叫生成thumnail?
  2. 使用此COM API有任何性能問題?它可以用於多個併發http請求嗎?

3.什麼是與Windows API代碼包殼實現這之間的差異,如

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

哪一個是更好的選擇?

4.內部實現將會怎樣?無論是使用GDI +還是其他類似IPP的方法?

請澄清

回答

3

不明白爲什麼這樣做時,有用於調整圖像大小簡單,清洗容易管理解決方案,所有的COM爛攤子:

  1. Image.GetThumbnailImage - 這種方法的缺點是低重新大小的圖像

  2. ImageResizer的質量 - 開源圖像縮放組件,非常好縮略圖質量

+0

我必須取得更好的表現。我發現此方法比其他方法生成縮略圖的速度更快 – KiranPalode

+1

實際上,您只是將底層開銷添加到底層的WIC調用中。 Windows資源管理器和縮略圖接口使用WIC API。 [ImageResizer](http://imageresizing.net)可讓您使用相同的WIC API,而無需通過[WicBuilder插件](http://imageresizing.net/plugins/wic)發生所有麻煩和內存泄漏。 –

+1

這不是默認設置,因爲它需要完全信任,並且WIC在Windows Server 2008 R2及更高版本上只是服務器安全的。 Windows Server 2008 R1也不能使用您的方法。 –