tdf#163340 replace list on paste
[LibreOffice.git] / embeddedobj / source / msole / olecomponent.cxx
bloba520781f14c975c8510df98a36c62dad7ef272d8
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <sal/config.h>
22 #include <com/sun/star/datatransfer/UnsupportedFlavorException.hpp>
23 #include <com/sun/star/lang/IllegalArgumentException.hpp>
24 #include <com/sun/star/lang/DisposedException.hpp>
25 #include <com/sun/star/embed/WrongStateException.hpp>
26 #include <com/sun/star/embed/UnreachableStateException.hpp>
27 #include <com/sun/star/embed/EmbedStates.hpp>
28 #include <com/sun/star/ucb/XSimpleFileAccess.hpp>
29 #include <com/sun/star/beans/XPropertySet.hpp>
30 #include <com/sun/star/io/TempFile.hpp>
31 #include <com/sun/star/io/XTruncate.hpp>
32 #include <com/sun/star/io/IOException.hpp>
33 #include <com/sun/star/awt/XRequestCallback.hpp>
35 #include "platform.h"
36 #include <comphelper/multicontainer2.hxx>
37 #include <comphelper/mimeconfighelper.hxx>
38 #include <comphelper/processfactory.hxx>
39 #include <comphelper/servicehelper.hxx>
40 #include <comphelper/windowserrorstring.hxx>
41 #include <osl/diagnose.h>
42 #include <osl/file.hxx>
43 #include <rtl/ref.hxx>
44 #include <o3tl/char16_t2wchar_t.hxx>
45 #include <o3tl/unit_conversion.hxx>
46 #include <systools/win32/comtools.hxx>
47 #include <vcl/svapp.hxx>
48 #include <vcl/threadex.hxx>
50 #include "graphconvert.hxx"
51 #include "olecomponent.hxx"
52 #include "olepersist.hxx"
53 #include "olewrapclient.hxx"
54 #include "advisesink.hxx"
55 #include <oleembobj.hxx>
56 #include "mtnotification.hxx"
57 #include <memory>
58 #include <string>
60 using namespace ::com::sun::star;
61 using namespace ::comphelper;
62 #define MAX_ENUM_ELE 20
63 #define FORMATS_NUM 3
65 FORMATETC const pFormatTemplates[FORMATS_NUM] = {
66 { CF_ENHMETAFILE, nullptr, 0, -1, TYMED_ENHMF },
67 { CF_METAFILEPICT, nullptr, 0, -1, TYMED_MFPICT },
68 { CF_BITMAP, nullptr, 0, -1, TYMED_GDI } };
71 // We have at least one single-threaded apartment (STA) in the process (the VCL Main thread, which
72 // is the GUI thread), and a multithreaded apartment (MTA) for most of other threads. OLE objects
73 // may be created in either: in interactive mode, this typically happens in the STA; when serving
74 // external requests, this may be either in STA (when explicit "OnMainThread" argument is passed to
75 // loadComponentFromURL, and the instantiation of the object happens during the load), or in MTA
76 // (the thread actually serving the incoming calls).
78 // The objects typically can only be used in the apartment where they were instantiated. This means
79 // that e.g. a call to IOleObject::Close will fail, if it is performed in a different thread, when
80 // it was started in the main thread. And vice versa, opening a document in a handler thread, then
81 // trying to interact with the OLE object in GUI would fail.
83 // To handle this, several workarounds were implemented in the past; the mentioned "OnMainThread"
84 // argument is one of these, allowing open document requests be processed not in the handler threads
85 // that received the request, but in the main thread which will then be used for interaction. Also
86 // OleComponent::GetExtent was changed to check if the first call to IDataObject::GetData failed
87 // with RPC_E_WRONG_THREAD, and then retry in the main thread.
89 // But ultimately every call to the OLE object needs such checks. E.g., failing to close the object
90 // keeps the server running, effectively leaking resources, until it crashes/freezes after multiple
91 // iterations.
93 // Currently, OleComponentNative_Impl is implemented using IGlobalInterfaceTable, which allows to
94 // register an object in process-global instance of the table from the thread that instantiated the
95 // object, and obtain a "cookie" (a number); and then use that cookie from any thread to access that
96 // object. The global table will do its magic to provide either the original object (when it is
97 // requested from the same apartment as used for its registration), or a "proxy", which will marshal
98 // all calls to the proper thread, transparently for the caller. This implementation should obsolete
99 // the previous workarounds (in theory).
101 // m_pGlobalTable is the reference to the global table.
102 // The storage object gets registered in the global table immediately when it's created.
103 // But the OLE object itself can't be registered immediately, before it is run: an attempt to call
104 // RegisterInterfaceInGlobal with such a newly created OLE object fails with CO_E_OBJNOTCONNECTED.
105 // Thus, the initial reference to the OLE object (which at this stage seems to be apartment-neutral)
106 // is stored to m_pObj. Only when it is run, it is registered in the global table.
108 // Indeed, the implicit change of the thread is a blocking operation, which opens a wonderful new
109 // opportunities for shiny deadlocks. Thus, precautions are needed to avoid them.
111 // When the OLE object is accessed by m_pObj (should be only in initialization code!), no measures
112 // are taken to change locking. But when it is accessed by getObj() - which may return the proxy -
113 // the calls are guarded by a SolarMutexReleaser, to allow the other thread do its job.
115 // There are at least two other mutexes in play here. One is in OleEmbeddedObject, that holds the
116 // OleComponent. The calls to OleComponent's methods are also wrapped there into unlock/lock pairs
117 // (see OleEmbeddedObject::changeState). The other is in OleComponent itself. For now, I see no
118 // deadlocks caused by that mutex, so no unlock/lock is introduced for that. It may turn out to be
119 // required eventually.
120 class OleComponentNative_Impl
122 public:
123 sal::systools::COMReference< IUnknown > m_pObj;
124 uno::Sequence< datatransfer::DataFlavor > m_aSupportedGraphFormats;
126 // The getters may return a proxy, that redirects the calls to another thread.
127 // Thus, calls to methods of returned objects must be inside solar mutex releaser.
128 auto getStorage() const { return getInterface<IStorage>(m_nStorage); }
129 auto getObj() const { return m_nOleObject ? getInterface<IUnknown>(m_nOleObject) : m_pObj; }
130 template <typename T>
131 auto get() const { return getObj().QueryInterface<T>(sal::systools::COM_QUERY); }
133 void registerStorage(IStorage* pStorage) { registerInterface(pStorage, m_nStorage); }
134 void registerObj() { registerInterface(m_pObj.get(), m_nOleObject); }
136 bool IsStorageRegistered() const { return m_nStorage != 0; }
138 OleComponentNative_Impl()
139 : m_pGlobalTable(CLSID_StdGlobalInterfaceTable, nullptr, CLSCTX_INPROC_SERVER)
141 // TODO: Extend format list
142 m_aSupportedGraphFormats = {
144 datatransfer::DataFlavor(
145 "application/x-openoffice-emf;windows_formatname=\"Image EMF\"",
146 "Windows Enhanced Metafile",
147 cppu::UnoType<uno::Sequence< sal_Int8 >>::get() ),
149 datatransfer::DataFlavor(
150 "application/x-openoffice-wmf;windows_formatname=\"Image WMF\"",
151 "Windows Metafile",
152 cppu::UnoType<uno::Sequence< sal_Int8 >>::get() ),
154 datatransfer::DataFlavor(
155 "application/x-openoffice-bitmap;windows_formatname=\"Bitmap\"",
156 "Bitmap",
157 cppu::UnoType<uno::Sequence< sal_Int8 >>::get() ),
159 datatransfer::DataFlavor(
160 "image/png",
161 "PNG",
162 cppu::UnoType<uno::Sequence< sal_Int8 >>::get() ),
164 datatransfer::DataFlavor(
165 "application/x-openoffice-gdimetafile;windows_formatname=\"GDIMetaFile\"",
166 "GDIMetafile",
167 cppu::UnoType<uno::Sequence< sal_Int8 >>::get() )
171 ~OleComponentNative_Impl()
173 if (m_nOleObject)
174 m_pGlobalTable->RevokeInterfaceFromGlobal(m_nOleObject);
175 if (m_nStorage)
176 m_pGlobalTable->RevokeInterfaceFromGlobal(m_nStorage);
179 bool ConvertDataForFlavor( const STGMEDIUM& aMedium,
180 const datatransfer::DataFlavor& aFlavor,
181 uno::Any& aResult );
183 bool GraphicalFlavor( const datatransfer::DataFlavor& aFlavor );
185 uno::Sequence< datatransfer::DataFlavor > GetFlavorsForAspects( sal_uInt32 nSupportedAspects );
187 sal::systools::COMReference<IStorage> CreateNewStorage(const OUString& url);
189 private:
190 sal::systools::COMReference<IGlobalInterfaceTable> m_pGlobalTable;
191 DWORD m_nStorage = 0;
192 DWORD m_nOleObject = 0;
194 template <typename T> sal::systools::COMReference<T> getInterface(DWORD cookie) const
196 sal::systools::COMReference<T> result;
197 HRESULT hr = m_pGlobalTable->GetInterfaceFromGlobal(cookie, IID_PPV_ARGS(&result));
198 SAL_WARN_IF(FAILED(hr), "embeddedobj.ole",
199 "GetInterfaceFromGlobal failed: is cookie " << cookie << " not registered?");
200 return result;
203 template <typename T> void registerInterface(T* pInterface, DWORD& cookie)
205 if (cookie != 0) // E.g., on subsequent RunObject calls
206 return;
207 HRESULT hr = m_pGlobalTable->RegisterInterfaceInGlobal(pInterface, __uuidof(T), &cookie);
208 SAL_WARN_IF(FAILED(hr), "embeddedobj.ole", "RegisterInterfaceInGlobal failed");
212 static DWORD GetAspectFromFlavor( const datatransfer::DataFlavor& aFlavor )
214 if ( aFlavor.MimeType.indexOf( ";Aspect=THUMBNAIL" ) != -1 )
215 return DVASPECT_THUMBNAIL;
216 else if ( aFlavor.MimeType.indexOf( ";Aspect=ICON" ) != -1 )
217 return DVASPECT_ICON;
218 else if ( aFlavor.MimeType.indexOf( ";Aspect=DOCPRINT" ) != -1 )
219 return DVASPECT_DOCPRINT;
220 else
221 return DVASPECT_CONTENT;
225 static OUString GetFlavorSuffixFromAspect( DWORD nAsp )
227 OUString aResult;
229 if ( nAsp == DVASPECT_THUMBNAIL )
230 aResult = ";Aspect=THUMBNAIL";
231 else if ( nAsp == DVASPECT_ICON )
232 aResult = ";Aspect=ICON";
233 else if ( nAsp == DVASPECT_DOCPRINT )
234 aResult = ";Aspect=DOCPRINT";
236 // no suffix for DVASPECT_CONTENT
238 return aResult;
242 bool OleComponentNative_Impl::ConvertDataForFlavor( const STGMEDIUM& aMedium,
243 const datatransfer::DataFlavor& aFlavor,
244 uno::Any& aResult )
246 bool bAnyIsReady = false;
248 // try to convert data from Medium format to specified Flavor format
249 if ( aFlavor.DataType == cppu::UnoType<uno::Sequence< sal_Int8 >>::get() )
251 // first the GDI-metafile must be generated
253 std::unique_ptr<sal_Int8[]> pBuf;
254 sal_uInt32 nBufSize = 0;
255 OUString aFormat;
257 if ( aMedium.tymed == TYMED_MFPICT ) // Win Metafile
259 aFormat = "image/x-wmf";
260 METAFILEPICT* pMF = static_cast<METAFILEPICT*>(GlobalLock( aMedium.hMetaFilePict ));
261 if ( pMF )
263 nBufSize = GetMetaFileBitsEx( pMF->hMF, 0, nullptr ) + 22;
264 pBuf.reset(new sal_Int8[nBufSize]);
267 // TODO/LATER: the unit size must be calculated correctly
268 *reinterpret_cast<long*>( pBuf.get() ) = 0x9ac6cdd7L;
269 *reinterpret_cast<short*>( pBuf.get()+6 ) = SHORT(0);
270 *reinterpret_cast<short*>( pBuf.get()+8 ) = SHORT(0);
271 *reinterpret_cast<short*>( pBuf.get()+10 ) = static_cast<SHORT>(pMF->xExt);
272 *reinterpret_cast<short*>( pBuf.get()+12 ) = static_cast<SHORT>(pMF->yExt);
273 *reinterpret_cast<short*>( pBuf.get()+14 ) = USHORT(2540);
276 if ( nBufSize && nBufSize == GetMetaFileBitsEx( pMF->hMF, nBufSize - 22, pBuf.get() + 22 ) )
278 if ( aFlavor.MimeType.match( "application/x-openoffice-wmf;windows_formatname=\"Image WMF\"" ) )
280 aResult <<= uno::Sequence< sal_Int8 >( pBuf.get(), nBufSize );
281 bAnyIsReady = true;
285 GlobalUnlock( aMedium.hMetaFilePict );
288 else if ( aMedium.tymed == TYMED_ENHMF ) // Enh Metafile
290 aFormat = "image/x-emf";
291 nBufSize = GetEnhMetaFileBits( aMedium.hEnhMetaFile, 0, nullptr );
292 pBuf.reset(new sal_Int8[nBufSize]);
293 if ( nBufSize && nBufSize == GetEnhMetaFileBits( aMedium.hEnhMetaFile, nBufSize, reinterpret_cast<LPBYTE>(pBuf.get()) ) )
295 if ( aFlavor.MimeType.match( "application/x-openoffice-emf;windows_formatname=\"Image EMF\"" ) )
297 aResult <<= uno::Sequence< sal_Int8 >( pBuf.get(), nBufSize );
298 bAnyIsReady = true;
302 else if ( aMedium.tymed == TYMED_GDI ) // Bitmap
304 aFormat = "image/x-MS-bmp";
306 // Find out size of buffer: deprecated GetBitmapBits does not have a mode to return
307 // required buffer size
308 BITMAP aBmp;
309 GetObjectW(aMedium.hBitmap, sizeof(aBmp), &aBmp);
310 nBufSize = aBmp.bmWidthBytes * aBmp.bmHeight;
312 pBuf.reset(new sal_Int8[nBufSize]);
313 if ( nBufSize && nBufSize == sal::static_int_cast< ULONG >( GetBitmapBits( aMedium.hBitmap, nBufSize, pBuf.get() ) ) )
315 if ( aFlavor.MimeType.match( "application/x-openoffice-bitmap;windows_formatname=\"Bitmap\"" ) )
317 aResult <<= uno::Sequence< sal_Int8 >( pBuf.get(), nBufSize );
318 bAnyIsReady = true;
323 if ( pBuf && !bAnyIsReady )
325 for (auto const& supportedFormat : m_aSupportedGraphFormats)
326 if ( aFlavor.MimeType.match( supportedFormat.MimeType )
327 && aFlavor.DataType == supportedFormat.DataType
328 && aFlavor.DataType == cppu::UnoType<uno::Sequence< sal_Int8 >>::get() )
330 bAnyIsReady = ConvertBufferToFormat( pBuf.get(), nBufSize, aFormat, aResult );
331 break;
336 return bAnyIsReady;
340 bool OleComponentNative_Impl::GraphicalFlavor( const datatransfer::DataFlavor& aFlavor )
342 // Actually all the required graphical formats must be supported
343 for (auto const& supportedFormat : m_aSupportedGraphFormats)
344 if ( aFlavor.MimeType.match( supportedFormat.MimeType )
345 && aFlavor.DataType == supportedFormat.DataType )
346 return true;
348 return false;
352 static bool GetClassIDFromSequence_Impl( uno::Sequence< sal_Int8 > const & aSeq, CLSID& aResult )
354 if ( aSeq.getLength() == 16 )
356 aResult.Data1 = ( ( ( ( ( static_cast<sal_uInt8>(aSeq[0]) << 8 ) + static_cast<sal_uInt8>(aSeq[1]) ) << 8 ) + static_cast<sal_uInt8>(aSeq[2]) ) << 8 ) + static_cast<sal_uInt8>(aSeq[3]);
357 aResult.Data2 = ( static_cast<sal_uInt8>(aSeq[4]) << 8 ) + static_cast<sal_uInt8>(aSeq[5]);
358 aResult.Data3 = ( static_cast<sal_uInt8>(aSeq[6]) << 8 ) + static_cast<sal_uInt8>(aSeq[7]);
359 for( int nInd = 0; nInd < 8; nInd++ )
360 aResult.Data4[nInd] = static_cast<sal_uInt8>(aSeq[nInd+8]);
362 return true;
365 return false;
369 static OUString WinAccToVcl_Impl( const sal_Unicode* pStr )
371 OUString aResult;
373 if( pStr )
375 while ( *pStr )
377 if ( *pStr == '&' )
379 aResult += "~";
380 while( *( ++pStr ) == '&' );
382 else
384 aResult += OUStringChar( *pStr );
385 pStr++;
390 return aResult;
394 OleComponent::OleComponent( const uno::Reference< uno::XComponentContext >& xContext, OleEmbeddedObject* pUnoOleObject )
395 : m_pInterfaceContainer( nullptr )
396 , m_bDisposed( false )
397 , m_bModified( false )
398 , m_pNativeImpl( std::make_unique<OleComponentNative_Impl>() )
399 , m_pUnoOleObject( pUnoOleObject )
400 , m_pOleWrapClientSite( nullptr )
401 , m_pImplAdviseSink( nullptr )
402 , m_xContext( xContext )
403 , m_bOleInitialized( false )
404 , m_bWorkaroundActive( false )
406 OSL_ENSURE( m_pUnoOleObject, "No owner object is provided!" );
408 HRESULT hr = OleInitialize( nullptr );
409 if ( hr == S_OK || hr == S_FALSE )
410 m_bOleInitialized = true;
411 else
413 SAL_WARN("embeddedobj.ole", "OleComponent ctor: OleInitialize() failed with 0x"
414 << OUString::number(static_cast<sal_uInt32>(hr), 16) << ": "
415 << WindowsErrorStringFromHRESULT(hr));
418 m_pOleWrapClientSite = new OleWrapperClientSite( this );
419 m_pOleWrapClientSite->AddRef();
421 m_pImplAdviseSink = new OleWrapperAdviseSink( this );
422 m_pImplAdviseSink->AddRef();
427 OleComponent::~OleComponent()
429 OSL_ENSURE( !m_pOleWrapClientSite && !m_pImplAdviseSink && !m_pInterfaceContainer && !m_bOleInitialized,
430 "The object was not closed successfully! DISASTER is possible!" );
432 if ( m_pOleWrapClientSite || m_pImplAdviseSink || m_pInterfaceContainer || m_bOleInitialized )
434 osl_atomic_increment(&m_refCount);
435 try {
436 Dispose();
437 } catch( const uno::Exception& ) {}
441 void OleComponent::Dispose()
443 if ( m_bDisposed )
444 return;
446 // Call CloseObject() without m_aMutex locked, since it will call
447 // IOleObject::Close(), which can call event listeners, which can run on a
448 // different thread.
449 CloseObject();
451 osl::MutexGuard aGuard(m_aMutex);
452 if ( m_pOleWrapClientSite )
454 m_pOleWrapClientSite->disconnectOleComponent();
455 m_pOleWrapClientSite->Release();
456 m_pOleWrapClientSite = nullptr;
459 if ( m_pImplAdviseSink )
461 m_pImplAdviseSink->disconnectOleComponent();
462 m_pImplAdviseSink->Release();
463 m_pImplAdviseSink = nullptr;
466 if ( m_pInterfaceContainer )
468 lang::EventObject aEvent( static_cast< ::cppu::OWeakObject* >( this ) );
469 m_pInterfaceContainer->disposeAndClear( aEvent );
471 delete m_pInterfaceContainer;
472 m_pInterfaceContainer = nullptr;
475 if ( m_bOleInitialized )
477 // since the disposing can happen not only from main thread but also from a clipboard
478 // the deinitialization might lead to a disaster, SO7 does not deinitialize OLE at all
479 // so currently the same approach is selected as workaround
480 // OleUninitialize();
481 m_bOleInitialized = false;
484 m_bDisposed = true;
488 void OleComponent::disconnectEmbeddedObject()
490 // must not be called from destructor of UNO OLE object!!!
491 osl::MutexGuard aGuard( m_aMutex );
492 m_pUnoOleObject = nullptr;
496 OUString OleComponent::getTempURL() const
498 OSL_ENSURE( m_pUnoOleObject, "Unexpected object absence!" );
499 if ( m_pUnoOleObject )
500 return m_pUnoOleObject->CreateTempURLEmpty_Impl();
501 else
502 return GetNewTempFileURL_Impl(m_xContext);
506 sal::systools::COMReference<IStorage> OleComponentNative_Impl::CreateNewStorage(const OUString& url)
508 if (IsStorageRegistered())
509 throw io::IOException(); // TODO:the object is already initialized
510 // TODO: in future a global memory could be used instead of file.
512 // write the stream to the temporary file
513 if (url.isEmpty())
514 throw uno::RuntimeException(); // TODO
516 // open an IStorage based on the temporary file
517 OUString aTempFilePath;
518 if (osl::FileBase::getSystemPathFromFileURL(url, aTempFilePath) != osl::FileBase::E_None)
519 throw uno::RuntimeException(); // TODO: something dangerous happened
521 sal::systools::COMReference<IStorage> pStorage;
522 HRESULT hr = StgCreateDocfile( o3tl::toW(aTempFilePath.getStr()), STGM_CREATE | STGM_READWRITE | STGM_TRANSACTED | STGM_DELETEONRELEASE, 0, &pStorage );
523 if (FAILED(hr) || !pStorage)
524 throw io::IOException(); // TODO: transport error code?
525 registerStorage(pStorage);
526 return pStorage;
530 uno::Sequence< datatransfer::DataFlavor > OleComponentNative_Impl::GetFlavorsForAspects( sal_uInt32 nSupportedAspects )
532 uno::Sequence< datatransfer::DataFlavor > aResult;
533 for ( sal_uInt32 nAsp = 1; nAsp <= 8; nAsp *= 2 )
534 if ( ( nSupportedAspects & nAsp ) == nAsp )
536 OUString aAspectSuffix = GetFlavorSuffixFromAspect( nAsp );
538 sal_Int32 nLength = aResult.getLength();
539 aResult.realloc( nLength + m_aSupportedGraphFormats.getLength() );
540 auto pResult = aResult.getArray();
542 for ( sal_Int32 nInd = 0; nInd < m_aSupportedGraphFormats.getLength(); nInd++ )
544 pResult[nLength + nInd].MimeType = m_aSupportedGraphFormats[nInd].MimeType + aAspectSuffix;
545 pResult[nLength + nInd].HumanPresentableName = m_aSupportedGraphFormats[nInd].HumanPresentableName;
546 pResult[nLength + nInd].DataType = m_aSupportedGraphFormats[nInd].DataType;
550 return aResult;
554 void OleComponent::RetrieveObjectDataFlavors_Impl()
556 if (!m_pNativeImpl->m_pObj)
557 throw embed::WrongStateException(); // TODO: the object is in wrong state
559 if ( !m_aDataFlavors.getLength() )
561 if (auto pDataObject = m_pNativeImpl->get<IDataObject>())
563 HRESULT hr;
564 sal::systools::COMReference< IEnumFORMATETC > pFormatEnum;
566 SolarMutexReleaser releaser;
567 hr = pDataObject->EnumFormatEtc(DATADIR_GET, &pFormatEnum);
569 if ( SUCCEEDED( hr ) && pFormatEnum )
571 FORMATETC pElem[ MAX_ENUM_ELE ];
572 ULONG nNum = 0;
574 // if it is possible to retrieve at least one supported graphical format for an aspect
575 // this format can be converted to other supported formats
576 sal_uInt32 nSupportedAspects = 0;
579 HRESULT hr2 = pFormatEnum->Next( MAX_ENUM_ELE, pElem, &nNum );
580 if( hr2 == S_OK || hr2 == S_FALSE )
582 for( sal_uInt32 nInd = 0; nInd < FORMATS_NUM; nInd++ )
584 if ( pElem[nInd].cfFormat == pFormatTemplates[nInd].cfFormat
585 && pElem[nInd].tymed == pFormatTemplates[nInd].tymed )
586 nSupportedAspects |= pElem[nInd].dwAspect;
589 else
590 break;
592 while( nNum == MAX_ENUM_ELE );
594 m_aDataFlavors = m_pNativeImpl->GetFlavorsForAspects( nSupportedAspects );
598 if ( !m_aDataFlavors.getLength() )
600 // TODO:
601 // for any reason the object could not provide this information
602 // try to get access to the cached representation
608 void OleComponent::InitializeObject_Impl()
609 // There will be no static objects!
611 if ( !m_pNativeImpl->m_pObj )
612 throw embed::WrongStateException();
614 // the linked object will be detected here
615 OSL_ENSURE( m_pUnoOleObject, "Unexpected object absence!" );
616 if ( m_pUnoOleObject )
617 m_pUnoOleObject->SetObjectIsLink_Impl( m_pNativeImpl->m_pObj.QueryInterface<IOleLink>(sal::systools::COM_QUERY).is() );
619 auto pViewObject2(m_pNativeImpl->m_pObj.QueryInterface<IViewObject2>(sal::systools::COM_QUERY));
620 if (!pViewObject2)
621 throw uno::RuntimeException(); // TODO
623 // remove all the caches
624 if ( sal::systools::COMReference< IOleCache > pIOleCache{ m_pNativeImpl->m_pObj, sal::systools::COM_QUERY } )
626 IEnumSTATDATA* pEnumSD = nullptr;
627 HRESULT hr2 = pIOleCache->EnumCache( &pEnumSD );
629 if ( SUCCEEDED( hr2 ) && pEnumSD )
631 pEnumSD->Reset();
632 STATDATA aSD;
633 DWORD nNum;
634 while( SUCCEEDED( pEnumSD->Next( 1, &aSD, &nNum ) ) && nNum == 1 )
635 hr2 = pIOleCache->Uncache( aSD.dwConnection );
638 // No IDataObject implementation, caching must be used instead
639 DWORD nConn;
640 FORMATETC aFormat = { 0, nullptr, DVASPECT_CONTENT, -1, TYMED_MFPICT };
641 hr2 = pIOleCache->Cache( &aFormat, ADVFCACHE_ONSAVE, &nConn );
644 auto pOleObject(m_pNativeImpl->m_pObj.QueryInterface<IOleObject>(sal::systools::COM_QUERY));
645 if (!pOleObject)
646 throw uno::RuntimeException(); // Static objects are not supported, they should be inserted as graphics
648 DWORD nOLEMiscFlags(0);
649 pOleObject->GetMiscStatus(DVASPECT_CONTENT, reinterpret_cast<DWORD*>(&nOLEMiscFlags));
650 // TODO: use other misc flags also
651 // the object should have drawable aspect even in case it supports only iconic representation
652 // if ( nOLEMiscFlags & OLEMISC_ONLYICONIC )
654 pOleObject->SetClientSite(m_pOleWrapClientSite);
656 // the only need in this registration is workaround for close notification
657 DWORD nAdvConn(0);
658 pOleObject->Advise(m_pImplAdviseSink, reinterpret_cast<DWORD*>(&nAdvConn));
659 pViewObject2->SetAdvise(DVASPECT_CONTENT, 0, m_pImplAdviseSink);
661 OleSetContainedObject(pOleObject, TRUE);
664 namespace
666 HRESULT OleLoadSeh(LPSTORAGE pIStorage, IUnknown** ppObj)
668 HRESULT hr = E_FAIL;
669 // tdf#119039: there is a nasty bug in OleLoad, that may call an unpaired
670 // IUnknown::Release on pIStorage on STG_E_FILENOTFOUND: see
671 // https://developercommunity.visualstudio.com/t/10144795
672 // Workaround it here to avoid crash in smart COM pointer destructor that
673 // would try to release already released object. Since we don't know if
674 // the bug appears each time STG_E_FILENOTFOUND is returned, this might
675 // potentially leak the storage object.
676 if (pIStorage)
677 pIStorage->AddRef();
679 __try {
680 hr = OleLoad(pIStorage, IID_IUnknown, nullptr, IID_PPV_ARGS_Helper(ppObj));
681 } __except( EXCEPTION_EXECUTE_HANDLER ) {
682 hr = E_FAIL;
684 if (pIStorage && hr != STG_E_FILENOTFOUND)
685 pIStorage->Release();
687 return hr;
691 void OleComponent::LoadEmbeddedObject( const OUString& aTempURL )
693 if ( !aTempURL.getLength() )
694 throw lang::IllegalArgumentException(); // TODO
696 if (m_pNativeImpl->IsStorageRegistered())
697 throw io::IOException(); // TODO the object is already initialized or wrong initialization is done
699 // open an IStorage based on the temporary file
700 OUString aFilePath;
701 if (osl::FileBase::getSystemPathFromFileURL(aTempURL, aFilePath) != ::osl::FileBase::E_None)
702 throw uno::RuntimeException(); // TODO: something dangerous happened
704 sal::systools::COMReference<IStorage> pStorage;
705 HRESULT hr = StgOpenStorage(o3tl::toW(aFilePath.getStr()), nullptr,
706 STGM_READWRITE | STGM_TRANSACTED, // | STGM_DELETEONRELEASE,
707 nullptr, 0, &pStorage);
708 if (FAILED(hr) || !pStorage)
709 throw io::IOException(); // TODO: transport error code?
711 m_pNativeImpl->registerStorage(pStorage);
713 hr = OleLoadSeh(pStorage, &m_pNativeImpl->m_pObj);
714 if (FAILED(hr))
715 throw uno::RuntimeException();
717 InitializeObject_Impl();
721 void OleComponent::CreateObjectFromClipboard()
723 auto pStorage(m_pNativeImpl->CreateNewStorage(getTempURL()));
724 if (!pStorage)
725 throw uno::RuntimeException(); // TODO
727 IDataObject * pDO = nullptr;
728 HRESULT hr = OleGetClipboard( &pDO );
729 if (FAILED(hr))
730 throw uno::RuntimeException();
732 hr = OleQueryCreateFromData(pDO);
733 if (S_OK == hr)
735 hr = OleCreateFromData( pDO,
736 IID_IUnknown,
737 OLERENDER_DRAW, // OLERENDER_FORMAT
738 nullptr, // &aFormat,
739 nullptr,
740 pStorage,
741 IID_PPV_ARGS_Helper(&m_pNativeImpl->m_pObj) );
742 if (FAILED(hr))
743 throw uno::RuntimeException();
745 else
747 // Static objects are not supported
748 pDO->Release();
751 InitializeObject_Impl();
755 void OleComponent::CreateNewEmbeddedObject( const uno::Sequence< sal_Int8 >& aSeqCLSID )
757 CLSID aClsID;
759 if ( !GetClassIDFromSequence_Impl( aSeqCLSID, aClsID ) )
760 throw lang::IllegalArgumentException(); // TODO
762 auto pStorage(m_pNativeImpl->CreateNewStorage(getTempURL()));
763 if (!pStorage)
764 throw uno::RuntimeException(); // TODO
766 // FORMATETC aFormat = { CF_METAFILEPICT, NULL, nAspect, -1, TYMED_MFPICT }; // for OLE..._DRAW should be NULL
768 HRESULT hr = OleCreate( aClsID,
769 IID_IUnknown,
770 OLERENDER_DRAW, // OLERENDER_FORMAT
771 nullptr, // &aFormat,
772 nullptr,
773 pStorage,
774 IID_PPV_ARGS_Helper(&m_pNativeImpl->m_pObj) );
775 if (FAILED(hr))
776 throw uno::RuntimeException(); // TODO
778 InitializeObject_Impl();
780 // TODO: getExtent???
784 void OleComponent::CreateObjectFromData( const uno::Reference< datatransfer::XTransferable >& )
785 // Static objects are not supported, they should be inserted as graphics
787 // TODO: May be this call is useless since there are no static objects
788 // and nonstatic objects will be created based on OLEstorage ( stream ).
789 // ???
791 // OleQueryCreateFromData...
795 void OleComponent::CreateObjectFromFile( const OUString& aFileURL )
797 auto pStorage(m_pNativeImpl->CreateNewStorage(getTempURL()));
798 if (!pStorage)
799 throw uno::RuntimeException(); // TODO:
801 OUString aFilePath;
802 if ( ::osl::FileBase::getSystemPathFromFileURL( aFileURL, aFilePath ) != ::osl::FileBase::E_None )
803 throw uno::RuntimeException(); // TODO: something dangerous happened
805 HRESULT hr = OleCreateFromFile( CLSID_NULL,
806 o3tl::toW(aFilePath.getStr()),
807 IID_IUnknown,
808 OLERENDER_DRAW, // OLERENDER_FORMAT
809 nullptr,
810 nullptr,
811 pStorage,
812 IID_PPV_ARGS_Helper(&m_pNativeImpl->m_pObj) );
813 if (FAILED(hr))
814 throw uno::RuntimeException(); // TODO
816 InitializeObject_Impl();
820 void OleComponent::CreateLinkFromFile( const OUString& aFileURL )
822 auto pStorage(m_pNativeImpl->CreateNewStorage(getTempURL()));
823 if (!pStorage)
824 throw uno::RuntimeException(); // TODO:
826 OUString aFilePath;
827 if ( ::osl::FileBase::getSystemPathFromFileURL( aFileURL, aFilePath ) != ::osl::FileBase::E_None )
828 throw uno::RuntimeException(); // TODO: something dangerous happened
830 HRESULT hr = OleCreateLinkToFile( o3tl::toW(aFilePath.getStr()),
831 IID_IUnknown,
832 OLERENDER_DRAW, // OLERENDER_FORMAT
833 nullptr,
834 nullptr,
835 pStorage,
836 IID_PPV_ARGS_Helper(&m_pNativeImpl->m_pObj) );
837 if (FAILED(hr))
838 throw uno::RuntimeException(); // TODO
840 InitializeObject_Impl();
844 void OleComponent::InitEmbeddedCopyOfLink( rtl::Reference<OleComponent> const & pOleLinkComponent )
846 if (!pOleLinkComponent)
847 throw lang::IllegalArgumentException(); // TODO
849 auto pOleLinkComponentObj(pOleLinkComponent->m_pNativeImpl->getObj());
850 if (!pOleLinkComponentObj)
851 throw lang::IllegalArgumentException();
853 // the object must be already disconnected from the temporary URL
854 auto pStorage(m_pNativeImpl->CreateNewStorage(getTempURL()));
856 SolarMutexReleaser releaser;
858 auto pDataObject(pOleLinkComponentObj.QueryInterface<IDataObject>(sal::systools::COM_QUERY));
859 if ( pDataObject && SUCCEEDED( OleQueryCreateFromData( pDataObject ) ) )
861 if (!pStorage)
862 throw uno::RuntimeException(); // TODO:
864 OleCreateFromData( pDataObject,
865 IID_IUnknown,
866 OLERENDER_DRAW,
867 nullptr,
868 nullptr,
869 pStorage,
870 IID_PPV_ARGS_Helper(&m_pNativeImpl->m_pObj) );
873 if ( !m_pNativeImpl->m_pObj )
875 auto pOleLink(pOleLinkComponentObj.QueryInterface<IOleLink>(sal::systools::COM_QUERY));
876 if ( !pOleLink )
877 throw io::IOException(); // TODO: the object doesn't support IOleLink
879 sal::systools::COMReference< IMoniker > pMoniker;
880 HRESULT hr = pOleLink->GetSourceMoniker( &pMoniker );
881 if ( FAILED( hr ) || !pMoniker )
882 throw io::IOException(); // TODO: can not retrieve moniker
884 // In case of file moniker life is easy : )
885 DWORD aMonType = 0;
886 hr = pMoniker->IsSystemMoniker( &aMonType );
887 if ( SUCCEEDED( hr ) && aMonType == MKSYS_FILEMONIKER )
889 sal::systools::COMReference< IMalloc > pMalloc;
890 hr = CoGetMalloc( 1, &pMalloc ); // if fails there will be a memory leak
891 OSL_ENSURE(SUCCEEDED(hr) && pMalloc, "CoGetMalloc() failed!");
893 LPOLESTR pOleStr = nullptr;
894 hr = pOleLink->GetSourceDisplayName( &pOleStr );
895 if ( SUCCEEDED( hr ) && pOleStr )
897 std::wstring aFilePath( pOleStr );
898 if ( pMalloc )
899 pMalloc->Free( pOleStr );
901 hr = OleCreateFromFile( CLSID_NULL,
902 aFilePath.c_str(),
903 IID_IUnknown,
904 OLERENDER_DRAW, // OLERENDER_FORMAT
905 nullptr,
906 nullptr,
907 pStorage,
908 IID_PPV_ARGS_Helper(&m_pNativeImpl->m_pObj) );
912 // in case of other moniker types the only way is to get storage
913 if ( !m_pNativeImpl->m_pObj )
915 sal::systools::COMReference< IBindCtx > pBindCtx;
916 hr = CreateBindCtx( 0, &pBindCtx );
917 if ( SUCCEEDED( hr ) && pBindCtx )
919 sal::systools::COMReference< IStorage > pObjectStorage;
920 hr = pMoniker->BindToStorage(pBindCtx, nullptr, IID_PPV_ARGS(&pObjectStorage));
921 if ( SUCCEEDED( hr ) && pObjectStorage )
923 hr = pObjectStorage->CopyTo(0, nullptr, nullptr, pStorage);
924 if ( SUCCEEDED( hr ) )
925 hr = OleLoadSeh(pStorage, &m_pNativeImpl->m_pObj);
931 InitializeObject_Impl();
935 void OleComponent::RunObject()
937 auto pOleObject(m_pNativeImpl->get<IOleObject>());
938 OSL_ENSURE(pOleObject, "The pointer can not be set to NULL here!");
939 if (!pOleObject)
940 throw embed::WrongStateException(); // TODO: the object is in wrong state
942 if (!OleIsRunning(pOleObject))
944 HRESULT hr = OleRun( m_pNativeImpl->m_pObj );
946 if ( FAILED( hr ) )
948 OUString error = WindowsErrorStringFromHRESULT(hr);
949 if ( hr == REGDB_E_CLASSNOTREG )
951 if (auto pOleObj
952 = m_pNativeImpl->m_pObj.QueryInterface<IOleObject>(sal::systools::COM_QUERY))
954 LPOLESTR lpUserType = nullptr;
955 if (SUCCEEDED(pOleObj->GetUserType(USERCLASSTYPE_FULL, &lpUserType)))
957 error += OUString::Concat("\n") + o3tl::toU(lpUserType);
958 sal::systools::COMReference<IMalloc> pMalloc;
959 hr = CoGetMalloc(1, &pMalloc); // if fails there will be a memory leak
960 SAL_WARN_IF(FAILED(hr) || !pMalloc, "embeddedobj.ole", "CoGetMalloc() failed");
961 if (pMalloc)
962 pMalloc->Free(lpUserType);
965 throw embed::UnreachableStateException(
966 error, getXWeak(), -1,
967 css::embed::EmbedStates::RUNNING); // the object server is not installed
969 else
970 throw io::IOException(error, getXWeak());
972 // Only now, when the object is activated, it can be registered in the global table;
973 // before this point, RegisterInterfaceInGlobal would return CO_E_OBJNOTCONNECTED
974 m_pNativeImpl->registerObj();
979 awt::Size OleComponent::CalculateWithFactor( const awt::Size& aSize,
980 const awt::Size& aMultiplier,
981 const awt::Size& aDivisor )
983 awt::Size aResult;
985 sal_Int64 nWidth = static_cast<sal_Int64>(aSize.Width) * static_cast<sal_Int64>(aMultiplier.Width) / static_cast<sal_Int64>(aDivisor.Width);
986 sal_Int64 nHeight = static_cast<sal_Int64>(aSize.Height) * static_cast<sal_Int64>(aMultiplier.Height) / static_cast<sal_Int64>(aDivisor.Height);
987 OSL_ENSURE( nWidth < SAL_MAX_INT32 && nWidth > SAL_MIN_INT32
988 && nHeight < SAL_MAX_INT32 && nHeight > SAL_MIN_INT32,
989 "Unacceptable result size!" );
991 aResult.Width = static_cast<sal_Int32>(nWidth);
992 aResult.Height = static_cast<sal_Int32>(nHeight);
994 return aResult;
998 void OleComponent::CloseObject()
1000 auto pOleObject(m_pNativeImpl->get<IOleObject>());
1001 if (pOleObject && OleIsRunning(pOleObject))
1003 SolarMutexReleaser releaser;
1004 HRESULT hr = pOleObject->Close(OLECLOSE_NOSAVE); // must be saved before
1005 SAL_WARN_IF(FAILED(hr), "embeddedobj.ole", "IOleObject::Close failed");
1010 uno::Sequence< embed::VerbDescriptor > OleComponent::GetVerbList()
1012 auto pOleObject(m_pNativeImpl->get<IOleObject>());
1013 if (!pOleObject)
1014 throw embed::WrongStateException(); // TODO: the object is in wrong state
1016 if( !m_aVerbList.getLength() )
1018 sal::systools::COMReference< IEnumOLEVERB > pEnum;
1019 HRESULT hr;
1021 SolarMutexReleaser releaser;
1022 hr = pOleObject->EnumVerbs(&pEnum);
1024 if (SUCCEEDED(hr))
1026 OLEVERB szEle[ MAX_ENUM_ELE ];
1027 ULONG nNum = 0;
1028 sal_Int32 nSeqSize = 0;
1032 hr = pEnum->Next(MAX_ENUM_ELE, szEle, &nNum);
1033 if( hr == S_OK || hr == S_FALSE )
1035 m_aVerbList.realloc( nSeqSize += nNum );
1036 auto pVerbList = m_aVerbList.getArray();
1037 for( sal_uInt32 nInd = 0; nInd < nNum; nInd++ )
1039 pVerbList[nSeqSize-nNum+nInd].VerbID = szEle[ nInd ].lVerb;
1040 pVerbList[nSeqSize-nNum+nInd].VerbName = WinAccToVcl_Impl( o3tl::toU(szEle[ nInd ].lpszVerbName) );
1041 pVerbList[nSeqSize-nNum+nInd].VerbFlags = szEle[ nInd ].fuFlags;
1042 pVerbList[nSeqSize-nNum+nInd].VerbAttributes = szEle[ nInd ].grfAttribs;
1045 else
1046 break;
1048 while( nNum == MAX_ENUM_ELE );
1052 return m_aVerbList;
1056 void OleComponent::ExecuteVerb( sal_Int32 nVerbID )
1058 RunObject();
1060 auto pOleObject(m_pNativeImpl->get<IOleObject>());
1061 if (!pOleObject)
1062 throw embed::WrongStateException(); // TODO
1064 SolarMutexReleaser releaser;
1066 // TODO: probably extents should be set here and stored in aRect
1067 // TODO: probably the parent window also should be set
1068 HRESULT hr = pOleObject->DoVerb(nVerbID, nullptr, m_pOleWrapClientSite, 0, nullptr, nullptr);
1070 if ( FAILED( hr ) )
1071 throw io::IOException(); // TODO
1075 void OleComponent::SetHostName( const OUString& aEmbDocName )
1077 auto pOleObject(m_pNativeImpl->get<IOleObject>());
1078 if (!pOleObject)
1079 throw embed::WrongStateException(); // TODO: the object is in wrong state
1081 SolarMutexReleaser releaser;
1082 pOleObject->SetHostNames(L"app name", o3tl::toW(aEmbDocName.getStr()));
1086 void OleComponent::SetExtent( const awt::Size& aVisAreaSize, sal_Int64 nAspect )
1088 auto pOleObject(m_pNativeImpl->get<IOleObject>());
1089 if (!pOleObject)
1090 throw embed::WrongStateException(); // TODO: the object is in wrong state
1092 DWORD nMSAspect = static_cast<DWORD>(nAspect); // first 32 bits are for MS aspects
1094 SIZEL aSize = { aVisAreaSize.Width, aVisAreaSize.Height };
1095 HRESULT hr;
1097 SolarMutexReleaser releaser;
1098 hr = pOleObject->SetExtent(nMSAspect, &aSize);
1101 if ( FAILED( hr ) )
1103 // TODO/LATER: is it correct? In future user code probably should be ready for the exception.
1104 // if the object is running but not activated, RPC_E_SERVER_DIED error code is returned by OLE package
1105 // in this case just do nothing
1106 // Also Visio returns E_FAIL on resize if it is in running state
1107 // if ( hr != RPC_E_SERVER_DIED )
1108 throw io::IOException(); // TODO
1113 awt::Size OleComponent::GetExtent( sal_Int64 nAspect )
1115 if (!m_pNativeImpl->m_pObj)
1116 throw embed::WrongStateException(); // TODO: the object is in wrong state
1118 DWORD nMSAspect = static_cast<DWORD>(nAspect); // first 32 bits are for MS aspects
1119 awt::Size aSize;
1120 bool bGotSize = false;
1122 if ( nMSAspect == DVASPECT_CONTENT )
1124 // Try to get the size from the replacement image first
1125 if (auto pDataObject = m_pNativeImpl->get<IDataObject>())
1127 STGMEDIUM aMedium;
1128 FORMATETC aFormat = pFormatTemplates[1]; // use windows metafile format
1129 aFormat.dwAspect = nMSAspect;
1131 HRESULT hr;
1133 SolarMutexReleaser releaser;
1134 hr = pDataObject->GetData(&aFormat, &aMedium);
1137 if (hr == RPC_E_WRONG_THREAD)
1139 // Assume that the OLE object was loaded on the main thread.
1140 vcl::solarthread::syncExecute([this, &hr, &pDataObject, &aFormat, &aMedium]() {
1141 // Make sure that the current state is embed::EmbedStates::RUNNING.
1142 RunObject();
1143 // Now try again on the correct thread.
1144 hr = pDataObject->GetData(&aFormat, &aMedium);
1148 if ( SUCCEEDED( hr ) && aMedium.tymed == TYMED_MFPICT ) // Win Metafile
1150 METAFILEPICT* pMF = static_cast<METAFILEPICT*>(GlobalLock( aMedium.hMetaFilePict ));
1151 if ( pMF )
1153 // the object uses 0.01 mm as unit, so the metafile size should be converted to object unit
1154 o3tl::Length eFrom = o3tl::Length::mm100;
1155 switch( pMF->mm )
1157 case MM_HIENGLISH:
1158 eFrom = o3tl::Length::in1000;
1159 break;
1161 case MM_LOENGLISH:
1162 eFrom = o3tl::Length::in100;
1163 break;
1165 case MM_LOMETRIC:
1166 eFrom = o3tl::Length::mm10;
1167 break;
1169 case MM_TWIPS:
1170 eFrom = o3tl::Length::twip;
1171 break;
1173 case MM_ISOTROPIC:
1174 case MM_ANISOTROPIC:
1175 case MM_HIMETRIC:
1176 // do nothing
1177 break;
1180 sal_Int64 nX = o3tl::convert(abs( pMF->xExt ), eFrom, o3tl::Length::mm100);
1181 sal_Int64 nY = o3tl::convert(abs( pMF->yExt ), eFrom, o3tl::Length::mm100);
1182 if ( nX < SAL_MAX_INT32 && nY < SAL_MAX_INT32 )
1184 aSize.Width = static_cast<sal_Int32>(nX);
1185 aSize.Height = static_cast<sal_Int32>(nY);
1186 bGotSize = true;
1188 else
1189 OSL_FAIL( "Unexpected size is provided!" );
1192 else if (!SUCCEEDED(hr))
1194 SAL_WARN("embeddedobj.ole", " OleComponent::GetExtent: GetData() failed");
1196 // i113605, to release storage medium
1197 if ( SUCCEEDED( hr ) )
1198 ::ReleaseStgMedium(&aMedium);
1202 if ( !bGotSize )
1203 throw lang::IllegalArgumentException();
1205 return aSize;
1209 awt::Size OleComponent::GetCachedExtent( sal_Int64 nAspect )
1211 auto pViewObject2(m_pNativeImpl->get<IViewObject2>());
1212 if (!pViewObject2)
1213 throw embed::WrongStateException(); // TODO: the object is in wrong state
1215 DWORD nMSAspect = static_cast<DWORD>(nAspect); // first 32 bits are for MS aspects
1216 SIZEL aSize;
1218 HRESULT hr;
1220 SolarMutexReleaser releaser;
1221 hr = pViewObject2->GetExtent(nMSAspect, -1, nullptr, &aSize);
1224 if ( FAILED( hr ) )
1226 // TODO/LATER: is it correct?
1227 // if there is no appropriate cache for the aspect, OLE_E_BLANK error code is returned
1228 // if ( hr == OLE_E_BLANK )
1229 // throw lang::IllegalArgumentException();
1230 //else
1231 // throw io::IOException(); // TODO
1233 SAL_WARN("embeddedobj.ole", " OleComponent::GetCachedExtent: GetExtent() failed");
1234 throw lang::IllegalArgumentException();
1237 return awt::Size( aSize.cx, aSize.cy );
1241 awt::Size OleComponent::GetRecommendedExtent( sal_Int64 nAspect )
1243 auto pOleObject(m_pNativeImpl->get<IOleObject>());
1244 if (!pOleObject)
1245 throw embed::WrongStateException(); // TODO: the object is in wrong state
1247 DWORD nMSAspect = static_cast<DWORD>(nAspect); // first 32 bits are for MS aspects
1248 SIZEL aSize;
1249 HRESULT hr;
1251 SolarMutexReleaser releaser;
1252 hr = pOleObject->GetExtent(nMSAspect, &aSize);
1254 if ( FAILED( hr ) )
1256 SAL_WARN("embeddedobj.ole", " OleComponent::GetRecommendedExtent: GetExtent() failed");
1257 throw lang::IllegalArgumentException();
1260 return awt::Size( aSize.cx, aSize.cy );
1264 sal_Int64 OleComponent::GetMiscStatus( sal_Int64 nAspect )
1266 auto pOleObject(m_pNativeImpl->get<IOleObject>());
1267 if (!pOleObject)
1268 throw embed::WrongStateException(); // TODO: the object is in wrong state
1270 DWORD nResult = 0;
1272 SolarMutexReleaser releaser;
1273 pOleObject->GetMiscStatus(static_cast<DWORD>(nAspect), &nResult);
1275 return static_cast<sal_Int64>(nResult); // first 32 bits are for MS flags
1279 uno::Sequence< sal_Int8 > OleComponent::GetCLSID()
1281 auto pOleObject(m_pNativeImpl->get<IOleObject>());
1282 if (!pOleObject)
1283 throw embed::WrongStateException(); // TODO: the object is in wrong state
1285 GUID aCLSID;
1286 HRESULT hr;
1288 SolarMutexReleaser releaser;
1289 hr = pOleObject->GetUserClassID(&aCLSID);
1291 if ( FAILED( hr ) )
1292 throw io::IOException(); // TODO:
1294 return MimeConfigurationHelper::GetSequenceClassID( aCLSID.Data1, aCLSID.Data2, aCLSID.Data3,
1295 aCLSID.Data4[0], aCLSID.Data4[1],
1296 aCLSID.Data4[2], aCLSID.Data4[3],
1297 aCLSID.Data4[4], aCLSID.Data4[5],
1298 aCLSID.Data4[6], aCLSID.Data4[7] );
1302 bool OleComponent::IsDirty()
1304 if ( IsWorkaroundActive() )
1305 return true;
1307 auto pPersistStorage(m_pNativeImpl->get<IPersistStorage>());
1308 if ( !pPersistStorage )
1309 throw io::IOException(); // TODO
1311 SolarMutexReleaser releaser;
1312 HRESULT hr = pPersistStorage->IsDirty();
1313 return ( hr != S_FALSE );
1317 void OleComponent::StoreOwnTmpIfNecessary()
1319 auto pOleObject(m_pNativeImpl->get<IOleObject>());
1320 if (!pOleObject)
1321 throw embed::WrongStateException(); // TODO: the object is in wrong state
1323 auto pPersistStorage(m_pNativeImpl->get<IPersistStorage>());
1324 if ( !pPersistStorage )
1325 throw io::IOException(); // TODO
1327 SolarMutexReleaser releaser;
1329 if ( m_bWorkaroundActive || pPersistStorage->IsDirty() != S_FALSE )
1331 auto pStorage(m_pNativeImpl->getStorage());
1332 HRESULT hr = OleSave(pPersistStorage, pStorage, TRUE);
1333 if ( FAILED( hr ) )
1335 // Till now was required only for AcrobatReader7.0.8
1336 GUID aCLSID;
1337 hr = pOleObject->GetUserClassID(&aCLSID);
1338 if ( FAILED( hr ) )
1340 SAL_WARN("embeddedobj.ole", "OleComponent::StoreOwnTmpIfNecessary: GetUserClassID() failed");
1341 throw io::IOException(); // TODO
1344 hr = WriteClassStg(pStorage, aCLSID);
1345 if ( FAILED( hr ) )
1346 throw io::IOException(); // TODO
1348 // the result of the following call is not checked because some objects, for example AcrobatReader7.0.8
1349 // return error even in case the saving was done correctly
1350 hr = pPersistStorage->Save(pStorage, TRUE);
1352 // another workaround for AcrobatReader7.0.8 object, this object might think that it is not changed
1353 // when it has been created from file, although it must be saved
1354 m_bWorkaroundActive = true;
1357 hr = pStorage->Commit(STGC_DEFAULT);
1358 if ( FAILED( hr ) )
1359 throw io::IOException(); // TODO
1361 hr = pPersistStorage->SaveCompleted( nullptr );
1362 if ( FAILED( hr ) && hr != E_UNEXPECTED )
1363 throw io::IOException(); // TODO
1369 bool OleComponent::SaveObject_Impl()
1371 bool bResult = false;
1372 OleEmbeddedObject* pLockObject = nullptr;
1375 osl::MutexGuard aGuard( m_aMutex );
1376 if ( m_pUnoOleObject )
1378 pLockObject = m_pUnoOleObject;
1379 pLockObject->acquire();
1383 if ( pLockObject )
1385 bResult = pLockObject->SaveObject_Impl();
1386 pLockObject->release();
1389 return bResult;
1393 bool OleComponent::OnShowWindow_Impl( bool bShow )
1395 bool bResult = false;
1396 OleEmbeddedObject* pLockObject = nullptr;
1399 osl::MutexGuard aGuard( m_aMutex );
1401 if ( m_pUnoOleObject )
1403 pLockObject = m_pUnoOleObject;
1404 pLockObject->acquire();
1408 if ( pLockObject )
1410 bResult = pLockObject->OnShowWindow_Impl( bShow );
1411 pLockObject->release();
1414 return bResult;
1418 void OleComponent::OnViewChange_Impl( sal_uInt32 dwAspect )
1420 // TODO: check if it is enough or may be saving notifications are required for Visio2000
1421 ::rtl::Reference< OleEmbeddedObject > xLockObject;
1424 osl::MutexGuard aGuard( m_aMutex );
1425 if ( m_pUnoOleObject )
1426 xLockObject = m_pUnoOleObject;
1429 if ( xLockObject.is() )
1431 uno::Reference < awt::XRequestCallback > xRequestCallback(
1432 m_xContext->getServiceManager()->createInstanceWithContext("com.sun.star.awt.AsyncCallback", m_xContext),
1433 uno::UNO_QUERY );
1434 xRequestCallback->addCallback( new MainThreadNotificationRequest( xLockObject, OLECOMP_ONVIEWCHANGE, dwAspect ), uno::Any() );
1439 void OleComponent::OnClose_Impl()
1441 ::rtl::Reference< OleEmbeddedObject > xLockObject;
1444 osl::MutexGuard aGuard( m_aMutex );
1445 if ( m_pUnoOleObject )
1446 xLockObject = m_pUnoOleObject;
1449 if ( xLockObject.is() )
1451 uno::Reference < awt::XRequestCallback > xRequestCallback(
1452 m_xContext->getServiceManager()->createInstanceWithContext("com.sun.star.awt.AsyncCallback", m_xContext),
1453 uno::UNO_QUERY );
1454 xRequestCallback->addCallback( new MainThreadNotificationRequest( xLockObject, OLECOMP_ONCLOSE ), uno::Any() );
1458 // XCloseable
1460 void SAL_CALL OleComponent::close( sal_Bool bDeliverOwnership )
1462 uno::Reference< uno::XInterface > xSelfHold;
1464 osl::MutexGuard aGuard(m_aMutex);
1465 if (m_bDisposed)
1466 throw lang::DisposedException(); // TODO
1468 xSelfHold.set(static_cast<::cppu::OWeakObject*>(this));
1469 lang::EventObject aSource(static_cast<::cppu::OWeakObject*>(this));
1471 if (m_pInterfaceContainer)
1473 comphelper::OInterfaceContainerHelper2* pContainer
1474 = m_pInterfaceContainer->getContainer(cppu::UnoType<util::XCloseListener>::get());
1475 if (pContainer != nullptr)
1477 comphelper::OInterfaceIteratorHelper2 pIterator(*pContainer);
1478 while (pIterator.hasMoreElements())
1482 static_cast<util::XCloseListener*>(pIterator.next())
1483 ->queryClosing(aSource, bDeliverOwnership);
1485 catch (const uno::RuntimeException&)
1487 pIterator.remove();
1492 pContainer
1493 = m_pInterfaceContainer->getContainer(cppu::UnoType<util::XCloseListener>::get());
1494 if (pContainer != nullptr)
1496 comphelper::OInterfaceIteratorHelper2 pCloseIterator(*pContainer);
1497 while (pCloseIterator.hasMoreElements())
1501 static_cast<util::XCloseListener*>(pCloseIterator.next())
1502 ->notifyClosing(aSource);
1504 catch (const uno::RuntimeException&)
1506 pCloseIterator.remove();
1513 Dispose();
1517 void SAL_CALL OleComponent::addCloseListener( const uno::Reference< util::XCloseListener >& xListener )
1519 ::osl::MutexGuard aGuard( m_aMutex );
1520 if ( m_bDisposed )
1521 throw lang::DisposedException(); // TODO
1523 if ( !m_pInterfaceContainer )
1524 m_pInterfaceContainer = new comphelper::OMultiTypeInterfaceContainerHelper2( m_aMutex );
1526 m_pInterfaceContainer->addInterface( cppu::UnoType<util::XCloseListener>::get(), xListener );
1530 void SAL_CALL OleComponent::removeCloseListener( const uno::Reference< util::XCloseListener >& xListener )
1532 ::osl::MutexGuard aGuard( m_aMutex );
1533 if ( m_bDisposed )
1534 throw lang::DisposedException(); // TODO
1536 if ( m_pInterfaceContainer )
1537 m_pInterfaceContainer->removeInterface( cppu::UnoType<util::XCloseListener>::get(),
1538 xListener );
1541 // XTransferable
1543 uno::Any SAL_CALL OleComponent::getTransferData( const datatransfer::DataFlavor& aFlavor )
1545 ::osl::ResettableMutexGuard aGuard( m_aMutex );
1546 if ( m_bDisposed )
1547 throw lang::DisposedException(); // TODO
1549 if (!m_pNativeImpl->m_pObj)
1550 throw embed::WrongStateException(); // TODO: the object is in wrong state
1552 uno::Any aResult;
1553 bool bSupportedFlavor = false;
1555 if ( m_pNativeImpl->GraphicalFlavor( aFlavor ) )
1557 DWORD nRequestedAspect = GetAspectFromFlavor( aFlavor );
1558 // if own icon is set and icon aspect is requested the own icon can be returned directly
1560 auto pDataObject(m_pNativeImpl->get<IDataObject>());
1561 if ( !pDataObject )
1562 throw io::IOException(); // TODO: transport error code
1564 // The following optimization does not make much sense currently just because
1565 // only one aspect is supported, and only three formats for the aspect are supported
1566 // and moreover it is not guaranteed that the once returned format will be supported further
1567 // example - i52106
1568 // TODO/LATER: bring the optimization back when other aspects are supported
1570 // FORMATETC* pFormatEtc = m_pNativeImpl->GetSupportedFormatForAspect( nRequestedAspect );
1571 // if ( pFormatEtc )
1572 // {
1573 // STGMEDIUM aMedium;
1574 // hr = pDataObject->GetData( pFormatEtc, &aMedium );
1575 // if ( SUCCEEDED( hr ) )
1576 // bSupportedFlavor = m_pNativeImpl->ConvertDataForFlavor( aMedium, aFlavor, aResult );
1577 // }
1578 // else
1580 // the supported format of the application is still not found, find one
1581 for ( sal_Int32 nInd = 0; nInd < FORMATS_NUM; nInd++ )
1583 STGMEDIUM aMedium;
1584 FORMATETC aFormat = pFormatTemplates[nInd];
1585 aFormat.dwAspect = nRequestedAspect;
1587 HRESULT hr;
1589 osl::ResettableMutexGuardScopedReleaser own_releaser(aGuard);
1590 SolarMutexReleaser releaser;
1591 hr = pDataObject->GetData(&aFormat, &aMedium);
1593 if ( SUCCEEDED( hr ) )
1595 bSupportedFlavor = m_pNativeImpl->ConvertDataForFlavor( aMedium, aFlavor, aResult );
1596 ::ReleaseStgMedium(&aMedium); // i113605, to release storage medium
1597 if ( bSupportedFlavor )
1599 // TODO/LATER: bring the optimization back when other aspects are supported
1600 // m_pNativeImpl->AddSupportedFormat( aFormat );
1601 break;
1607 // If the replacement could not be retrieved, the cached representation should be used
1608 // currently it is not necessary to retrieve it here, so it is implemented in the object itself
1610 // TODO: Investigate if there is already the format name
1611 // and whether this format is really required
1612 else if ( aFlavor.DataType == cppu::UnoType<io::XInputStream>::get()
1613 && aFlavor.MimeType == "application/x-openoffice-contentstream" )
1615 // allow to retrieve stream-representation of the object persistence
1616 bSupportedFlavor = true;
1617 uno::Reference < io::XStream > xTempFileStream(
1618 io::TempFile::create(m_xContext),
1619 uno::UNO_QUERY_THROW );
1621 uno::Reference< io::XOutputStream > xTempOutStream = xTempFileStream->getOutputStream();
1622 uno::Reference< io::XInputStream > xTempInStream = xTempFileStream->getInputStream();
1623 if ( !(xTempOutStream.is() && xTempInStream.is()) )
1624 throw io::IOException(); // TODO:
1626 OSL_ENSURE( m_pUnoOleObject, "Unexpected object absence!" );
1627 if ( !m_pUnoOleObject )
1628 throw uno::RuntimeException();
1630 m_pUnoOleObject->StoreObjectToStream( xTempOutStream );
1632 xTempOutStream->closeOutput();
1633 xTempOutStream.clear();
1635 aResult <<= xTempInStream;
1638 if ( !bSupportedFlavor )
1639 throw datatransfer::UnsupportedFlavorException();
1641 return aResult;
1645 uno::Sequence< datatransfer::DataFlavor > SAL_CALL OleComponent::getTransferDataFlavors()
1647 ::osl::MutexGuard aGuard( m_aMutex );
1648 if ( m_bDisposed )
1649 throw lang::DisposedException(); // TODO
1651 RetrieveObjectDataFlavors_Impl();
1653 return m_aDataFlavors;
1657 sal_Bool SAL_CALL OleComponent::isDataFlavorSupported( const datatransfer::DataFlavor& aFlavor )
1659 ::osl::MutexGuard aGuard( m_aMutex );
1660 if ( m_bDisposed )
1661 throw lang::DisposedException(); // TODO
1663 RetrieveObjectDataFlavors_Impl();
1665 for (auto const& supportedFormat : m_aDataFlavors)
1666 if ( supportedFormat.MimeType.equals( aFlavor.MimeType ) && supportedFormat.DataType == aFlavor.DataType )
1667 return true;
1669 return false;
1672 void SAL_CALL OleComponent::dispose()
1676 close( true );
1678 catch ( const uno::Exception& )
1683 void SAL_CALL OleComponent::addEventListener( const uno::Reference< lang::XEventListener >& xListener )
1685 ::osl::MutexGuard aGuard( m_aMutex );
1686 if ( m_bDisposed )
1687 throw lang::DisposedException(); // TODO
1689 if ( !m_pInterfaceContainer )
1690 m_pInterfaceContainer = new comphelper::OMultiTypeInterfaceContainerHelper2( m_aMutex );
1692 m_pInterfaceContainer->addInterface( cppu::UnoType<lang::XEventListener>::get(), xListener );
1696 void SAL_CALL OleComponent::removeEventListener( const uno::Reference< lang::XEventListener >& xListener )
1698 ::osl::MutexGuard aGuard( m_aMutex );
1699 if ( m_bDisposed )
1700 throw lang::DisposedException(); // TODO
1702 if ( m_pInterfaceContainer )
1703 m_pInterfaceContainer->removeInterface( cppu::UnoType<lang::XEventListener>::get(),
1704 xListener );
1707 sal_Int64 SAL_CALL OleComponent::getSomething( const css::uno::Sequence< sal_Int8 >& aIdentifier )
1711 uno::Sequence < sal_Int8 > aCLSID = GetCLSID();
1712 if ( MimeConfigurationHelper::ClassIDsEqual( aIdentifier, aCLSID ) )
1713 return comphelper::getSomething_cast(m_pNativeImpl->m_pObj.get());
1715 catch ( const uno::Exception& )
1719 return 0;
1722 sal_Bool SAL_CALL OleComponent::isModified()
1724 return m_bModified;
1727 void SAL_CALL OleComponent::setModified( sal_Bool bModified )
1729 m_bModified = bModified;
1731 if ( bModified && m_pInterfaceContainer )
1733 comphelper::OInterfaceContainerHelper2* pContainer =
1734 m_pInterfaceContainer->getContainer( cppu::UnoType<util::XModifyListener>::get());
1735 if ( pContainer != nullptr )
1737 comphelper::OInterfaceIteratorHelper2 pIterator( *pContainer );
1738 while ( pIterator.hasMoreElements() )
1742 lang::EventObject aEvent( static_cast<util::XModifiable*>(this) );
1743 static_cast<util::XModifyListener*>(pIterator.next())->modified( aEvent );
1745 catch( const uno::RuntimeException& )
1747 pIterator.remove();
1754 void SAL_CALL OleComponent::addModifyListener( const css::uno::Reference < css::util::XModifyListener >& xListener )
1756 ::osl::MutexGuard aGuard( m_aMutex );
1757 if ( m_bDisposed )
1758 throw lang::DisposedException(); // TODO
1760 if ( !m_pInterfaceContainer )
1761 m_pInterfaceContainer = new comphelper::OMultiTypeInterfaceContainerHelper2( m_aMutex );
1763 m_pInterfaceContainer->addInterface( cppu::UnoType<util::XModifyListener>::get(), xListener );
1766 void SAL_CALL OleComponent::removeModifyListener( const css::uno::Reference < css::util::XModifyListener >& xListener)
1768 ::osl::MutexGuard aGuard( m_aMutex );
1769 if ( m_bDisposed )
1770 throw lang::DisposedException(); // TODO
1772 if ( m_pInterfaceContainer )
1773 m_pInterfaceContainer->removeInterface( cppu::UnoType<util::XModifyListener>::get(),
1774 xListener );
1777 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */