/******************************************************************************* * Copyright (c) 2000, 2017 IBM Corporation and others. * * This program and the accompanying materials * are made available under the terms of the Eclipse Public License 2.0 * which accompanies this distribution, and is available at * https://www.eclipse.org/legal/epl-2.0/ * * SPDX-License-Identifier: EPL-2.0 * * Contributors: * IBM Corporation - initial API and implementation *******************************************************************************/ package org.eclipse.swt.ole.win32; import java.io.*; import org.eclipse.swt.*; import org.eclipse.swt.graphics.*; import org.eclipse.swt.internal.*; import org.eclipse.swt.internal.ole.win32.*; import org.eclipse.swt.internal.win32.*; import org.eclipse.swt.widgets.*; /** * OleClientSite provides a site to manage an embedded OLE Document within a container. * *

The OleClientSite provides the following capabilities: *

* *

This object implements the OLE Interfaces IUnknown, IOleClientSite, IAdviseSink, * IOleInPlaceSite * *

Note that although this class is a subclass of Composite, * it does not make sense to add Control children to it, * or set a layout on it. *

*
*
Styles
BORDER *
Events
Dispose, Move, Resize *
* * @see OLE and ActiveX snippets * @see SWT Examples: OLEExample, OleWebBrowser */ public class OleClientSite extends Composite { // Interfaces for this Ole Client Container COMObject iOleClientSite; private COMObject iAdviseSink; private COMObject iOleInPlaceSite; private COMObject iOleDocumentSite; protected GUID appClsid; private GUID objClsid; private int refCount; // References to the associated Frame. protected OleFrame frame; // Access to the embedded/linked Ole Object protected IUnknown objIUnknown; protected IOleObject objIOleObject; protected IViewObject2 objIViewObject2; protected IOleInPlaceObject objIOleInPlaceObject; protected IOleCommandTarget objIOleCommandTarget; protected IOleDocumentView objDocumentView; // Related storage information protected IStorage tempStorage; // IStorage interface of the receiver // Internal state and style information private int aspect; // the display aspect of the embedded object, e.g., DvaspectContent or DvaspectIcon private int type; // Indicates the type of client that can be supported inside this container private boolean isStatic; // Indicates item's display is static, i.e., a bitmap, metafile, etc. boolean isActivated; private RECT borderWidths = new RECT(); private RECT indent = new RECT(); private boolean inUpdate = false; private boolean inInit = true; private boolean inDispose = false; private static final String WORDPROGID = "Word.Document"; //$NON-NLS-1$ private Listener listener; static final int STATE_NONE = 0; static final int STATE_RUNNING = 1; static final int STATE_INPLACEACTIVE = 2; static final int STATE_UIACTIVE = 3; static final int STATE_ACTIVE = 4; int state = STATE_NONE; protected OleClientSite(Composite parent, int style) { /* * NOTE: this constructor should never be used by itself because it does * not create an Ole Object */ super(parent, style); createCOMInterfaces(); // install the Ole Frame for this Client Site while (parent != null) { if (parent instanceof OleFrame){ frame = (OleFrame)parent; break; } parent = parent.getParent(); } if (frame == null) OLE.error(SWT.ERROR_INVALID_ARGUMENT); frame.AddRef(); aspect = COM.DVASPECT_CONTENT; type = COM.OLEEMBEDDED; isStatic = false; listener = new Listener() { private int nestedFocusEvents = 0; @Override public void handleEvent(Event e) { switch (e.type) { case SWT.Resize : case SWT.Move : onResize(e); break; case SWT.Dispose : onDispose(e); break; case SWT.FocusIn: nestedFocusEvents++; boolean hasFocus = isFocusControl(); onFocusIn(e); nestedFocusEvents--; /* * Added additional check below to avoid calling OleFrame#onFocusIn() twice, * which other wise lead to Main Menu refresh problem as seen in bug 527268 */ if (nestedFocusEvents == 0 && hasFocus == isFocusControl()) frame.onFocusIn(e); break; case SWT.FocusOut: nestedFocusEvents++; onFocusOut(e); nestedFocusEvents--; if (nestedFocusEvents == 0) frame.onFocusOut(e); break; case SWT.Paint: onPaint(e); break; case SWT.Traverse: onTraverse(e); break; case SWT.KeyDown: /* required for traversal */ break; case SWT.Activate: isActivated = true; break; case SWT.Deactivate: isActivated = false; break; default : OLE.error(SWT.ERROR_NOT_IMPLEMENTED); } } }; frame.addListener(SWT.Resize, listener); frame.addListener(SWT.Move, listener); addListener(SWT.Dispose, listener); addListener(SWT.FocusIn, listener); addListener(SWT.FocusOut, listener); addListener(SWT.Paint, listener); addListener(SWT.Traverse, listener); addListener(SWT.KeyDown, listener); addListener(SWT.Activate, listener); addListener(SWT.Deactivate, listener); } /** * Create an OleClientSite child widget using the OLE Document type associated with the * specified file. The OLE Document type is determined either through header information in the file * or through a Registry entry for the file extension. Use style bits to select a particular look * or set of properties. * * @param parent a composite widget; must be an OleFrame * @param style the bitwise OR'ing of widget styles * @param file the file that is to be opened in this OLE Document * * @exception IllegalArgumentException * * @exception SWTException * */ public OleClientSite(Composite parent, int style, File file) { this(parent, style); try { if (file == null || file.isDirectory() || !file.exists()) OLE.error(SWT.ERROR_INVALID_ARGUMENT); // Is there an associated CLSID? GUID fileClsid = new GUID(); char[] fileName = (file.getAbsolutePath()+"\0").toCharArray(); int result = COM.GetClassFile(fileName, fileClsid); if (result != COM.S_OK) OLE.error(OLE.ERROR_INVALID_CLASSID, result); // associated CLSID may not be installed on this machine String progID = getProgID(fileClsid); if (progID == null) OLE.error(OLE.ERROR_INVALID_CLASSID, result); appClsid = fileClsid; OleCreate(appClsid, fileClsid, fileName, file); } catch (SWTException e) { dispose(); disposeCOMInterfaces(); throw e; } } /** * Create an OleClientSite child widget to edit a blank document using the specified OLE Document * application. Use style bits to select a particular look or set of properties. * * @param parent a composite widget; must be an OleFrame * @param style the bitwise OR'ing of widget styles * @param progId the unique program identifier of an OLE Document application; * the value of the ProgID key or the value of the VersionIndependentProgID key specified * in the registry for the desired OLE Document (for example, the VersionIndependentProgID * for Word is Word.Document) * * @exception IllegalArgumentException * * @exception SWTException * */ public OleClientSite(Composite parent, int style, String progId) { this(parent, style); try { appClsid = getClassID(progId); if (appClsid == null) OLE.error(OLE.ERROR_INVALID_CLASSID); // Open a temporary storage object tempStorage = createTempStorage(); // Create ole object with storage object long[] address = new long[1]; /* * Bug in ICA Client 2.7. The creation of the IOleObject fails if the client * site is provided to OleCreate(). The fix is to detect that the program * id is an ICA Client and do not pass a client site to OleCreate(). * IOleObject.SetClientSite() is called later on. */ long clientSite = isICAClient() ? 0 : iOleClientSite.getAddress(); int result = COM.OleCreate(appClsid, COM.IIDIUnknown, COM.OLERENDER_DRAW, null, clientSite, tempStorage.getAddress(), address); if (result != COM.S_OK) OLE.error(OLE.ERROR_CANNOT_CREATE_OBJECT, result); objIUnknown = new IUnknown(address[0]); // Init sinks addObjectReferences(); if (COM.OleRun(objIUnknown.getAddress()) == OLE.S_OK) state = STATE_RUNNING; } catch (SWTException e) { dispose(); disposeCOMInterfaces(); throw e; } } /** * Create an OleClientSite child widget to edit the specified file using the specified OLE Document * application. Use style bits to select a particular look or set of properties. *

* IMPORTANT: This method is not part of the public * API for OleClientSite. It is marked public only so that it * can be shared within the packages provided by SWT. It is not * available on all platforms, and should never be called from * application code. *

* @param parent a composite widget; must be an OleFrame * @param style the bitwise OR'ing of widget styles * @param progId the unique program identifier of am OLE Document application; * the value of the ProgID key or the value of the VersionIndependentProgID key specified * in the registry for the desired OLE Document (for example, the VersionIndependentProgID * for Word is Word.Document) * @param file the file that is to be opened in this OLE Document * * @exception IllegalArgumentException * * @exception SWTException * * * @noreference This method is not intended to be referenced by clients. */ public OleClientSite(Composite parent, int style, String progId, File file) { this(parent, style); try { if (file == null || file.isDirectory() || !file.exists()) OLE.error(SWT.ERROR_INVALID_ARGUMENT); appClsid = getClassID(progId); if (appClsid == null) OLE.error(OLE.ERROR_INVALID_CLASSID); // Are we opening this file with the preferred OLE object? char[] fileName = (file.getAbsolutePath()+"\0").toCharArray(); GUID fileClsid = new GUID(); COM.GetClassFile(fileName, fileClsid); OleCreate(appClsid, fileClsid, fileName, file); } catch (SWTException e) { dispose(); disposeCOMInterfaces(); throw e; } } void OleCreate(GUID appClsid, GUID fileClsid, char[] fileName, File file) { /* Bug in Windows. In some machines running Windows Vista and * Office 2007, OleCreateFromFile() fails to open files from * Office Word 97 - 2003 and in some other cases it fails to * save files due to a lock. The fix is to detect this case and * create the activeX using CoCreateInstance(). */ boolean isOffice2007 = isOffice2007(true); if (!isOffice2007 && COM.IsEqualGUID(appClsid, fileClsid)){ // Using the same application that created file, therefore, use default mechanism. tempStorage = createTempStorage(); // Create ole object with storage object long[] address = new long[1]; int result = COM.OleCreateFromFile(appClsid, fileName, COM.IIDIUnknown, COM.OLERENDER_DRAW, null, iOleClientSite.getAddress(), tempStorage.getAddress(), address); if (result != COM.S_OK) OLE.error(OLE.ERROR_CANNOT_CREATE_OBJECT, result); objIUnknown = new IUnknown(address[0]); } else { // Not using the same application that created file, therefore, copy from original file to a new storage file IStorage storage = null; if (COM.StgIsStorageFile(fileName) == COM.S_OK) { long[] address = new long[1]; int mode = COM.STGM_READ | COM.STGM_TRANSACTED | COM.STGM_SHARE_EXCLUSIVE; int result = COM.StgOpenStorage(fileName, 0, mode, 0, 0, address); //Does an AddRef if successful if (result != COM.S_OK) OLE.error(OLE.ERROR_CANNOT_OPEN_FILE, result); storage = new IStorage(address[0]); } else { // Original file is not a Storage file so copy contents to a stream in a new storage file long[] address = new long[1]; int mode = COM.STGM_READWRITE | COM.STGM_DIRECT | COM.STGM_SHARE_EXCLUSIVE | COM.STGM_CREATE; int result = COM.StgCreateDocfile(null, mode | COM.STGM_DELETEONRELEASE, 0, address); // Increments ref count if successful if (result != COM.S_OK) OLE.error(OLE.ERROR_CANNOT_OPEN_FILE, result); storage = new IStorage(address[0]); // Create a stream on the storage object. // Word does not follow the standard and does not use "CONTENTS" as the name of // its primary stream String streamName = "CONTENTS"; //$NON-NLS-1$ GUID wordGUID = getClassID(WORDPROGID); if (wordGUID != null && COM.IsEqualGUID(appClsid, wordGUID)) streamName = "WordDocument"; //$NON-NLS-1$ if (isOffice2007) streamName = "Package"; //$NON-NLS-1$ address = new long[1]; result = storage.CreateStream(streamName, mode, 0, 0, address); // Increments ref count if successful if (result != COM.S_OK) { storage.Release(); OLE.error(OLE.ERROR_CANNOT_OPEN_FILE, result); } IStream stream = new IStream(address[0]); try { // Copy over data in file to named stream FileInputStream fileInput = new FileInputStream(file); int increment = 1024*4; byte[] buffer = new byte[increment]; int count = 0; while((count = fileInput.read(buffer)) > 0){ long pv = OS.CoTaskMemAlloc(count); OS.MoveMemory(pv, buffer, count); result = stream.Write(pv, count, null) ; OS.CoTaskMemFree(pv); if (result != COM.S_OK) { fileInput.close(); stream.Release(); storage.Release(); OLE.error(OLE.ERROR_CANNOT_OPEN_FILE, result); } } fileInput.close(); stream.Commit(COM.STGC_DEFAULT); stream.Release(); } catch (IOException err) { stream.Release(); storage.Release(); OLE.error(OLE.ERROR_CANNOT_OPEN_FILE); } } // Open a temporary storage object tempStorage = createTempStorage(); // Copy over contents of file int result = storage.CopyTo(0, null, null, tempStorage.getAddress()); storage.Release(); if (result != COM.S_OK) OLE.error(OLE.ERROR_CANNOT_OPEN_FILE, result); // create ole client long[] ppv = new long[1]; result = COM.CoCreateInstance(appClsid, 0, COM.CLSCTX_INPROC_HANDLER | COM.CLSCTX_INPROC_SERVER, COM.IIDIUnknown, ppv); if (result != COM.S_OK) OLE.error(OLE.ERROR_CANNOT_CREATE_OBJECT, result); objIUnknown = new IUnknown(ppv[0]); // get the persistent storage of the ole client ppv = new long[1]; result = objIUnknown.QueryInterface(COM.IIDIPersistStorage, ppv); if (result != COM.S_OK) OLE.error(OLE.ERROR_CANNOT_CREATE_OBJECT, result); IPersistStorage iPersistStorage = new IPersistStorage(ppv[0]); // load the contents of the file into the ole client site result = iPersistStorage.Load(tempStorage.getAddress()); iPersistStorage.Release(); if (result != COM.S_OK)OLE.error(OLE.ERROR_CANNOT_CREATE_OBJECT, result); } // Init sinks addObjectReferences(); if (COM.OleRun(objIUnknown.getAddress()) == OLE.S_OK) state = STATE_RUNNING; } protected void addObjectReferences() { // long[] ppvObject = new long[1]; if (objIUnknown.QueryInterface(COM.IIDIPersist, ppvObject) == COM.S_OK) { IPersist objIPersist = new IPersist(ppvObject[0]); GUID tempid = new GUID(); if (objIPersist.GetClassID(tempid) == COM.S_OK) objClsid = tempid; objIPersist.Release(); } // ppvObject = new long[1]; int result = objIUnknown.QueryInterface(COM.IIDIViewObject2, ppvObject); if (result != COM.S_OK) OLE.error(OLE.ERROR_INTERFACE_NOT_FOUND, result); objIViewObject2 = new IViewObject2(ppvObject[0]); objIViewObject2.SetAdvise(aspect, 0, iAdviseSink.getAddress()); // ppvObject = new long[1]; result = objIUnknown.QueryInterface(COM.IIDIOleObject, ppvObject); if (result != COM.S_OK) OLE.error(OLE.ERROR_INTERFACE_NOT_FOUND, result); objIOleObject = new IOleObject(ppvObject[0]); /* * Feature in Windows. Despite the fact that the clientSite was provided during the * creation of the OleObject (which is required by WMP11 - see bug 173556), * some applications choose to ignore this optional parameter (see bug 211663) * during OleCreate. The fix is to check whether the clientSite has already been set * and set it. Note that setting it twice can result in assert failures. */ long[] ppvClientSite = new long[1]; result = objIOleObject.GetClientSite(ppvClientSite); if (ppvClientSite[0] == 0) { objIOleObject.SetClientSite(iOleClientSite.getAddress()); } else { Release(); // GetClientSite performs an AddRef so we must release it. } int[] pdwConnection = new int[1]; objIOleObject.Advise(iAdviseSink.getAddress(), pdwConnection); objIOleObject.SetHostNames("main", "main"); //$NON-NLS-1$ //$NON-NLS-2$ // Notify the control object that it is embedded in an OLE container COM.OleSetContainedObject(objIUnknown.getAddress(), true); // Is OLE object linked or embedded? ppvObject = new long[1]; if (objIUnknown.QueryInterface(COM.IIDIOleLink, ppvObject) == COM.S_OK) { IOleLink objIOleLink = new IOleLink(ppvObject[0]); long[] ppmk = new long[1]; if (objIOleLink.GetSourceMoniker(ppmk) == COM.S_OK) { new IUnknown(ppmk[0]).Release(); type = COM.OLELINKED; objIOleLink.BindIfRunning(); } else { isStatic = true; } objIOleLink.Release(); } } protected int AddRef() { refCount++; return refCount; } private int CanInPlaceActivate() { if (aspect == COM.DVASPECT_CONTENT && type == COM.OLEEMBEDDED) return COM.S_OK; return COM.S_FALSE; } private int ContextSensitiveHelp(int fEnterMode) { return COM.S_OK; } protected void createCOMInterfaces() { iOleClientSite = new COMObject(new int[]{2, 0, 0, 0, 3, 1, 0, 1, 0}){ @Override public long method0(long[] args) {return QueryInterface(args[0], args[1]);} @Override public long method1(long[] args) {return AddRef();} @Override public long method2(long[] args) {return Release();} @Override public long method3(long[] args) {return SaveObject();} // method4 GetMoniker - not implemented @Override public long method5(long[] args) {return GetContainer(args[0]);} @Override public long method6(long[] args) {return ShowObject();} @Override public long method7(long[] args) {return OnShowWindow((int)args[0]);} // method8 RequestNewObjectLayout - not implemented }; iAdviseSink = new COMObject(new int[]{2, 0, 0, 2, 2, 1, 0, 0}){ @Override public long method0(long[] args) {return QueryInterface(args[0], args[1]);} @Override public long method1(long[] args) {return AddRef();} @Override public long method2(long[] args) {return Release();} @Override public long method3(long[] args) {return OnDataChange(args[0], args[1]);} @Override public long method4(long[] args) {return OnViewChange((int)args[0], (int)args[1]);} //method5 OnRename - not implemented @Override public long method6(long[] args) {OnSave();return 0;} @Override public long method7(long[] args) {return OnClose();} }; iOleInPlaceSite = new COMObject(new int[]{2, 0, 0, 1, 1, 0, 0, 0, 5, 1, 1, 0, 0, 0, 1}){ @Override public long method0(long[] args) {return QueryInterface(args[0], args[1]);} @Override public long method1(long[] args) {return AddRef();} @Override public long method2(long[] args) {return Release();} @Override public long method3(long[] args) {return GetWindow(args[0]);} @Override public long method4(long[] args) {return ContextSensitiveHelp((int)args[0]);} @Override public long method5(long[] args) {return CanInPlaceActivate();} @Override public long method6(long[] args) {return OnInPlaceActivate();} @Override public long method7(long[] args) {return OnUIActivate();} @Override public long method8(long[] args) {return GetWindowContext(args[0], args[1], args[2], args[3], args[4]);} @Override public long method9(long[] args) {return Scroll(args[0]);} @Override public long method10(long[] args) {return OnUIDeactivate((int)args[0]);} @Override public long method11(long[] args) {return OnInPlaceDeactivate();} // method12 DiscardUndoState - not implemented // method13 DeactivateAndUndoChange - not implemented @Override public long method14(long[] args) {return OnPosRectChange(args[0]);} }; iOleDocumentSite = new COMObject(new int[]{2, 0, 0, 1}){ @Override public long method0(long[] args) {return QueryInterface(args[0], args[1]);} @Override public long method1(long[] args) {return AddRef();} @Override public long method2(long[] args) {return Release();} @Override public long method3(long[] args) {return ActivateMe(args[0]);} }; } protected IStorage createTempStorage() { long[] tempStorage = new long[1]; int grfMode = COM.STGM_READWRITE | COM.STGM_SHARE_EXCLUSIVE | COM.STGM_DELETEONRELEASE; int result = COM.StgCreateDocfile(null, grfMode, 0, tempStorage); if (result != COM.S_OK) OLE.error(OLE.ERROR_CANNOT_CREATE_FILE, result); return new IStorage(tempStorage[0]); } /** * Deactivates an active in-place object and discards the object's undo state. */ public void deactivateInPlaceClient() { if (objIOleInPlaceObject != null) { objIOleInPlaceObject.InPlaceDeactivate(); } } private void deleteTempStorage() { //Destroy this item's contents in the temp root IStorage. if (tempStorage != null){ tempStorage.Release(); } tempStorage = null; } protected void disposeCOMInterfaces() { if (iOleClientSite != null) iOleClientSite.dispose(); iOleClientSite = null; if (iAdviseSink != null) iAdviseSink.dispose(); iAdviseSink = null; if (iOleInPlaceSite != null) iOleInPlaceSite.dispose(); iOleInPlaceSite = null; if (iOleDocumentSite != null) iOleDocumentSite.dispose(); iOleDocumentSite = null; } /** * Requests that the OLE Document or ActiveX Control perform an action; actions are almost always * changes to the activation state. * * @param verb the operation that is requested. This is one of the OLE.OLEIVERB_ values * * @return an HRESULT value indicating the success of the operation request; OLE.S_OK indicates * success */ public int doVerb(int verb) { // Not all OLE clients (for example PowerPoint) can be set into the running state in the constructor. // The fix is to ensure that the client is in the running state before invoking any verb on it. if (state == STATE_NONE) { if (COM.OleRun(objIUnknown.getAddress()) == OLE.S_OK) state = STATE_RUNNING; } if (state == STATE_NONE || isStatic) return COM.E_FAIL; // See PR: 1FV9RZW RECT rect = new RECT(); OS.GetClientRect(handle, rect); int result = objIOleObject.DoVerb(verb, null, iOleClientSite.getAddress(), 0, handle, rect); if (state != STATE_RUNNING && inInit) { updateStorage(); inInit = false; } return result; } /** * Asks the OLE Document or ActiveX Control to execute a command from a standard * list of commands. The OLE Document or ActiveX Control must support the IOleCommandTarget * interface. The OLE Document or ActiveX Control does not have to support all the commands * in the standard list. To check if a command is supported, you can call queryStatus with * the cmdID. * * @param cmdID the ID of a command; these are the OLE.OLECMDID_ values - a small set of common * commands * @param options the optional flags; these are the OLE.OLECMDEXECOPT_ values * @param in the argument for the command * @param out the return value of the command * * @return an HRESULT value; OLE.S_OK is returned if successful * */ public int exec(int cmdID, int options, Variant in, Variant out) { if (objIOleCommandTarget == null) { long[] address = new long[1]; if (objIUnknown.QueryInterface(COM.IIDIOleCommandTarget, address) != COM.S_OK) return OLE.ERROR_INTERFACE_NOT_FOUND; objIOleCommandTarget = new IOleCommandTarget(address[0]); } long inAddress = 0; if (in != null){ inAddress = OS.GlobalAlloc(OS.GMEM_FIXED | OS.GMEM_ZEROINIT, VARIANT.sizeof); in.getData(inAddress); } long outAddress = 0; if (out != null){ outAddress = OS.GlobalAlloc(OS.GMEM_FIXED | OS.GMEM_ZEROINIT, VARIANT.sizeof); out.getData(outAddress); } int result = objIOleCommandTarget.Exec(null, cmdID, options, inAddress, outAddress); if (inAddress != 0){ COM.VariantClear(inAddress); OS.GlobalFree(inAddress); } if (outAddress != 0) { out.setData(outAddress); COM.VariantClear(outAddress); OS.GlobalFree(outAddress); } return result; } IDispatch getAutomationObject() { long[] ppvObject = new long[1]; if (objIUnknown.QueryInterface(COM.IIDIDispatch, ppvObject) != COM.S_OK) return null; return new IDispatch(ppvObject[0]); } protected GUID getClassID(String clientName) { // create a GUID struct to hold the result GUID guid = new GUID(); // create a null terminated array of char char[] buffer = null; if (clientName != null) { int count = clientName.length(); buffer = new char[count + 1]; clientName.getChars(0, count, buffer, 0); } if (COM.CLSIDFromProgID(buffer, guid) != COM.S_OK){ int result = COM.CLSIDFromString(buffer, guid); if (result != COM.S_OK) return null; } return guid; } private int GetContainer(long ppContainer) { /* Simple containers that do not support links to their embedded * objects probably do not need to implement this method. Instead, * they can return E_NOINTERFACE and set ppContainer to NULL. */ if (ppContainer != 0) OS.MoveMemory(ppContainer, new long[]{0}, C.PTR_SIZEOF); return COM.E_NOINTERFACE; } private SIZE getExtent() { SIZE sizel = new SIZE(); // get the current size of the embedded OLENatives object if (objIOleObject != null) { if ( objIViewObject2 != null && !COM.OleIsRunning(objIOleObject.getAddress())) { objIViewObject2.GetExtent(aspect, -1, 0, sizel); } else { objIOleObject.GetExtent(aspect, sizel); } } return xFormHimetricToPixels(sizel); } /** * Returns the indent value that would be used to compute the clipping area * of the active X object. * * NOTE: The indent value is no longer being used by the client site. * * @return the rectangle representing the indent */ public Rectangle getIndent() { return new Rectangle(indent.left, indent.right, indent.top, indent.bottom); } /** * Returns the program ID of the OLE Document or ActiveX Control. * * @return the program ID of the OLE Document or ActiveX Control */ public String getProgramID(){ return getProgID(appClsid); } String getProgID(GUID clsid) { if (clsid != null){ long[] lplpszProgID = new long[1]; if (COM.ProgIDFromCLSID(clsid, lplpszProgID) == COM.S_OK) { long hMem = lplpszProgID[0]; int length = OS.GlobalSize(hMem); long ptr = OS.GlobalLock(hMem); char[] buffer = new char[length]; OS.MoveMemory(buffer, ptr, length); OS.GlobalUnlock(hMem); OS.GlobalFree(hMem); String result = new String(buffer); // remove null terminator int index = result.indexOf("\0"); return result.substring(0, index); } } return null; } int ActivateMe(long pViewToActivate) { if (pViewToActivate == 0) { long[] ppvObject = new long[1]; if (objIUnknown.QueryInterface(COM.IIDIOleDocument, ppvObject) != COM.S_OK) return COM.E_FAIL; IOleDocument objOleDocument = new IOleDocument(ppvObject[0]); if (objOleDocument.CreateView(iOleInPlaceSite.getAddress(), 0, 0, ppvObject) != COM.S_OK) return COM.E_FAIL; objOleDocument.Release(); objDocumentView = new IOleDocumentView(ppvObject[0]); } else { objDocumentView = new IOleDocumentView(pViewToActivate); objDocumentView.AddRef(); objDocumentView.SetInPlaceSite(iOleInPlaceSite.getAddress()); } objDocumentView.UIActivate(1);//TRUE RECT rect = getRect(); objDocumentView.SetRect(rect); objDocumentView.Show(1);//TRUE return COM.S_OK; } protected int GetWindow(long phwnd) { if (phwnd == 0) return COM.E_INVALIDARG; if (frame == null) { OS.MoveMemory(phwnd, new long[] {0}, C.PTR_SIZEOF); return COM.E_NOTIMPL; } // Copy the Window's handle into the memory passed in OS.MoveMemory(phwnd, new long[] {handle}, C.PTR_SIZEOF); return COM.S_OK; } RECT getRect() { Rectangle area = DPIUtil.autoScaleUp(getClientArea()); // To Pixels RECT rect = new RECT(); rect.left = area.x; rect.top = area.y; rect.right = area.x + area.width; rect.bottom = area.y + area.height; return rect; } private int GetWindowContext(long ppFrame, long ppDoc, long lprcPosRect, long lprcClipRect, long lpFrameInfo) { if (frame == null || ppFrame == 0) return COM.E_NOTIMPL; // fill in frame handle long iOleInPlaceFrame = frame.getIOleInPlaceFrame(); OS.MoveMemory(ppFrame, new long[] {iOleInPlaceFrame}, C.PTR_SIZEOF); frame.AddRef(); // null out document handle if (ppDoc != 0) OS.MoveMemory(ppDoc, new long[] {0}, C.PTR_SIZEOF); // fill in position and clipping info RECT rect = getRect(); if (lprcPosRect != 0) OS.MoveMemory(lprcPosRect, rect, RECT.sizeof); if (lprcClipRect != 0) OS.MoveMemory(lprcClipRect, rect, RECT.sizeof); // get frame info OLEINPLACEFRAMEINFO frameInfo = new OLEINPLACEFRAMEINFO(); frameInfo.cb = OLEINPLACEFRAMEINFO.sizeof; frameInfo.fMDIApp = 0; frameInfo.hwndFrame = frame.handle; Shell shell = getShell(); Menu menubar = shell.getMenuBar(); if (menubar != null && !menubar.isDisposed()) { long hwnd = shell.handle; int cAccel = (int)OS.SendMessage(hwnd, OS.WM_APP, 0, 0); if (cAccel != 0) { long hAccel = OS.SendMessage(hwnd, OS.WM_APP+1, 0, 0); if (hAccel != 0) { frameInfo.cAccelEntries = cAccel; frameInfo.haccel = hAccel; } } } COM.MoveMemory(lpFrameInfo, frameInfo, OLEINPLACEFRAMEINFO.sizeof); return COM.S_OK; } boolean isICAClient() { return getProgramID().startsWith("Citrix.ICAClient"); //$NON-NLS-1$ } /** * Returns whether ole document is dirty by checking whether the content * of the file representing the document is dirty. * * @return true if the document has been modified, * false otherwise. * @since 3.1 */ public boolean isDirty() { /* * Note: this method must return true unless it is absolutely clear that the * contents of the Ole Document do not differ from the contents in the file * on the file system. */ // Get access to the persistent storage mechanism long[] address = new long[1]; if (objIOleObject.QueryInterface(COM.IIDIPersistFile, address) != COM.S_OK) return true; IPersistFile permStorage = new IPersistFile(address[0]); // Are the contents of the permanent storage different from the file? int result = permStorage.IsDirty(); permStorage.Release(); if (result == COM.S_FALSE) return false; return true; } @Override public boolean isFocusControl () { checkWidget (); long focusHwnd = OS.GetFocus(); if (objIOleInPlaceObject == null) return (handle == focusHwnd); long[] phwnd = new long[1]; objIOleInPlaceObject.GetWindow(phwnd); while (focusHwnd != 0) { if (phwnd[0] == focusHwnd) return true; focusHwnd = OS.GetParent(focusHwnd); } return false; } private boolean isOffice2007(boolean program) { String programID = getProgramID(); if (programID == null) return false; if (program) { int lastDot = programID.lastIndexOf('.'); if (lastDot != -1) { programID = programID.substring(0, lastDot); GUID guid = getClassID(programID); programID = getProgID(guid); if (programID == null) return false; } } if (programID.equals("Word.Document.12")) return true; //$NON-NLS-1$ if (programID.equals("Excel.Sheet.12")) return true; //$NON-NLS-1$ if (programID.equals("PowerPoint.Show.12")) return true; //$NON-NLS-1$ return false; } private int OnClose() { return COM.S_OK; } private int OnDataChange(long pFormatetc, long pStgmed) { return COM.S_OK; } private void onDispose(Event e) { inDispose = true; // remove listeners removeListener(SWT.Dispose, listener); removeListener(SWT.FocusIn, listener); removeListener(SWT.FocusOut, listener); removeListener(SWT.Paint, listener); removeListener(SWT.Traverse, listener); removeListener(SWT.KeyDown, listener); if (state != STATE_NONE) doVerb(OLE.OLEIVERB_DISCARDUNDOSTATE); deactivateInPlaceClient(); releaseObjectInterfaces(); // Note, must release object interfaces before releasing frame deleteTempStorage(); frame.removeListener(SWT.Resize, listener); frame.removeListener(SWT.Move, listener); frame.Release(); frame = null; } void onFocusIn(Event e) { if (inDispose) return; if (state != STATE_UIACTIVE) { long[] ppvObject = new long[1]; if (objIUnknown.QueryInterface(COM.IIDIOleInPlaceObject, ppvObject) == COM.S_OK) { IOleInPlaceObject objIOleInPlaceObject = new IOleInPlaceObject(ppvObject[0]); objIOleInPlaceObject.Release(); doVerb(OLE.OLEIVERB_SHOW); } } if (objIOleInPlaceObject == null) return; if (isFocusControl()) return; long[] phwnd = new long[1]; objIOleInPlaceObject.GetWindow(phwnd); if (phwnd[0] == 0) return; OS.SetFocus(phwnd[0]); } void onFocusOut(Event e) { } private int OnInPlaceActivate() { state = STATE_INPLACEACTIVE; frame.setCurrentDocument(this); if (objIOleObject == null) return COM.S_OK; long[] ppvObject = new long[1]; if (objIOleObject.QueryInterface(COM.IIDIOleInPlaceObject, ppvObject) == COM.S_OK) { objIOleInPlaceObject = new IOleInPlaceObject(ppvObject[0]); } return COM.S_OK; } private int OnInPlaceDeactivate() { if (objIOleInPlaceObject != null) objIOleInPlaceObject.Release(); objIOleInPlaceObject = null; state = STATE_RUNNING; redraw(); Shell shell = getShell(); if (isFocusControl() || frame.isFocusControl()) { shell.traverse(SWT.TRAVERSE_TAB_NEXT); } return COM.S_OK; } private int OnPosRectChange(long lprcPosRect) { Point size = DPIUtil.autoScaleUp(getSize()); // To Pixels setExtent(size.x, size.y); return COM.S_OK; } private void onPaint(Event e) { if (state == STATE_RUNNING || state == STATE_INPLACEACTIVE) { SIZE size = getExtent(); Rectangle area = DPIUtil.autoScaleUp(getClientArea()); // To Pixels RECT rect = new RECT(); if (getProgramID().startsWith("Excel.Sheet")) { //$NON-NLS-1$ rect.left = area.x; rect.right = area.x + (area.height * size.cx / size.cy); rect.top = area.y; rect.bottom = area.y + area.height; } else { rect.left = area.x; rect.right = area.x + size.cx; rect.top = area.y; rect.bottom = area.y + size.cy; } long pArea = OS.GlobalAlloc(COM.GMEM_FIXED | COM.GMEM_ZEROINIT, RECT.sizeof); OS.MoveMemory(pArea, rect, RECT.sizeof); COM.OleDraw(objIUnknown.getAddress(), aspect, e.gc.handle, pArea); OS.GlobalFree(pArea); } } private void onResize(Event e) { setBounds(); } private void OnSave() { } private int OnShowWindow(int fShow) { return COM.S_OK; } private int OnUIActivate() { if (objIOleInPlaceObject == null) return COM.E_FAIL; state = STATE_UIACTIVE; long[] phwnd = new long[1]; if (objIOleInPlaceObject.GetWindow(phwnd) == COM.S_OK) { OS.SetWindowPos(phwnd[0], OS.HWND_TOP, 0, 0, 0, 0, OS.SWP_NOSIZE | OS.SWP_NOMOVE); } return COM.S_OK; } int OnUIDeactivate(int fUndoable) { // currently, we are ignoring the fUndoable flag if (frame == null || frame.isDisposed()) return COM.S_OK; state = STATE_INPLACEACTIVE; frame.SetActiveObject(0,0); redraw(); Shell shell = getShell(); if (isFocusControl() || frame.isFocusControl()) { shell.traverse(SWT.TRAVERSE_TAB_NEXT); } Menu menubar = shell.getMenuBar(); if (menubar == null || menubar.isDisposed()) return COM.S_OK; long shellHandle = shell.handle; OS.SetMenu(shellHandle, menubar.handle); return COM.OleSetMenuDescriptor(0, shellHandle, 0, 0, 0); } private void onTraverse(Event event) { switch (event.detail) { case SWT.TRAVERSE_ESCAPE: case SWT.TRAVERSE_RETURN: case SWT.TRAVERSE_TAB_NEXT: case SWT.TRAVERSE_TAB_PREVIOUS: case SWT.TRAVERSE_PAGE_NEXT: case SWT.TRAVERSE_PAGE_PREVIOUS: case SWT.TRAVERSE_MNEMONIC: event.doit = true; break; } } private int OnViewChange(int dwAspect, int lindex) { return COM.S_OK; } protected int QueryInterface(long riid, long ppvObject) { if (riid == 0 || ppvObject == 0) return COM.E_NOINTERFACE; GUID guid = new GUID(); COM.MoveMemory(guid, riid, GUID.sizeof); if (COM.IsEqualGUID(guid, COM.IIDIUnknown) || COM.IsEqualGUID(guid, COM.IIDIOleClientSite)) { OS.MoveMemory(ppvObject, new long[] {iOleClientSite.getAddress()}, C.PTR_SIZEOF); AddRef(); return COM.S_OK; } if (COM.IsEqualGUID(guid, COM.IIDIAdviseSink)) { OS.MoveMemory(ppvObject, new long[] {iAdviseSink.getAddress()}, C.PTR_SIZEOF); AddRef(); return COM.S_OK; } if (COM.IsEqualGUID(guid, COM.IIDIOleInPlaceSite)) { OS.MoveMemory(ppvObject, new long[] {iOleInPlaceSite.getAddress()}, C.PTR_SIZEOF); AddRef(); return COM.S_OK; } if (COM.IsEqualGUID(guid, COM.IIDIOleDocumentSite )) { String progID = getProgramID(); if (!progID.startsWith("PowerPoint")) { //$NON-NLS-1$ OS.MoveMemory(ppvObject, new long[] {iOleDocumentSite.getAddress()}, C.PTR_SIZEOF); AddRef(); return COM.S_OK; } } OS.MoveMemory(ppvObject, new long[] {0}, C.PTR_SIZEOF); return COM.E_NOINTERFACE; } /** * Returns the status of the specified command. The status is any bitwise OR'd combination of * SWTOLE.OLECMDF_SUPPORTED, SWTOLE.OLECMDF_ENABLED, SWTOLE.OLECMDF_LATCHED, SWTOLE.OLECMDF_NINCHED. * You can query the status of a command before invoking it with OleClientSite.exec. The * OLE Document or ActiveX Control must support the IOleCommandTarget to make use of this method. * * @param cmd the ID of a command; these are the OLE.OLECMDID_ values - a small set of common * commands * * @return the status of the specified command or 0 if unable to query the OLE Object; these are the * OLE.OLECMDF_ values */ public int queryStatus(int cmd) { if (objIOleCommandTarget == null) { long[] address = new long[1]; if (objIUnknown.QueryInterface(COM.IIDIOleCommandTarget, address) != COM.S_OK) return 0; objIOleCommandTarget = new IOleCommandTarget(address[0]); } OLECMD olecmd = new OLECMD(); olecmd.cmdID = cmd; int result = objIOleCommandTarget.QueryStatus(null, 1, olecmd, 0); if (result != COM.S_OK) return 0; return olecmd.cmdf; } protected int Release() { refCount--; if (refCount == 0) { disposeCOMInterfaces(); } return refCount; } protected void releaseObjectInterfaces() { if (objIOleInPlaceObject!= null) objIOleInPlaceObject.Release(); objIOleInPlaceObject = null; if (objIOleObject != null) { objIOleObject.Close(COM.OLECLOSE_NOSAVE); objIOleObject.Release(); } objIOleObject = null; if (objDocumentView != null){ objDocumentView.Release(); } objDocumentView = null; if (objIViewObject2 != null) { objIViewObject2.SetAdvise(aspect, 0, 0); objIViewObject2.Release(); } objIViewObject2 = null; if (objIOleCommandTarget != null) objIOleCommandTarget.Release(); objIOleCommandTarget = null; if (objIUnknown != null){ objIUnknown.Release(); } objIUnknown = null; if (COM.FreeUnusedLibraries) { COM.CoFreeUnusedLibraries(); } } /** * Saves the document to the specified file and includes OLE specific information if specified. * This method must only be used for files that have an OLE Storage format. For example, * a word file edited with Word.Document should be saved using this method because there is * formating information that should be stored in the OLE specific Storage format. * * @param file the file to which the changes are to be saved * @param includeOleInfo the flag to indicate whether OLE specific information should be saved. * * @return true if the save was successful */ public boolean save(File file, boolean includeOleInfo) { /* * Bug in Office 2007. Saving Office 2007 documents to compound file storage object * causes the output file to be corrupted. The fix is to detect Office 2007 documents * using the program ID and save only the content of the 'Package' stream. */ if (isOffice2007(false)) { return saveOffice2007(file); } if (includeOleInfo) return saveToStorageFile(file); return saveToTraditionalFile(file); } private boolean saveFromContents(long address, File file) { boolean success = false; IStream tempContents = new IStream(address); tempContents.AddRef(); try { FileOutputStream writer = new FileOutputStream(file); int increment = 1024 * 4; long pv = OS.CoTaskMemAlloc(increment); int[] pcbWritten = new int[1]; while (tempContents.Read(pv, increment, pcbWritten) == COM.S_OK && pcbWritten[0] > 0) { byte[] buffer = new byte[ pcbWritten[0]]; OS.MoveMemory(buffer, pv, pcbWritten[0]); writer.write(buffer); // Note: if file does not exist, this will create the file the // first time it is called success = true; } OS.CoTaskMemFree(pv); writer.close(); } catch (IOException err) { } tempContents.Release(); return success; } private boolean saveFromOle10Native(long address, File file) { boolean success = false; IStream tempContents = new IStream(address); tempContents.AddRef(); // The "\1Ole10Native" stream contains a DWORD header whose value is the length // of the native data that follows. long pv = OS.CoTaskMemAlloc(4); int[] size = new int[1]; int rc = tempContents.Read(pv, 4, null); OS.MoveMemory(size, pv, 4); OS.CoTaskMemFree(pv); if (rc == COM.S_OK && size[0] > 0) { // Read the data byte[] buffer = new byte[size[0]]; pv = OS.CoTaskMemAlloc(size[0]); rc = tempContents.Read(pv, size[0], null); OS.MoveMemory(buffer, pv, size[0]); OS.CoTaskMemFree(pv); // open the file and write data into it try { FileOutputStream writer = new FileOutputStream(file); writer.write(buffer); // Note: if file does not exist, this will create the file writer.close(); success = true; } catch (IOException err) { } } tempContents.Release(); return success; } private int SaveObject() { updateStorage(); return COM.S_OK; } private boolean saveOffice2007(File file) { if (file == null || file.isDirectory()) return false; if (!updateStorage()) return false; boolean result = false; /* Excel fails to open the package stream when the PersistStorage is not in hands off mode */ long[] ppv = new long[1]; IPersistStorage iPersistStorage = null; if (objIUnknown.QueryInterface(COM.IIDIPersistStorage, ppv) == COM.S_OK) { iPersistStorage = new IPersistStorage(ppv[0]); tempStorage.AddRef(); iPersistStorage.HandsOffStorage(); } long[] address = new long[1]; int grfMode = COM.STGM_DIRECT | COM.STGM_READ | COM.STGM_SHARE_EXCLUSIVE; if (tempStorage.OpenStream("Package", 0, grfMode, 0, address) == COM.S_OK) { //$NON-NLS-1$ result = saveFromContents(address[0], file); } if (iPersistStorage != null) { iPersistStorage.SaveCompleted(tempStorage.getAddress()); tempStorage.Release(); iPersistStorage.Release(); } return result; } /** * Saves the document to the specified file and includes OLE specific information. This method * must only be used for files that have an OLE Storage format. For example, a word file * edited with Word.Document should be saved using this method because there is formating information * that should be stored in the OLE specific Storage format. * * @param file the file to which the changes are to be saved * * @return true if the save was successful */ private boolean saveToStorageFile(File file) { // The file will be saved using the formating of the current application - this // may not be the format of the application that was originally used to create the file // e.g. if an Excel file is opened in Word, the Word application will save the file in the // Word format // Note: if the file already exists, some applications will not overwrite the file // In these cases, you should delete the file first (probably save the contents of the file in case the // save fails) if (file == null || file.isDirectory()) return false; if (!updateStorage()) return false; // get access to the persistent storage mechanism long[] address = new long[1]; if (objIOleObject.QueryInterface(COM.IIDIPersistStorage, address) != COM.S_OK) return false; IPersistStorage permStorage = new IPersistStorage(address[0]); try { address = new long[1]; char[] path = (file.getAbsolutePath()+"\0").toCharArray(); int mode = COM.STGM_TRANSACTED | COM.STGM_READWRITE | COM.STGM_SHARE_EXCLUSIVE | COM.STGM_CREATE; int result = COM.StgCreateDocfile(path, mode, 0, address); //Does an AddRef if successful if (result != COM.S_OK) return false; IStorage storage = new IStorage(address[0]); try { if (COM.OleSave(permStorage.getAddress(), storage.getAddress(), false) == COM.S_OK) { if (storage.Commit(COM.STGC_DEFAULT) == COM.S_OK) { return true; } } } finally { storage.Release(); } } finally { permStorage.Release(); } return false; } /** * Saves the document to the specified file. This method must be used for * files that do not have an OLE Storage format. For example, a bitmap file edited with MSPaint * should be saved using this method because bitmap is a standard format that does not include any * OLE specific data. * * @param file the file to which the changes are to be saved * * @return true if the save was successful */ private boolean saveToTraditionalFile(File file) { // Note: if the file already exists, some applications will not overwrite the file // In these cases, you should delete the file first (probably save the contents of the file in case the // save fails) if (file == null || file.isDirectory()) return false; if (!updateStorage()) return false; long[] address = new long[1]; // Look for a CONTENTS stream if (tempStorage.OpenStream("CONTENTS", 0, COM.STGM_DIRECT | COM.STGM_READ | COM.STGM_SHARE_EXCLUSIVE, 0, address) == COM.S_OK) //$NON-NLS-1$ return saveFromContents(address[0], file); // Look for Ole 1.0 object stream if (tempStorage.OpenStream("\1Ole10Native", 0, COM.STGM_DIRECT | COM.STGM_READ | COM.STGM_SHARE_EXCLUSIVE, 0, address) == COM.S_OK) //$NON-NLS-1$ return saveFromOle10Native(address[0], file); return false; } private int Scroll(long scrollExtent) { return COM.S_OK; } void setBorderSpace(RECT newBorderwidth) { borderWidths = newBorderwidth; // readjust size and location of client site setBounds(); } void setBounds() { Rectangle area = DPIUtil.autoScaleUp(frame.getClientArea()); // To Pixels setBounds(DPIUtil.autoScaleDown(borderWidths.left), DPIUtil.autoScaleDown(borderWidths.top), DPIUtil.autoScaleDown(area.width - borderWidths.left - borderWidths.right), DPIUtil.autoScaleDown(area.height - borderWidths.top - borderWidths.bottom)); setObjectRects(); } private void setExtent(int width, int height){ // Resize the width and height of the embedded/linked OLENatives object // to the specified values. if (objIOleObject == null || isStatic || inUpdate) return; SIZE currentExtent = getExtent(); if (width == currentExtent.cx && height == currentExtent.cy) return; SIZE newExtent = new SIZE(); newExtent.cx = width; newExtent.cy = height; newExtent = xFormPixelsToHimetric(newExtent); // Get the server running first, then do a SetExtent, then show it boolean alreadyRunning = COM.OleIsRunning(objIOleObject.getAddress()); if (!alreadyRunning) COM.OleRun(objIOleObject.getAddress()); if (objIOleObject.SetExtent(aspect, newExtent) == COM.S_OK){ inUpdate = true; objIOleObject.Update(); inUpdate = false; if (!alreadyRunning) // Close server if it wasn't already running upon entering this method. objIOleObject.Close(COM.OLECLOSE_SAVEIFDIRTY); } } /** * The indent value is no longer being used by the client site. * * @param newIndent the rectangle representing the indent amount */ public void setIndent(Rectangle newIndent) { indent = new RECT(); indent.left = newIndent.x; indent.right = newIndent.width; indent.top = newIndent.y; indent.bottom = newIndent.height; } private void setObjectRects() { if (objIOleInPlaceObject == null) return; // size the object to fill the available space // leave a border RECT rect = getRect(); objIOleInPlaceObject.SetObjectRects(rect, rect); } private int ShowObject() { /* Tells the container to position the object so it is visible to * the user. This method ensures that the container itself is * visible and not minimized. */ setBounds(); return COM.S_OK; } /** * Displays a dialog with the property information for this OLE Object. The OLE Document or * ActiveX Control must support the ISpecifyPropertyPages interface. * * @param title the name that will appear in the titlebar of the dialog */ public void showProperties(String title) { // Get the Property Page information from the OLE Object long[] ppvObject = new long[1]; if (objIUnknown.QueryInterface(COM.IIDISpecifyPropertyPages, ppvObject) != COM.S_OK) return; ISpecifyPropertyPages objISPP = new ISpecifyPropertyPages(ppvObject[0]); CAUUID caGUID = new CAUUID(); int result = objISPP.GetPages(caGUID); objISPP.Release(); if (result != COM.S_OK) return; // create a frame in which to display the pages char[] chTitle = null; if (title != null) { chTitle = new char[title.length()]; title.getChars(0, title.length(), chTitle, 0); } result = COM.OleCreatePropertyFrame(frame.handle, 10, 10, chTitle, 1, new long[] {objIUnknown.getAddress()}, caGUID.cElems, caGUID.pElems, COM.LOCALE_USER_DEFAULT, 0, 0); // free the property page information OS.CoTaskMemFree(caGUID.pElems); } private boolean updateStorage() { if (tempStorage == null) return false; long[] ppv = new long[1]; if (objIUnknown.QueryInterface(COM.IIDIPersistStorage, ppv) != COM.S_OK) return false; IPersistStorage iPersistStorage = new IPersistStorage(ppv[0]); int result = COM.OleSave(iPersistStorage.getAddress(), tempStorage.getAddress(), true); if (result != COM.S_OK){ // OleSave will fail for static objects, so do what OleSave does. COM.WriteClassStg(tempStorage.getAddress(), objClsid); result = iPersistStorage.Save(tempStorage.getAddress(), true); } tempStorage.Commit(COM.STGC_DEFAULT); result = iPersistStorage.SaveCompleted(0); iPersistStorage.Release(); return true; } private SIZE xFormHimetricToPixels(SIZE aSize) { // Return a new Size which is the pixel transformation of a // size in HIMETRIC units. long hDC = OS.GetDC(0); int xppi = OS.GetDeviceCaps(hDC, 88); // logical pixels/inch in x int yppi = OS.GetDeviceCaps(hDC, 90); // logical pixels/inch in y OS.ReleaseDC(0, hDC); int cx = Compatibility.round(aSize.cx * xppi, 2540); // 2540 HIMETRIC units per inch int cy = Compatibility.round(aSize.cy * yppi, 2540); SIZE size = new SIZE(); size.cx = cx; size.cy = cy; return size; } private SIZE xFormPixelsToHimetric(SIZE aSize) { // Return a new size which is the HIMETRIC transformation of a // size in pixel units. long hDC = OS.GetDC(0); int xppi = OS.GetDeviceCaps(hDC, 88); // logical pixels/inch in x int yppi = OS.GetDeviceCaps(hDC, 90); // logical pixels/inch in y OS.ReleaseDC(0, hDC); int cx = Compatibility.round(aSize.cx * 2540, xppi); // 2540 HIMETRIC units per inch int cy = Compatibility.round(aSize.cy * 2540, yppi); SIZE size = new SIZE(); size.cx = cx; size.cy = cy; return size; } }