IExtractImage是从控制台应用程序而不是插件工作的?

时间:2010-02-10 05:59:20

标签: c# interop

我正在编写一个程序,需要能够从文件中提取缩略图。我已经掌握了一个类ThumbnailCreator,它可以做到这一点。该课程的来源如下。

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


internal class ThumbnailCreator : IDisposable
{
    // Fields
    private IMalloc alloc;
    private Size desiredSize;
    private bool disposed;
    private static readonly string fileExtention = ".jpg";
    private Bitmap thumbnail;

    // Methods
    public ThumbnailCreator()
    {
        this.desiredSize = new Size(100, 100);
    }

    public ThumbnailCreator(Size desiredSize)
    {
        this.desiredSize = new Size(100, 100);
        this.DesiredSize = desiredSize;
    }



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

    ~ThumbnailCreator()
    {
        this.Dispose();
    }

    private bool getThumbNail(string file, IntPtr pidl, IShellFolder item)
    {
        bool CS;
        IntPtr hBmp = IntPtr.Zero;
        IExtractImage extractImage = null;
        try
        {
            if (Path.GetFileName(PathFromPidl(pidl)).ToUpper().Equals(Path.GetFileName(file).ToUpper()))
            {
                int prgf;
                IUnknown iunk = null;
                Guid iidExtractImage = new Guid("BB2E617C-0920-11d1-9A0B-00C04FC2D6C1");
                item.GetUIObjectOf(IntPtr.Zero, 1, ref pidl, ref iidExtractImage, out prgf, ref iunk);
                extractImage = (IExtractImage) iunk;
                if (extractImage != null)
                {
                    SIZE sz = new SIZE {
                        cx = this.desiredSize.Width,
                        cy = this.desiredSize.Height
                    };
                    StringBuilder location = new StringBuilder(260, 260);
                    int priority = 0;
                    int requestedColourDepth = 0x20;
                    EIEIFLAG flags = EIEIFLAG.IEIFLAG_SCREEN | EIEIFLAG.IEIFLAG_ASPECT;
                    int uFlags = (int) flags;
                    extractImage.GetLocation(location, location.Capacity, ref priority, ref sz, requestedColourDepth, ref uFlags);
                    extractImage.Extract(out hBmp);


                    if (hBmp != IntPtr.Zero)
                    {
                        this.thumbnail = Image.FromHbitmap(hBmp);
                    }
                    Marshal.ReleaseComObject(extractImage);
                    extractImage = null;
                }
                return true;
            }
            CS = false;
        }
        catch (Exception)
        {
            if (hBmp != IntPtr.Zero)
            {
                UnManagedMethods.DeleteObject(hBmp);
            }
            if (extractImage != null)
            {
                Marshal.ReleaseComObject(extractImage);
            }
            throw;
        }
        return CS;
    }

    public Bitmap GetThumbNail(string file)
    {
        if (!File.Exists(file) && !Directory.Exists(file))
        {
            throw new FileNotFoundException(string.Format("The file '{0}' does not exist", file), file);
        }
        if (this.thumbnail != null)
        {
            this.thumbnail.Dispose();
            this.thumbnail = null;
        }
        IShellFolder folder = getDesktopFolder;
        if (folder != null)
        {
            IntPtr pidlMain;
            try
            {
                int cParsed;
                int pdwAttrib;
                string filePath = Path.GetDirectoryName(file);
                folder.ParseDisplayName(IntPtr.Zero, IntPtr.Zero, filePath, out cParsed, out pidlMain, out pdwAttrib);
            }
            catch (Exception)
            {
                Marshal.ReleaseComObject(folder);
                throw;
            }
            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)
                {
                    Marshal.ReleaseComObject(folder);
                    this.Allocator.Free(pidlMain);
                    throw;
                }
                if (item != null)
                {
                    IEnumIDList idEnum = null;
                    try
                    {
                        item.EnumObjects(IntPtr.Zero, ESHCONTF.SHCONTF_NONFOLDERS | ESHCONTF.SHCONTF_FOLDERS, ref idEnum);
                    }
                    catch (Exception)
                    {
                        Marshal.ReleaseComObject(folder);
                        this.Allocator.Free(pidlMain);
                        throw;
                    }
                    if (idEnum != null)
                    {
                        IntPtr pidl = IntPtr.Zero;
                        bool complete = false;
                        while (!complete)
                        {
                            int fetched;
                            if (idEnum.Next(1, ref pidl, out fetched) != 0)
                            {
                                pidl = IntPtr.Zero;
                                complete = true;
                            }
                            else if (this.getThumbNail(file, pidl, item))
                            {
                                complete = true;
                            }
                            if (pidl != IntPtr.Zero)
                            {
                                this.Allocator.Free(pidl);
                            }
                        }
                        Marshal.ReleaseComObject(idEnum);
                    }
                    Marshal.ReleaseComObject(item);
                }
                this.Allocator.Free(pidlMain);
            }
            Marshal.ReleaseComObject(folder);
        }
        return this.thumbnail;
    }

    private static string PathFromPidl(IntPtr pidl)
    {
        StringBuilder path = new StringBuilder(260, 260);
        if (UnManagedMethods.SHGetPathFromIDList(pidl, path) != 0)
        {
            return path.ToString();
        }
        return string.Empty;
    }

    // Properties
    private IMalloc Allocator
    {
        get
        {
            if (!this.disposed && (this.alloc == null))
            {
                UnManagedMethods.SHGetMalloc(out this.alloc);
            }
            return this.alloc;
        }
    }

    public Size DesiredSize
    {
        get
        {
            return this.desiredSize;
        }
        set
        {
            this.desiredSize = value;
        }
    }

    private static IShellFolder getDesktopFolder
    {
        get
        {
            IShellFolder ppshf;
            UnManagedMethods.SHGetDesktopFolder(out ppshf);
            return ppshf;
        }
    }

    public Bitmap ThumbNail
    {
        get
        {
            return this.thumbnail;
        }
    }

    // Nested Types
    private enum EIEIFLAG
    {
        IEIFLAG_ASPECT = 4,
        IEIFLAG_ASYNC = 1,
        IEIFLAG_CACHE = 2,
        IEIFLAG_GLEAM = 0x10,
        IEIFLAG_NOBORDER = 0x100,
        IEIFLAG_NOSTAMP = 0x80,
        IEIFLAG_OFFLINE = 8,
        IEIFLAG_ORIGSIZE = 0x40,
        IEIFLAG_QUALITY = 0x200,
        IEIFLAG_SCREEN = 0x20
    }

    [Flags]
    private enum ESFGAO
    {
        SFGAO_CANCOPY = 1,
        SFGAO_CANDELETE = 0x20,
        SFGAO_CANLINK = 4,
        SFGAO_CANMOVE = 2,
        SFGAO_CANRENAME = 0x10,
        SFGAO_CAPABILITYMASK = 0x177,
        SFGAO_COMPRESSED = 0x4000000,
        SFGAO_CONTENTSMASK = -2147483648,
        SFGAO_DISPLAYATTRMASK = 0xf0000,
        SFGAO_DROPTARGET = 0x100,
        SFGAO_FILESYSANCESTOR = 0x10000000,
        SFGAO_FILESYSTEM = 0x40000000,
        SFGAO_FOLDER = 0x20000000,
        SFGAO_GHOSTED = 0x80000,
        SFGAO_HASPROPSHEET = 0x40,
        SFGAO_HASSUBFOLDER = -2147483648,
        SFGAO_LINK = 0x10000,
        SFGAO_READONLY = 0x40000,
        SFGAO_REMOVABLE = 0x2000000,
        SFGAO_SHARE = 0x20000,
        SFGAO_VALIDATE = 0x1000000
    }

    [Flags]
    private enum ESHCONTF
    {
        SHCONTF_FOLDERS = 0x20,
        SHCONTF_INCLUDEHIDDEN = 0x80,
        SHCONTF_NONFOLDERS = 0x40
    }

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

    [Flags]
    private enum ESTRRET
    {
        STRRET_WSTR,
        STRRET_OFFSET,
        STRRET_CSTR
    }

    [ComImport, Guid("000214F2-0000-0000-C000-000000000046"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
    private interface IEnumIDList
    {
        [PreserveSig]
        int Next(int celt, ref IntPtr rgelt, out int pceltFetched);
        void Skip(int celt);
        void Reset();
        void Clone(ref ThumbnailCreator.IEnumIDList ppenum);
    }

    [ComImport, InterfaceType(ComInterfaceType.InterfaceIsIUnknown), Guid("BB2E617C-0920-11d1-9A0B-00C04FC2D6C1")]
    private interface IExtractImage
    {
        void GetLocation([Out, MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszPathBuffer, int cch, ref int pdwPriority, ref ThumbnailCreator.SIZE prgSize, int dwRecClrDepth, ref int pdwFlags);
        void Extract(out IntPtr phBmpThumbnail);
    }

    [ComImport, Guid("00000002-0000-0000-C000-000000000046"), InterfaceType(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();
    }

    [ComImport, InterfaceType(ComInterfaceType.InterfaceIsIUnknown), Guid("000214E6-0000-0000-C000-000000000046")]
    private interface IShellFolder
    {
        void ParseDisplayName(IntPtr hwndOwner, IntPtr pbcReserved, [MarshalAs(UnmanagedType.LPWStr)] string lpszDisplayName, out int pchEaten, out IntPtr ppidl, out int pdwAttributes);
        void EnumObjects(IntPtr hwndOwner, [MarshalAs(UnmanagedType.U4)] ThumbnailCreator.ESHCONTF grfFlags, ref ThumbnailCreator.IEnumIDList ppenumIDList);
        void BindToObject(IntPtr pidl, IntPtr pbcReserved, ref Guid riid, ref ThumbnailCreator.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 ThumbnailCreator.ESFGAO rgfInOut);
        void GetUIObjectOf(IntPtr hwndOwner, int cidl, ref IntPtr apidl, ref Guid riid, out int prgfInOut, ref ThumbnailCreator.IUnknown ppvOut);
        void GetDisplayNameOf(IntPtr pidl, [MarshalAs(UnmanagedType.U4)] ThumbnailCreator.ESHGDN uFlags, ref ThumbnailCreator.STRRET_CSTR lpName);
        void SetNameOf(IntPtr hwndOwner, IntPtr pidl, [MarshalAs(UnmanagedType.LPWStr)] string lpszName, [MarshalAs(UnmanagedType.U4)] ThumbnailCreator.ESHCONTF uFlags, ref IntPtr ppidlOut);
    }

    [ComImport, InterfaceType(ComInterfaceType.InterfaceIsIUnknown), Guid("00000000-0000-0000-C000-000000000046")]
    private interface IUnknown
    {
        [PreserveSig]
        IntPtr QueryInterface(ref Guid riid, out IntPtr pVoid);
        [PreserveSig]
        IntPtr AddRef();
        [PreserveSig]
        IntPtr Release();
    }

    [StructLayout(LayoutKind.Sequential)]
    private struct SIZE
    {
        public int cx;
        public int cy;
    }

    [StructLayout(LayoutKind.Explicit, CharSet=CharSet.Auto)]
    private struct STRRET_ANY
    {
        // Fields
        [FieldOffset(4)]
        public IntPtr pOLEString;
        [FieldOffset(0)]
        public ThumbnailCreator.ESTRRET uType;
    }

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

    private class UnManagedMethods
    {
        // Methods
        [DllImport("gdi32", CharSet=CharSet.Auto)]
        internal static extern int DeleteObject(IntPtr hObject);
        [DllImport("shell32", CharSet=CharSet.Auto)]
        internal static extern int SHGetDesktopFolder(out ThumbnailCreator.IShellFolder ppshf);
        [DllImport("shell32", CharSet=CharSet.Auto)]
        internal static extern int SHGetMalloc(out ThumbnailCreator.IMalloc ppMalloc);
        [DllImport("shell32", CharSet=CharSet.Auto)]
        internal static extern int SHGetPathFromIDList(IntPtr pidl, StringBuilder pszPath);
    }
}

作为测试,我创建了一个快速控制台应用程序,其代码如下。此测试工作正常,能够提取缩略图,并将其保存为PNG文件。

static void Main(string[] args)
{
    try
    {
        Console.WriteLine("press a key to extract");
        System.Console.ReadKey();

        string path = @"C:\somefile.xyz";
        ThumbnailCreator creator = new ThumbnailCreator();
        creator.DesiredSize = new Size(600, 600);
        Bitmap bm = creator.GetThumbNail(path);
        bm.Save(@"C:\blah.png", System.Drawing.Imaging.ImageFormat.Png);
        Console.WriteLine("press a key to exit");
        System.Console.ReadKey();
    }
    catch (Exception exp)
    {
        Console.WriteLine(exp.Message);

    }
}

我的问题是我想在运行中使用它作为另一个应用程序的插件的真正应用程序。当我尝试在插件中运行相同的测试代码时creator.GetThumbNail(path);返回null。

我进一步调试了一下,发现在方法ThumbnailCreator.getThumbNail(string file, IntPtr pidl,IshellFolder item)中,行extractImage.Extract(out hBmp);返回IntPtr.Zero。在使用此方法的控制台应用程序中实际返回一个数字。也许这就是问题?或者也许这个问题在此之前发生。老实说,当谈到Interop和Windows API时,我完全迷失了。

有没有人知道为什么这个类可以在独立的控制台应用程序中工作,但不能作为另一个应用程序的插件的一部分?

更新

以下是有关如何创建此插件的更多详细信息。要向此程序添加自定义命令,您需要创建一个从is API实现ICommand接口的类。该接口具有单个Execute()方法,其中代码是在用户从程序执行自定义命令时应该运行的位置。但是,本机应用程序实际上是实例化我的命令类并调用execute方法。

本机应用实例化我的命令类的方式有什么不同,或者调用会阻止缩略图提取工作的Execute()方法?

2 个答案:

答案 0 :(得分:1)

我自己也遇到过这个问题而且我确实说过,花了一些时间来弄清楚它是一个小问题。在您的方案中,您的控制台应用程序可能编译为x64,Windows应用程序编译为x86。

64位操作系统上的ExtractImage接口(BB2E617C-0920-11d1-9A0B-00C04FC2D6C1)只能从64位应用程序访问(因为资源管理器是64位),因此导致“类未注册(HRESULT异常:0x80040154(REGDB_E_CLASSNOTREG) ))“编译为x86的应用程序中的异常。

答案 1 :(得分:0)

调用Shell API的代码必须位于COM Single Threaded Apartment中,尝试将[STAThread]属性放在调用它的线程上。