IExtractImage работает из консольного приложения, а не из плагина? - PullRequest
3 голосов
/ 10 февраля 2010

Я пишу программу, которая должна иметь возможность извлекать миниатюру изображения из файла. Я получил класс 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); возвращает ноль.

Я отладил немного дальше и обнаружил, что в методе ThumbnailCreator.getThumbNail(string file, IntPtr pidl,IshellFolder item) строка extractImage.Extract(out hBmp); возвращает IntPtr.Zero. Тогда как в консольном приложении, которое работает, этот метод на самом деле возвращает число. Может в этом проблема? Или, может быть, эта проблема случается до этого. Честно говоря, я совершенно заблудился, когда дело доходит до Interop и Windows API.

Кто-нибудь знает какие-либо возможные причины, по которым этот класс будет работать в автономном консольном приложении, но не как часть плагина для другого приложения?

Обновление

Вот немного подробнее о том, как создается этот плагин. Чтобы добавить пользовательскую команду в эту программу, вы создаете класс, который реализует интерфейс ICommand от is API. Интерфейс имеет единственный метод Execute (), где код - это место, которое должно выполняться, когда пользователь выполняет пользовательскую команду из программы. Однако нативное приложение - это то, что создает экземпляр моего командного класса и вызывает метод execute.

Что может отличаться в том, как нативное приложение создает экземпляр моего командного класса и / или вызывает метод Execute (), который препятствует работе извлечения миниатюр?

Ответы [ 2 ]

1 голос
/ 22 мая 2012

Просто сам столкнулся с этой проблемой, и я говорю, что потребовалось некоторое время, чтобы понять, что это проблема битности. В вашем сценарии ваше консольное приложение, вероятно, было скомпилировано как x64, а ваше приложение Windows - как x86.

Интерфейс ExtractImage (BB2E617C-0920-11d1-9A0B-00C04FC2D6C1) в 64-битной ОС доступен только из 64-битных приложений (поскольку проводник 64-битный), в результате чего «класс не зарегистрирован (исключение из HRESULT: 0x80040154 (REGDB_E_CLASSNOTREG») )) "исключение в приложениях, скомпилированных как x86.

0 голосов
/ 10 февраля 2010

Код, который вызывает API-интерфейс оболочки, должен находиться в однозаходной квартире COM, попробуйте добавить атрибут [STAThread] в поток, который вызывает это.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...