如何在 C# 中将 stdole.stdPicture 转换为 .net 图像?

发布于 2025-01-12 22:52:52 字数 1387 浏览 3 评论 0原文

我正在构建一个 Outlook 插件,其中我将所有 Exchange 用户从全局地址列表复制到本地联系人。

问题是我也想传输交换用户的图片,但是 exchUser.GetPicture() 返回一个 stdole.stdPicture 并且我还没有找到一个有效的解决方案来下载或将其转换为图像/ jpg/...

这里是从全局地址列表中获取交换用户的代码:

private void EnumerateGAL()
{
    Outlook.AddressList gal = Application.Session.GetGlobalAddressList(); 
    if (gal != null) 
    {
        for (int i = 1; i <= gal.AddressEntries.Count - 1; i++)
        {
            Outlook.AddressEntry addrEntry = gal.AddressEntries[i];
            Outlook.ExchangeUser exchUser = addrEntry.GetExchangeUser();
                    
            if (addrEntry.AddressEntryUserType == Outlook.OlAddressEntryUserType.olExchangeUserAddressEntry
                && exchUser.CompanyName == "")
            {
                CreateContact(exchUser);
                //exchUser.GetPicture() returns stdole.stdPicture
            }
        }
    }
    return;
}

我找到的最接近的解决方案是 stdole.IPictureDisp 的转换,它返回一个位图,但 IPuctureDisp 和 stdPicture 与我在某处读到的不同。

public static System.Drawing.Image ConvertPicture(stdole.IPictureDisp image)
{
    int type = image.Type;
    if (type == 1)
    {
        IntPtr hPal = (IntPtr)image.hPal;
        return Image.FromHbitmap((IntPtr)image.Handle, hPal);
    }
    return null;
}

最后我需要下载图片,因为我只能将图片上传到有路径的联系人。 那么,有没有办法下载 stdPicture 或将其转换为能够下载的呢?

Im building an Add-In for Outlook where I copy all exchange users from the global address list to the local contacts.

The problem is I want transfer the picture of the exchange user too, but exchUser.GetPicture() returns a stdole.stdPicture and I have not yet found a working solution to download or convert it into an image/jpg/...

Here the code to get the exchange User from the global address list:

private void EnumerateGAL()
{
    Outlook.AddressList gal = Application.Session.GetGlobalAddressList(); 
    if (gal != null) 
    {
        for (int i = 1; i <= gal.AddressEntries.Count - 1; i++)
        {
            Outlook.AddressEntry addrEntry = gal.AddressEntries[i];
            Outlook.ExchangeUser exchUser = addrEntry.GetExchangeUser();
                    
            if (addrEntry.AddressEntryUserType == Outlook.OlAddressEntryUserType.olExchangeUserAddressEntry
                && exchUser.CompanyName == "")
            {
                CreateContact(exchUser);
                //exchUser.GetPicture() returns stdole.stdPicture
            }
        }
    }
    return;
}

The closest solution I found, was a conversion of stdole.IPictureDisp which returns a bitmap but IPuctureDisp and stdPicture isn´t the same as I read somewhere.

public static System.Drawing.Image ConvertPicture(stdole.IPictureDisp image)
{
    int type = image.Type;
    if (type == 1)
    {
        IntPtr hPal = (IntPtr)image.hPal;
        return Image.FromHbitmap((IntPtr)image.Handle, hPal);
    }
    return null;
}

In the end I need to download the picture because I can only upload a picture to a contact with a path.
So, is there a way to download a stdPicture or convert it to be able to download it?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

戈亓 2025-01-19 22:52:52

完成工作有四种主要方法。

“传统”方法是使用 System.Windows.Forms.AxHost 类中的 GetIPictureDispFromPictureGetPictureFromIPicture 方法。它们都是类的受保护成员,因此您不能在外部使用它们。因此,通常对 AxHost 类进行子类化并公开在内部调用基类受保护方法的公共方法。这种方法允许您双向转换:

internal class AxHostConverter : AxHost
{
    private AxHostConverter() : base("") { }

    static public stdole.IPictureDisp ImageToPictureDisp(Image image)
    {
       return (stdole.IPictureDisp)GetIPictureDispFromPicture(image);
    }

    static public Image PictureDispToImage(stdole.IPictureDisp pictureDisp)
    {
        return GetPictureFromIPicture(pictureDisp);
    }
}

第二个选择是使用 OleLoadPictureOleCreatePictureIndirect。这里有一篇关于该主题的旧支持文章。 OleLoadPicture 创建一个新的图片对象并根据流的内容对其进行初始化。

internal class OleCreateConverter

{

    [DllImport("oleaut32.dll", EntryPoint = "OleCreatePictureIndirect",

        CharSet = CharSet.Ansi, ExactSpelling = true, PreserveSig = true)]

    private static extern int OleCreatePictureIndirect(

        [In] PictDescBitmap pictdesc, ref Guid iid, bool fOwn,

        [MarshalAs(UnmanagedType.Interface)] out object ppVoid);

    const short _PictureTypeBitmap = 1;
    [StructLayout(LayoutKind.Sequential)]
    internal class PictDescBitmap
    {
        internal int cbSizeOfStruct = Marshal.SizeOf(typeof(PictDescBitmap));
        internal int pictureType = _PictureTypeBitmap;
        internal IntPtr hBitmap = IntPtr.Zero;
        internal IntPtr hPalette = IntPtr.Zero;
        internal int unused = 0;

        internal PictDescBitmap(Bitmap bitmap)
        {
            this.hBitmap = bitmap.GetHbitmap();
        }
    }

    public static stdole.IPictureDisp ImageToPictureDisp(Image image)
    {
       if (image == null || !(image is Bitmap))
        {
            return null;
        }

        PictDescBitmap pictDescBitmap = new PictDescBitmap((Bitmap)image);
        object ppVoid = null;
        Guid iPictureDispGuid = typeof(stdole.IPictureDisp).GUID;
        OleCreatePictureIndirect(pictDescBitmap, ref iPictureDispGuid, true, out ppVoid);
        stdole.IPictureDisp picture = (stdole.IPictureDisp)ppVoid;
        return picture;
    }

    public static Image PictureDispToImage(stdole.IPictureDisp pictureDisp)
    {
        Image image = null;
        if (pictureDisp != null && pictureDisp.Type == _PictureTypeBitmap)
        {
            IntPtr paletteHandle = new IntPtr(pictureDisp.hPal);
            IntPtr bitmapHandle = new IntPtr(pictureDisp.Handle);
            image = Image.FromHbitmap(bitmapHandle, paletteHandle);
        }
        return image;
    }
}

第三个选择是使用 VB6 兼容性库(此处记录)。要使用此功能,您需要添加对 Microsoft.VisualBasic.Compatibility.dll 的引用,该引用列在添加引用 对话框的 .NET 选项卡上(它驻留在 GAC 中)。然后,您可以使用 Support 类中的 ImageToIPictureDispIPictureDispToImage 方法。这显然是迄今为止最简单的方法,尽管它确实引入了 VB6 兼容性 DLL。在内部,VB6 兼容性代码看起来很像上面的第二个选项 - 使用 OleCreatePictureIndirect

using Microsoft.VisualBasic.Compatibility.VB6;

internal class VB6CompatibilityConverter
{
    public static stdole.IPictureDisp ImageToPictureDisp(Image image)
    {
        return (stdole.IPictureDisp)Support.ImageToIPictureDisp(image);
    }

    public static Image PictureDispToImage(stdole.IPictureDisp pictureDisp)
    {
        return Support.IPictureDispToImage(pictureDisp);
    }
}

最后,您可以自己实现IPictureDispIPicture。如果您只想从图像转换为 IPictureDisp,这很好,但不能帮助您向另一个方向进行转换。下面的实现依赖于 Image 实际上是派生的 Bitmap 类型,因为我们在内部调用了 Bitmap.GetHbitmap。如果您想保留对通用 Image 类型的支持,则必须做更多的工作来 p/调用一堆未记录的 GDI 方法

internal class PictureDispConverter
{
    public static stdole.IPictureDisp BitmapToPictureDisp(Bitmap bitmap)
    {
        return new PictureDispImpl(bitmap);
    }
 
    public static Image PictureDispToBitmap(stdole.IPictureDisp pictureDisp)
    {
        // TODO
        return null;
    }
}

internal class PictureDispImpl : stdole.IPictureDisp, stdole.IPicture
{
    #region Init

    [DllImport("gdi32.dll")]
    static extern void DeleteObject(IntPtr handle);

    private Bitmap _image;
    private IntPtr _handle;
 
    public PictureDispImpl(Bitmap image)
    {
        _image = image;
    }
 
    ~PictureDispImpl()
    {
        if (_handle != IntPtr.Zero)
        {
            DeleteObject(_handle);
        }
    }

    #endregion

    #region IPictureDisp Members

    public int Width
    {
        get { return _image.Width; }
    }

    public int Height
    {
        get { return _image.Height; }
    }

    public short Type
    {
        get { return 1; }
    }

    public int Handle
    {
        get
        {
            if (_handle == IntPtr.Zero)
            {
                _handle = _image.GetHbitmap();
            }
            return _handle.ToInt32();
        }
    }
 
    public int hPal
    {
        get { return 0; }
        set { }
    }
 
    public void Render(
        int hdc, int x, int y, int cx, int cy, int xSrc, int ySrc, int cxSrc, int cySrc, IntPtr prcWBounds)
    {
        Graphics graphics = Graphics.FromHdc(new IntPtr(hdc));
        graphics.DrawImage(
            _image, new Rectangle(x, y, cx, cy), xSrc, ySrc, cxSrc, cySrc, GraphicsUnit.Pixel);
    }
 
    #endregion

    #region IPicture Members
 
    public int Attributes
    {
        get { return 0; }
    }

    public int CurDC
    {
        get { return 0; }
    }

    public bool KeepOriginalFormat
    {
        get { return false; }
        set { }
    }
 
    public void PictureChanged()
    {
    }
 
    public void SaveAsFile(IntPtr pstm, bool fSaveMemCopy, out int pcbSize)
    {
        pcbSize = 0;
    }

    public void SelectPicture(int hdcIn, out int phdcOut, out int phbmpOut)
    {
        phdcOut = 0;
        phbmpOut = 0;
    }

    public void SetHdc(int hdc)
    {

    }
    #endregion
}

There are four main ways to get the job done.

The "traditional" approach is to use the GetIPictureDispFromPicture and GetPictureFromIPicture methods in the System.Windows.Forms.AxHost class. These are both protected members of the class, so you can't use them externally. For this reason, it is common to subclass the AxHost class and expose public methods that internally call the base class protected methods. This approach allows you to convert in both directions:

internal class AxHostConverter : AxHost
{
    private AxHostConverter() : base("") { }

    static public stdole.IPictureDisp ImageToPictureDisp(Image image)
    {
       return (stdole.IPictureDisp)GetIPictureDispFromPicture(image);
    }

    static public Image PictureDispToImage(stdole.IPictureDisp pictureDisp)
    {
        return GetPictureFromIPicture(pictureDisp);
    }
}

Your second option is to use OleLoadPicture or OleCreatePictureIndirect. There's an old support article on the topic here. OleLoadPicture creates a new picture object and initializes it from the contents of a stream.

internal class OleCreateConverter

{

    [DllImport("oleaut32.dll", EntryPoint = "OleCreatePictureIndirect",

        CharSet = CharSet.Ansi, ExactSpelling = true, PreserveSig = true)]

    private static extern int OleCreatePictureIndirect(

        [In] PictDescBitmap pictdesc, ref Guid iid, bool fOwn,

        [MarshalAs(UnmanagedType.Interface)] out object ppVoid);

    const short _PictureTypeBitmap = 1;
    [StructLayout(LayoutKind.Sequential)]
    internal class PictDescBitmap
    {
        internal int cbSizeOfStruct = Marshal.SizeOf(typeof(PictDescBitmap));
        internal int pictureType = _PictureTypeBitmap;
        internal IntPtr hBitmap = IntPtr.Zero;
        internal IntPtr hPalette = IntPtr.Zero;
        internal int unused = 0;

        internal PictDescBitmap(Bitmap bitmap)
        {
            this.hBitmap = bitmap.GetHbitmap();
        }
    }

    public static stdole.IPictureDisp ImageToPictureDisp(Image image)
    {
       if (image == null || !(image is Bitmap))
        {
            return null;
        }

        PictDescBitmap pictDescBitmap = new PictDescBitmap((Bitmap)image);
        object ppVoid = null;
        Guid iPictureDispGuid = typeof(stdole.IPictureDisp).GUID;
        OleCreatePictureIndirect(pictDescBitmap, ref iPictureDispGuid, true, out ppVoid);
        stdole.IPictureDisp picture = (stdole.IPictureDisp)ppVoid;
        return picture;
    }

    public static Image PictureDispToImage(stdole.IPictureDisp pictureDisp)
    {
        Image image = null;
        if (pictureDisp != null && pictureDisp.Type == _PictureTypeBitmap)
        {
            IntPtr paletteHandle = new IntPtr(pictureDisp.hPal);
            IntPtr bitmapHandle = new IntPtr(pictureDisp.Handle);
            image = Image.FromHbitmap(bitmapHandle, paletteHandle);
        }
        return image;
    }
}

Your third option is to use the VB6 compatibility library, documented here. To use this, you'll need to add a reference to Microsoft.VisualBasic.Compatibility.dll, which is listed on the .NET tab of the Add References dialog (it resides in the GAC). Then, you can use the ImageToIPictureDisp and IPictureDispToImage methods in the Support class. This is obviously by far the simplest approach, although it does pull in the VB6 compatibility DLL. Internally, the VB6 compatibility code looks a lot like the second option above – using OleCreatePictureIndirect.

using Microsoft.VisualBasic.Compatibility.VB6;

internal class VB6CompatibilityConverter
{
    public static stdole.IPictureDisp ImageToPictureDisp(Image image)
    {
        return (stdole.IPictureDisp)Support.ImageToIPictureDisp(image);
    }

    public static Image PictureDispToImage(stdole.IPictureDisp pictureDisp)
    {
        return Support.IPictureDispToImage(pictureDisp);
    }
}

Finally, you can implement IPictureDisp and IPicture yourself. This is fine if you just want to convert from an Image to an IPictureDisp, but doesn't help you converting in the other direction. The implementation below relies on the Image actually being a derived Bitmap type, because we call Bitmap.GetHbitmap internally. If you want to keep the support to the generic Image type, you'll have to do a lot more work to p/invoke to a bunch of undocumented GDI methods instead

internal class PictureDispConverter
{
    public static stdole.IPictureDisp BitmapToPictureDisp(Bitmap bitmap)
    {
        return new PictureDispImpl(bitmap);
    }
 
    public static Image PictureDispToBitmap(stdole.IPictureDisp pictureDisp)
    {
        // TODO
        return null;
    }
}

internal class PictureDispImpl : stdole.IPictureDisp, stdole.IPicture
{
    #region Init

    [DllImport("gdi32.dll")]
    static extern void DeleteObject(IntPtr handle);

    private Bitmap _image;
    private IntPtr _handle;
 
    public PictureDispImpl(Bitmap image)
    {
        _image = image;
    }
 
    ~PictureDispImpl()
    {
        if (_handle != IntPtr.Zero)
        {
            DeleteObject(_handle);
        }
    }

    #endregion

    #region IPictureDisp Members

    public int Width
    {
        get { return _image.Width; }
    }

    public int Height
    {
        get { return _image.Height; }
    }

    public short Type
    {
        get { return 1; }
    }

    public int Handle
    {
        get
        {
            if (_handle == IntPtr.Zero)
            {
                _handle = _image.GetHbitmap();
            }
            return _handle.ToInt32();
        }
    }
 
    public int hPal
    {
        get { return 0; }
        set { }
    }
 
    public void Render(
        int hdc, int x, int y, int cx, int cy, int xSrc, int ySrc, int cxSrc, int cySrc, IntPtr prcWBounds)
    {
        Graphics graphics = Graphics.FromHdc(new IntPtr(hdc));
        graphics.DrawImage(
            _image, new Rectangle(x, y, cx, cy), xSrc, ySrc, cxSrc, cySrc, GraphicsUnit.Pixel);
    }
 
    #endregion

    #region IPicture Members
 
    public int Attributes
    {
        get { return 0; }
    }

    public int CurDC
    {
        get { return 0; }
    }

    public bool KeepOriginalFormat
    {
        get { return false; }
        set { }
    }
 
    public void PictureChanged()
    {
    }
 
    public void SaveAsFile(IntPtr pstm, bool fSaveMemCopy, out int pcbSize)
    {
        pcbSize = 0;
    }

    public void SelectPicture(int hdcIn, out int phdcOut, out int phbmpOut)
    {
        phdcOut = 0;
        phbmpOut = 0;
    }

    public void SetHdc(int hdc)
    {

    }
    #endregion
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文