1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
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>
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"
60 using namespace ::com::sun::star
;
61 using namespace ::comphelper
;
62 #define MAX_ENUM_ELE 20
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
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
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\"",
152 cppu::UnoType
<uno::Sequence
< sal_Int8
>>::get() ),
154 datatransfer::DataFlavor(
155 "application/x-openoffice-bitmap;windows_formatname=\"Bitmap\"",
157 cppu::UnoType
<uno::Sequence
< sal_Int8
>>::get() ),
159 datatransfer::DataFlavor(
162 cppu::UnoType
<uno::Sequence
< sal_Int8
>>::get() ),
164 datatransfer::DataFlavor(
165 "application/x-openoffice-gdimetafile;windows_formatname=\"GDIMetaFile\"",
167 cppu::UnoType
<uno::Sequence
< sal_Int8
>>::get() )
171 ~OleComponentNative_Impl()
174 m_pGlobalTable
->RevokeInterfaceFromGlobal(m_nOleObject
);
176 m_pGlobalTable
->RevokeInterfaceFromGlobal(m_nStorage
);
179 bool ConvertDataForFlavor( const STGMEDIUM
& aMedium
,
180 const datatransfer::DataFlavor
& aFlavor
,
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
);
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?");
203 template <typename T
> void registerInterface(T
* pInterface
, DWORD
& cookie
)
205 if (cookie
!= 0) // E.g., on subsequent RunObject calls
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
;
221 return DVASPECT_CONTENT
;
225 static OUString
GetFlavorSuffixFromAspect( DWORD nAsp
)
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
242 bool OleComponentNative_Impl::ConvertDataForFlavor( const STGMEDIUM
& aMedium
,
243 const datatransfer::DataFlavor
& aFlavor
,
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;
257 if ( aMedium
.tymed
== TYMED_MFPICT
) // Win Metafile
259 aFormat
= "image/x-wmf";
260 METAFILEPICT
* pMF
= static_cast<METAFILEPICT
*>(GlobalLock( aMedium
.hMetaFilePict
));
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
);
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
);
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
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
);
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
);
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
)
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]);
369 static OUString
WinAccToVcl_Impl( const sal_Unicode
* pStr
)
380 while( *( ++pStr
) == '&' );
384 aResult
+= OUStringChar( *pStr
);
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;
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
);
437 } catch( const uno::Exception
& ) {}
441 void OleComponent::Dispose()
446 // Call CloseObject() without m_aMutex locked, since it will call
447 // IOleObject::Close(), which can call event listeners, which can run on a
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;
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();
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
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
);
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
;
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
>())
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
];
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
;
592 while( nNum
== MAX_ENUM_ELE
);
594 m_aDataFlavors
= m_pNativeImpl
->GetFlavorsForAspects( nSupportedAspects
);
598 if ( !m_aDataFlavors
.getLength() )
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
));
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
)
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
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
));
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
658 pOleObject
->Advise(m_pImplAdviseSink
, reinterpret_cast<DWORD
*>(&nAdvConn
));
659 pViewObject2
->SetAdvise(DVASPECT_CONTENT
, 0, m_pImplAdviseSink
);
661 OleSetContainedObject(pOleObject
, TRUE
);
666 HRESULT
OleLoadSeh(LPSTORAGE pIStorage
, IUnknown
** ppObj
)
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.
680 hr
= OleLoad(pIStorage
, IID_IUnknown
, nullptr, IID_PPV_ARGS_Helper(ppObj
));
681 } __except( EXCEPTION_EXECUTE_HANDLER
) {
684 if (pIStorage
&& hr
!= STG_E_FILENOTFOUND
)
685 pIStorage
->Release();
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
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
);
715 throw uno::RuntimeException();
717 InitializeObject_Impl();
721 void OleComponent::CreateObjectFromClipboard()
723 auto pStorage(m_pNativeImpl
->CreateNewStorage(getTempURL()));
725 throw uno::RuntimeException(); // TODO
727 IDataObject
* pDO
= nullptr;
728 HRESULT hr
= OleGetClipboard( &pDO
);
730 throw uno::RuntimeException();
732 hr
= OleQueryCreateFromData(pDO
);
735 hr
= OleCreateFromData( pDO
,
737 OLERENDER_DRAW
, // OLERENDER_FORMAT
738 nullptr, // &aFormat,
741 IID_PPV_ARGS_Helper(&m_pNativeImpl
->m_pObj
) );
743 throw uno::RuntimeException();
747 // Static objects are not supported
751 InitializeObject_Impl();
755 void OleComponent::CreateNewEmbeddedObject( const uno::Sequence
< sal_Int8
>& aSeqCLSID
)
759 if ( !GetClassIDFromSequence_Impl( aSeqCLSID
, aClsID
) )
760 throw lang::IllegalArgumentException(); // TODO
762 auto pStorage(m_pNativeImpl
->CreateNewStorage(getTempURL()));
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
,
770 OLERENDER_DRAW
, // OLERENDER_FORMAT
771 nullptr, // &aFormat,
774 IID_PPV_ARGS_Helper(&m_pNativeImpl
->m_pObj
) );
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 ).
791 // OleQueryCreateFromData...
795 void OleComponent::CreateObjectFromFile( const OUString
& aFileURL
)
797 auto pStorage(m_pNativeImpl
->CreateNewStorage(getTempURL()));
799 throw uno::RuntimeException(); // TODO:
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()),
808 OLERENDER_DRAW
, // OLERENDER_FORMAT
812 IID_PPV_ARGS_Helper(&m_pNativeImpl
->m_pObj
) );
814 throw uno::RuntimeException(); // TODO
816 InitializeObject_Impl();
820 void OleComponent::CreateLinkFromFile( const OUString
& aFileURL
)
822 auto pStorage(m_pNativeImpl
->CreateNewStorage(getTempURL()));
824 throw uno::RuntimeException(); // TODO:
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()),
832 OLERENDER_DRAW
, // OLERENDER_FORMAT
836 IID_PPV_ARGS_Helper(&m_pNativeImpl
->m_pObj
) );
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
) ) )
862 throw uno::RuntimeException(); // TODO:
864 OleCreateFromData( pDataObject
,
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
));
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 : )
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
);
899 pMalloc
->Free( pOleStr
);
901 hr
= OleCreateFromFile( CLSID_NULL
,
904 OLERENDER_DRAW
, // OLERENDER_FORMAT
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!");
940 throw embed::WrongStateException(); // TODO: the object is in wrong state
942 if (!OleIsRunning(pOleObject
))
944 HRESULT hr
= OleRun( m_pNativeImpl
->m_pObj
);
948 OUString error
= WindowsErrorStringFromHRESULT(hr
);
949 if ( hr
== REGDB_E_CLASSNOTREG
)
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");
962 pMalloc
->Free(lpUserType
);
965 throw embed::UnreachableStateException(
966 error
, getXWeak(), -1,
967 css::embed::EmbedStates::RUNNING
); // the object server is not installed
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
)
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
);
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
>());
1014 throw embed::WrongStateException(); // TODO: the object is in wrong state
1016 if( !m_aVerbList
.getLength() )
1018 sal::systools::COMReference
< IEnumOLEVERB
> pEnum
;
1021 SolarMutexReleaser releaser
;
1022 hr
= pOleObject
->EnumVerbs(&pEnum
);
1026 OLEVERB szEle
[ MAX_ENUM_ELE
];
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
;
1048 while( nNum
== MAX_ENUM_ELE
);
1056 void OleComponent::ExecuteVerb( sal_Int32 nVerbID
)
1060 auto pOleObject(m_pNativeImpl
->get
<IOleObject
>());
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);
1071 throw io::IOException(); // TODO
1075 void OleComponent::SetHostName( const OUString
& aEmbDocName
)
1077 auto pOleObject(m_pNativeImpl
->get
<IOleObject
>());
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
>());
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
};
1097 SolarMutexReleaser releaser
;
1098 hr
= pOleObject
->SetExtent(nMSAspect
, &aSize
);
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
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
>())
1128 FORMATETC aFormat
= pFormatTemplates
[1]; // use windows metafile format
1129 aFormat
.dwAspect
= nMSAspect
;
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.
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
));
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
;
1158 eFrom
= o3tl::Length::in1000
;
1162 eFrom
= o3tl::Length::in100
;
1166 eFrom
= o3tl::Length::mm10
;
1170 eFrom
= o3tl::Length::twip
;
1174 case MM_ANISOTROPIC
:
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
);
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
);
1203 throw lang::IllegalArgumentException();
1209 awt::Size
OleComponent::GetCachedExtent( sal_Int64 nAspect
)
1211 auto pViewObject2(m_pNativeImpl
->get
<IViewObject2
>());
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
1220 SolarMutexReleaser releaser
;
1221 hr
= pViewObject2
->GetExtent(nMSAspect
, -1, nullptr, &aSize
);
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();
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
>());
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
1251 SolarMutexReleaser releaser
;
1252 hr
= pOleObject
->GetExtent(nMSAspect
, &aSize
);
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
>());
1268 throw embed::WrongStateException(); // TODO: the object is in wrong state
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
>());
1283 throw embed::WrongStateException(); // TODO: the object is in wrong state
1288 SolarMutexReleaser releaser
;
1289 hr
= pOleObject
->GetUserClassID(&aCLSID
);
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() )
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
>());
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
);
1335 // Till now was required only for AcrobatReader7.0.8
1337 hr
= pOleObject
->GetUserClassID(&aCLSID
);
1340 SAL_WARN("embeddedobj.ole", "OleComponent::StoreOwnTmpIfNecessary: GetUserClassID() failed");
1341 throw io::IOException(); // TODO
1344 hr
= WriteClassStg(pStorage
, aCLSID
);
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
);
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();
1385 bResult
= pLockObject
->SaveObject_Impl();
1386 pLockObject
->release();
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();
1410 bResult
= pLockObject
->OnShowWindow_Impl( bShow
);
1411 pLockObject
->release();
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
),
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
),
1454 xRequestCallback
->addCallback( new MainThreadNotificationRequest( xLockObject
, OLECOMP_ONCLOSE
), uno::Any() );
1460 void SAL_CALL
OleComponent::close( sal_Bool bDeliverOwnership
)
1462 uno::Reference
< uno::XInterface
> xSelfHold
;
1464 osl::MutexGuard
aGuard(m_aMutex
);
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
&)
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();
1517 void SAL_CALL
OleComponent::addCloseListener( const uno::Reference
< util::XCloseListener
>& xListener
)
1519 ::osl::MutexGuard
aGuard( m_aMutex
);
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
);
1534 throw lang::DisposedException(); // TODO
1536 if ( m_pInterfaceContainer
)
1537 m_pInterfaceContainer
->removeInterface( cppu::UnoType
<util::XCloseListener
>::get(),
1543 uno::Any SAL_CALL
OleComponent::getTransferData( const datatransfer::DataFlavor
& aFlavor
)
1545 ::osl::ResettableMutexGuard
aGuard( m_aMutex
);
1547 throw lang::DisposedException(); // TODO
1549 if (!m_pNativeImpl
->m_pObj
)
1550 throw embed::WrongStateException(); // TODO: the object is in wrong state
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
>());
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
1568 // TODO/LATER: bring the optimization back when other aspects are supported
1570 // FORMATETC* pFormatEtc = m_pNativeImpl->GetSupportedFormatForAspect( nRequestedAspect );
1571 // if ( pFormatEtc )
1573 // STGMEDIUM aMedium;
1574 // hr = pDataObject->GetData( pFormatEtc, &aMedium );
1575 // if ( SUCCEEDED( hr ) )
1576 // bSupportedFlavor = m_pNativeImpl->ConvertDataForFlavor( aMedium, aFlavor, aResult );
1580 // the supported format of the application is still not found, find one
1581 for ( sal_Int32 nInd
= 0; nInd
< FORMATS_NUM
; nInd
++ )
1584 FORMATETC aFormat
= pFormatTemplates
[nInd
];
1585 aFormat
.dwAspect
= nRequestedAspect
;
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 );
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
, aGuard
);
1632 xTempOutStream
->closeOutput();
1633 xTempOutStream
.clear();
1635 aResult
<<= xTempInStream
;
1638 if ( !bSupportedFlavor
)
1639 throw datatransfer::UnsupportedFlavorException();
1645 uno::Sequence
< datatransfer::DataFlavor
> SAL_CALL
OleComponent::getTransferDataFlavors()
1647 ::osl::MutexGuard
aGuard( m_aMutex
);
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
);
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
)
1672 void SAL_CALL
OleComponent::dispose()
1678 catch ( const uno::Exception
& )
1683 void SAL_CALL
OleComponent::addEventListener( const uno::Reference
< lang::XEventListener
>& xListener
)
1685 ::osl::MutexGuard
aGuard( m_aMutex
);
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
);
1700 throw lang::DisposedException(); // TODO
1702 if ( m_pInterfaceContainer
)
1703 m_pInterfaceContainer
->removeInterface( cppu::UnoType
<lang::XEventListener
>::get(),
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
& )
1722 sal_Bool SAL_CALL
OleComponent::isModified()
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
& )
1754 void SAL_CALL
OleComponent::addModifyListener( const css::uno::Reference
< css::util::XModifyListener
>& xListener
)
1756 ::osl::MutexGuard
aGuard( m_aMutex
);
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
);
1770 throw lang::DisposedException(); // TODO
1772 if ( m_pInterfaceContainer
)
1773 m_pInterfaceContainer
->removeInterface( cppu::UnoType
<util::XModifyListener
>::get(),
1777 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */