如何在 C# 中将 stdole.stdPicture 转换为 .net 图像?
How do I convert stdole.stdPicture to a .net image in C#?
我正在为 Outlook 构建一个加载项,我将所有交换用户从全局地址列表复制到本地联系人。
问题是我也想传输交换用户的图片,但是 exchUser.GetPicture()
returns stdole.stdPicture 而且我还没有找到下载或转换它的有效解决方案进入 image/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 的转换,其中 returns 是位图,但 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或将其转换为能够下载呢?
完成工作有四种主要方法。
“传统”方法是使用 System.Windows.Forms.AxHost
class 中的 GetIPictureDispFromPicture
和 GetPictureFromIPicture
方法。它们都是 class 的受保护成员,因此您不能在外部使用它们。出于这个原因,subclass AxHost
class 和公开 public 内部调用基本 class 受保护方法的方法是很常见的。这种方法允许您双向转换:
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);
}
}
您的第二个选择是使用 OleLoadPicture
或 OleCreatePictureIndirect
。这里有一篇关于该主题的旧支持文章。 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
的引用,它在 Add References
对话框的 .NET 选项卡上列出(它位于 GAC 中)。然后,可以在Support
class中使用ImageToIPictureDisp
和IPictureDispToImage
方法。这显然是迄今为止最简单的方法,尽管它确实引入了 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);
}
}
最后,您可以自己实现IPictureDisp
和IPicture
。如果您只想将图像转换为 IPictureDisp
,这很好,但不会帮助您在另一个方向上进行转换。下面的实现依赖于 Image 实际上是派生的 Bitmap 类型,因为我们在内部调用 Bitmap.GetHbitmap
。如果你想保持对通用 Image
类型的支持,你将不得不做更多的工作来 p/invoke 一堆未记录的 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
}
我正在为 Outlook 构建一个加载项,我将所有交换用户从全局地址列表复制到本地联系人。
问题是我也想传输交换用户的图片,但是 exchUser.GetPicture()
returns stdole.stdPicture 而且我还没有找到下载或转换它的有效解决方案进入 image/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 的转换,其中 returns 是位图,但 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或将其转换为能够下载呢?
完成工作有四种主要方法。
“传统”方法是使用 System.Windows.Forms.AxHost
class 中的 GetIPictureDispFromPicture
和 GetPictureFromIPicture
方法。它们都是 class 的受保护成员,因此您不能在外部使用它们。出于这个原因,subclass AxHost
class 和公开 public 内部调用基本 class 受保护方法的方法是很常见的。这种方法允许您双向转换:
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);
}
}
您的第二个选择是使用 OleLoadPicture
或 OleCreatePictureIndirect
。这里有一篇关于该主题的旧支持文章。 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
的引用,它在 Add References
对话框的 .NET 选项卡上列出(它位于 GAC 中)。然后,可以在Support
class中使用ImageToIPictureDisp
和IPictureDispToImage
方法。这显然是迄今为止最简单的方法,尽管它确实引入了 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);
}
}
最后,您可以自己实现IPictureDisp
和IPicture
。如果您只想将图像转换为 IPictureDisp
,这很好,但不会帮助您在另一个方向上进行转换。下面的实现依赖于 Image 实际上是派生的 Bitmap 类型,因为我们在内部调用 Bitmap.GetHbitmap
。如果你想保持对通用 Image
类型的支持,你将不得不做更多的工作来 p/invoke 一堆未记录的 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
}