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

标签 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 时,我完全不知所措。

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

更新

下面是有关如何创建此插件的更多详细信息。要向该程序添加自定义命令,您需要创建一个类,该类实现来自 API 的 ICommand 接口(interface)。该接口(interface)有一个 Execute() 方法,其中放置了当用户从程序中执行自定义命令时应运行的代码。然而, native 应用程序实际实例化了我的命令类并调用了执行方法。

native 应用程序实例化我的命令类和/或调用 Execute() 方法会阻止缩略图提取工作的方式有何不同?

最佳答案

我自己刚遇到这个问题,我确实说,花了一些时间才弄清楚这是一个位数问题。在您的场景中,您的控制台应用程序可能被编译为 x64,您的 Windows 应用程序可能被编译为 x86。

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

关于c# - IExtractImage 从控制台应用程序而不是插件工作?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/2234759/

相关文章:

c# - 在 C# 中将 Soap XML 解析为对象

c# - 下载前 1000 个字节

c# - 如何模仿URI查询

c++ - 为 CGI 和 WPF 开发混合的 C++ 和 C++/CLI

.net - 如何集成/混合托管和非托管代码

c# - 使用多个 VS 解决方案和多个项目时的 NuGet 策略

c# - 从 C# 注册自定义 win32 窗口类

c# - Outlook Redemption 版本

实现 C++ 接口(interface)的 C# COM 程序集

c# - 为什么 Thread.Start 会抛出 OutOfMemoryException