Resizing the embedded OLE objects using .NET

A few days back, I’ve got a case where my customer wanted to resize the embedded OLE Object programmatically? In this case Excel was embedded in Visio, that embedded file added to the shapes collection in Visio but in reality its nothing but an OLE document and we want to resize the OLE document instead of Shape. Note that if you activate the Excel workbook, resize the OLE container window which contains in-place activated Excel ,close the activation mode- It will resize the OLE object but if you don’t activate the  Excel Object and try to resize it, it changes the shape property instead of the OLE Object.

To do this, I resized the OLE object manually and recorded a macro. But it didn’t record anything. Then I tried to use Visio Object Model calls but it has limited number of method to manipulate the embedded OLE Object.

Finally we noticed that we can use IOLEObject interface which enables an embedded object to provide basic functionality to its container. And that’s exactly what we need. We want to resize the OLE container.

Here are the screenshots which explains the difference between resizing the OLE object and shape.

Original document :

clip_image001[4]

What if we resize the shape. Please note that it expands the shape.

clip_image001[6]

What if we resize the OLE Object. It exposes more rows and columns of Excel.

clip_image001[8]

Following are the steps to resize the OLE object.

Step 1 :Declare the IOLEObject Interface as described in https://msdn.microsoft.com/en-us/library/microsoft.visualstudio.ole.interop.ioleobject.aspx and its supporting interfaces.

 

 

 [ComImport]
  [Guid("00000118-0000-0000-C000-000000000046")]
  [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
  public interface IOleClientSite
  {
      void SaveObject();
      void GetMoniker(uint dwAssign, uint dwWhichMoniker, ref object ppmk);
      void GetContainer(ref object ppContainer);
      void ShowObject();
      void OnShowWindow(bool fShow);
      void RequestNewObjectLayout();
  }

  [ComImport, SuppressUnmanagedCodeSecurity, Guid("00000112-0000-0000-C000-000000000046"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
  public interface IOleObject
  {
      [PreserveSig]
      int SetClientSite([In, MarshalAs(UnmanagedType.Interface)] IOleClientSite pClientSite);
      IOleClientSite GetClientSite();
      [PreserveSig]
      int SetHostNames([In, MarshalAs(UnmanagedType.LPWStr)] string szContainerApp, [In, MarshalAs(UnmanagedType.LPWStr)] string szContainerObj);
      [PreserveSig]
      int Close(int dwSaveOption);
      [PreserveSig]
      int SetMoniker([In, MarshalAs(UnmanagedType.U4)] int dwWhichMoniker, [In, MarshalAs(UnmanagedType.Interface)] object pmk);
      [PreserveSig]
      int GetMoniker([In, MarshalAs(UnmanagedType.U4)] int dwAssign, [In, MarshalAs(UnmanagedType.U4)] int dwWhichMoniker, [MarshalAs(UnmanagedType.Interface)] out object moniker);
      [PreserveSig]
      int InitFromData([In, MarshalAs(UnmanagedType.Interface)] IDataObject pDataObject, int fCreation, [In, MarshalAs(UnmanagedType.U4)] int dwReserved);
      [PreserveSig]
      int GetClipboardData([In, MarshalAs(UnmanagedType.U4)] int dwReserved, out IDataObject data);
      [PreserveSig]
      int DoVerb(int iVerb, [In] IntPtr lpmsg, [In, MarshalAs(UnmanagedType.Interface)] IOleClientSite pActiveSite, int lindex, IntPtr hwndParent, [In] Win32.COMRECT lprcPosRect);
      [PreserveSig]
      int EnumVerbs(out IEnumOLEVERB e);
      [PreserveSig]
      int OleUpdate();
      [PreserveSig]
      int IsUpToDate();
      [PreserveSig]
      int GetUserClassID([In, Out] ref Guid pClsid);
      [PreserveSig]
      int GetUserType([In, MarshalAs(UnmanagedType.U4)] int dwFormOfType, [MarshalAs(UnmanagedType.LPWStr)] out string userType);
      [PreserveSig]
      int SetExtent([In, MarshalAs(UnmanagedType.U4)] int dwDrawAspect, [In] Win32.tagSIZEL pSizel);
      [PreserveSig]
      int GetExtent([In, MarshalAs(UnmanagedType.U4)] int dwDrawAspect, [Out]Win32.tagSIZEL pSizel);
      [PreserveSig]
      int Advise(IAdviseSink pAdvSink, out int cookie);
      [PreserveSig]
      int Unadvise([In, MarshalAs(UnmanagedType.U4)] int dwConnection);
      [PreserveSig]
      int EnumAdvise(out IEnumSTATDATA e);
      [PreserveSig]
      int GetMiscStatus([In, MarshalAs(UnmanagedType.U4)] int dwAspect, out int misc);
      [PreserveSig]
      int SetColorScheme([In] Win32.tagLOGPALETTE pLogpal);
  }

  [ComImport, Guid("00000104-0000-0000-C000-000000000046"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
  public interface IEnumOLEVERB
  {
      [PreserveSig]
      int Next([MarshalAs(UnmanagedType.U4)] int celt, [Out] Win32.tagOLEVERB rgelt, [Out, MarshalAs(UnmanagedType.LPArray)] int[] pceltFetched);
      [PreserveSig]
      int Skip([In, MarshalAs(UnmanagedType.U4)] int celt);
      void Reset();
      void Clone(out IEnumOLEVERB ppenum);
  }

In the above sample we have used Win32 class. This class defines the necessary types used by IOLEObject interface. Here is the sample cs file

 public class Win32
    {
    [StructLayout(LayoutKind.Sequential)]
        public class COMRECT
        {
            public int left;
            public int top;
            public int right;
            public int bottom;
            public COMRECT() { }
            public COMRECT(Rectangle r)
            {
                this.left = r.X;
                this.top = r.Y;
                this.right = r.Right;
                this.bottom = r.Bottom;
            }
            public COMRECT(int left, int top, int right, int bottom)
            {
                this.left = left;
                this.top = top;
                this.right = right;
                this.bottom = bottom;
            }
            public static COMRECT FromXYWH(int x, int y, int width, int height)
            {
                return new COMRECT(x, y, x + width, y + height);
            }
            public override string ToString()
            {
                return string.Concat(new object[] { "Left = ", this.left, " Top ", this.top, " Right = ", this.right, " Bottom = ", this.bottom });
            }
        }

        [StructLayout(LayoutKind.Sequential)]
        public sealed class tagLOGPALETTE
        {
            [MarshalAs(UnmanagedType.U2)]
            public short palVersion;
            [MarshalAs(UnmanagedType.U2)]
            public short palNumEntries;
            public tagLOGPALETTE() { }
        }

        [StructLayout(LayoutKind.Sequential)]
        public sealed class tagSIZEL
        {
            public int cx;
            public int cy;
            public tagSIZEL() { }
            public tagSIZEL(int cx, int cy) { this.cx = cx; this.cy = cy; }
            public tagSIZEL(tagSIZEL o) { this.cx = o.cx; this.cy = o.cy; }
        }

        [StructLayout(LayoutKind.Sequential)]
        public sealed class tagOLEVERB
        {
            public int lVerb;
            [MarshalAs(UnmanagedType.LPWStr)]
            public string lpszVerbName;
            [MarshalAs(UnmanagedType.U4)]
            public int fuFlags;
            [MarshalAs(UnmanagedType.U4)]
            public int grfAttribs;
            public tagOLEVERB() { }
        }
    }

Step 3: Now call the GetExtent and setExtent method of the IOLEObject interface to get and set the coordinates of the OLEObject. Following is the sample code snippet to call the methods.

 Visio.Application oApp= new Visio.Application();
Visio.Document oDoc= oApp.Documents.Open(<<Valid document path>>);    //Instantiate a document
Visio.Pages oPages = oDoc.Pages;  //Get the pages collection of the document
Visio.Page oPage = oPages[1];  //Get the first page
Visio.Shape oShape = oPage.Shapes[1];  //Get the first shape on the page
object obj = oShape.Object;   //Get the object associated with the shape
IOleObject oc = obj as IOleObject;  //Cast the object as an IOleObject

Win32.tagSIZEL sz=new Win32.tagSIZEL();  //Create a tagSIZEL structure (see: https://msdn.microsoft.com/en-us/library/bb401793.aspx)
    
oc.GetExtent(1, sz);  //Get the current size in the structure we just created
sz.cx = 20000; //change the size
sz.cy = 8000;
oc.SetExtent(1, sz); //Apply that change to the object