IExtractImage在控制台应用程序中可以工作,但在插件中却不行?

3

我正在编写一个程序,需要从文件中提取缩略图。我已经找到了一个名为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 stuff时,我完全迷失了。
有人知道为什么这个类在独立的控制台应用程序中可以工作,但在其他应用程序的插件中却不能工作吗?
更新
这里有关于如何创建这个插件的更多细节。要添加一个自定义命令到这个程序中,你需要创建一个实现了API中的ICommand接口的类。该接口有一个单独的Execute()方法,其中放置的代码应在用户从程序中执行自定义命令时运行。然而,实际上实例化我的命令类并调用Execute()方法的是本机应用程序。
本机应用程序实例化我的命令类或调用Execute()方法的方式有什么不同,会阻止缩略图提取工作吗?
2个回答

1

刚遇到过这个问题,我不得不说,花了一些时间才发现它是一个位数的问题。在您的情况中,您的控制台应用程序可能已编译为x64,而您的Windows应用程序为x86。

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


0
代码调用Shell API必须在COM单线程公寓中,尝试将[STAThread]属性放在调用此方法的线程上。

我尝试使用 "System.Threading.Thread.CurrentThread.GetApartmentState();" 检查 ApartmentState,并发现当前线程已经是STA线程。我也尝试添加了 [STAThread] 属性,以防万一,但它仍然无法工作。 - Eric Anastas
一定是其他问题,抱歉。 - John Knoeller

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接