Using Image MSO in ADXOlForm

Add-in Express™ Support Service
That's what is more important than anything else

Using Image MSO in ADXOlForm
 
nwein




Posts: 577
Joined: 2011-03-28
so you got me intrigued with the https://www.add-in-express.com/forum/read.php?FID=5&TID=14398 to use CommandBars to get ImageMSO.
I've started playing around with this, and with the help of https://www.add-in-express.com/forum/read.php?FID=5&TID=10990&MID=56221 and https://blogs.msdn.microsoft.com/andreww/2007/07/30/converting-between-ipicturedisp-and-system-drawing-image/ I got it to work.
However the result comes back with some white background as can be seen in this image:
User added an image
Is there anyway to fix it ? (edit: never mind, solved this using BitMap MakeTransparent()).

I've noticed that there's an ADX MSO IPictureDisp interface (as opposed to the stdole.IPictureDisp). Do you guys support OLEPicture conversion internally so that I don't need to wrap my code with AX conversion?
Here's a code fragment that was used in the above example:

var activeExplorer = COMAddinModule.CurrentInstance.OutlookApp.ActiveExplorer();
var commandBars = activeExplorer.CommandBars;
IPictureDisp plusSignPicture = commandBars.GetImageMso("AddAccount", 16, 16);
Image plusSignImage = AxHostConverter.PictureDispToImage(plusSignPicture);
myButton.Image = plusSignImage;


Where AxHostConverter is:
public class AxHostConverter : AxHost
{
	private AxHostConverter() : base(string.Empty) { }

	public static IPictureDisp ImageToPictureDisp(Image image)
	{
		return (IPictureDisp) GetIPictureDispFromPicture(image);
	}
	
	public static Image PictureDispToImage(IPictureDisp pictureDisp)
	{
	return GetPictureFromIPicture(pictureDisp);
	}
}


I also don't like the fact that what's currently is easy design-time setting, now have to be converted manually when setting the images like so; I guess I could wrap it somehow similar to how the designer is doing it to make it less awkward (though it would have been nice if it was ADX__Forms would support it as a toolbox property similarly to how the ribbon designer supports it).
Posted 01 Mar, 2017 11:38:36 Top
Andrei Smolin


Add-in Express team


Posts: 18817
Joined: 2006-05-11
Hello Nir,

Below is a method which is based on the code published at https://imagemso.codeplex.com/SourceControl/latest#Excel.Images/Images.cs. You use this method as follows:

private void button1_Click(object sender, EventArgs e) {
    var commandBars = MyAddin41.AddinModule.CurrentInstance.ExcelApp.CommandBars;
    stdole.IPictureDisp myPictureDisp = commandBars.GetImageMso("FormatPainter", 16, 16);
    //Image myImage = AxHostConverter.PictureDispToImage(myPictureDisp);
    //button2.Image = myImage;
    Bitmap myImage = MyHelperClass.GetBitmapFromIPictureDisp(myPictureDisp);
    button2.Image = myImage;
}


nwein writes:
Do you guys support OLEPicture conversion internally so that I don't need to wrap my code with AX conversion?


No. That type is stdole.IPictureDisp re-declared.

nwein writes:
I also don't like the fact that what's currently is easy design-time setting, now have to be converted manually...


You can only retrieve Office images at the run time. Using them in code seems to be an only choice. On the other hand, you can create a descendant of e.g. System.Windows.Forms.Button use these images on a standard .NET controls which aren't prepared to use Office images. Creating an Office-dset of standard controls


Andrei Smolin
Add-in Express Team Leader

public class MyHelperClass {
    public static Bitmap GetBitmapFromIPictureDisp(stdole.IPictureDisp iPicturedisp) {

        try {   // Throws an ArgumentException if the ImageMso name is not supported.
            var ipd = iPicturedisp;

            // Gets the info about the HBITMAP inside the IPictureDisp.
            var dibsection = new DIBSECTION();
            GetObjectDIBSection((IntPtr)ipd.Handle, Marshal.SizeOf(dibsection), ref dibsection);
            var w = dibsection.dsBm.bmWidth;
            var h = dibsection.dsBm.bmHeight;

            // Create the destination Bitmap object.
            var image = new Bitmap(w, h, System.Drawing.Imaging.PixelFormat.Format32bppArgb);

            unsafe
            {
                // Gets a pointer to the raw bits.
                var pBits = (RGBQUAD*)(void*)dibsection.dsBm.bmBits;

                // Scan the image to check if alpha channel is empty
                // 24bpp RGB when false, 32bpp ARGB when true.
                bool alpha = false;
                for (int x = 0; x < dibsection.dsBmih.biWidth; x++)
                    for (int y = 0; y < dibsection.dsBmih.biHeight; y++)
                        alpha |= pBits[y * dibsection.dsBmih.biWidth + x].rgbReserved != 0;

                // Copy each pixel byte for byte.
                for (int x = 0; x < dibsection.dsBmih.biWidth; x++)
                    for (int y = 0; y < dibsection.dsBmih.biHeight; y++) {
                        var offset = y * dibsection.dsBmih.biWidth + x;
                        if (pBits[offset].rgbReserved != 0)
                            image.SetPixel(x, y, Color.FromArgb(pBits[offset].rgbReserved, pBits[offset].rgbRed, pBits[offset].rgbGreen, pBits[offset].rgbBlue));
                        else if (!alpha)
                            image.SetPixel(x, y, Color.FromArgb(pBits[offset].rgbRed, pBits[offset].rgbGreen, pBits[offset].rgbBlue));
                    }
            }
            return image;
        } catch (ArgumentException) {   // The ImageMso name or dimensions are not supported.
            return null;
        }
    }

    [StructLayout(LayoutKind.Sequential)]
    private struct RGBQUAD {
        public byte rgbBlue;
        public byte rgbGreen;
        public byte rgbRed;
        public byte rgbReserved;
    }

    [StructLayout(LayoutKind.Sequential)]
    private struct BITMAP {
        public Int32 bmType;
        public Int32 bmWidth;
        public Int32 bmHeight;
        public Int32 bmWidthBytes;
        public Int16 bmPlanes;
        public Int16 bmBitsPixel;
        public IntPtr bmBits;
    }

    [StructLayout(LayoutKind.Sequential)]
    private struct BITMAPINFOHEADER {
        public int biSize;
        public int biWidth;
        public int biHeight;
        public Int16 biPlanes;
        public Int16 biBitCount;
        public int biCompression;
        public int biSizeImage;
        public int biXPelsPerMeter;
        public int biYPelsPerMeter;
        public int biClrUsed;
        public int bitClrImportant;
    }

    [StructLayout(LayoutKind.Sequential)]
    private struct DIBSECTION {
        public BITMAP dsBm;
        public BITMAPINFOHEADER dsBmih;
        public int dsBitField1;
        public int dsBitField2;
        public int dsBitField3;
        public IntPtr dshSection;
        public int dsOffset;
    }

    [DllImport("gdi32.dll", EntryPoint = "GetObject")]
    private static extern int GetObjectDIBSection(IntPtr hObject, int nCount, ref DIBSECTION lpObject);
} 
Posted 02 Mar, 2017 05:34:29 Top
nwein




Posts: 577
Joined: 2011-03-28
Thanks Andrei, I'll digest this and see how it works.
Posted 02 Mar, 2017 13:02:28 Top
Andrei Smolin


Add-in Express team


Posts: 18817
Joined: 2006-05-11
You are welcome!


Andrei Smolin
Add-in Express Team Leader
Posted 03 Mar, 2017 03:03:30 Top