1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3  * This file is part of the LibreOffice project.
4  *
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/.
8  *
9  * This file incorporates work covered by the following license notice:
10  *
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 .
18  */
19 
20 #include "APNDataObject.hxx"
21 #include <osl/diagnose.h>
22 
23 #include <systools/win32/comtools.hxx>
24 
25 #define FREE_HGLOB_ON_RELEASE   TRUE
26 #define KEEP_HGLOB_ON_RELEASE   FALSE
27 
28 // ctor
29 
CAPNDataObject(IDataObjectPtr rIDataObject)30 CAPNDataObject::CAPNDataObject( IDataObjectPtr rIDataObject ) :
31     m_rIDataObjectOrg( rIDataObject ),
32     m_hGlobal( nullptr ),
33     m_nRefCnt( 0 )
34 {
35 
36     OSL_ENSURE( m_rIDataObjectOrg.get( ), "constructing CAPNDataObject with empty data object" );
37 
38     // we marshal the IDataObject interface pointer here so
39     // that it can be unmarshalled multiple times when this
40     // class will be used from another apartment
41     IStreamPtr pStm;
42     HRESULT hr = CreateStreamOnHGlobal( nullptr, KEEP_HGLOB_ON_RELEASE, &pStm );
43 
44     OSL_ENSURE( E_INVALIDARG != hr, "invalid args passed to CreateStreamOnHGlobal" );
45 
46     if ( SUCCEEDED( hr ) )
47     {
48         HRESULT hr_marshal = CoMarshalInterface(
49             pStm.get(),
50             __uuidof(IDataObject),
51             static_cast<LPUNKNOWN>(m_rIDataObjectOrg.get()),
52             MSHCTX_LOCAL,
53             nullptr,
54             MSHLFLAGS_TABLEWEAK );
55 
56         OSL_ENSURE( CO_E_NOTINITIALIZED != hr_marshal, "COM is not initialized" );
57 
58         // marshalling may fail if COM is not initialized
59         // for the calling thread which is a program time
60         // error or because of stream errors which are runtime
61         // errors for instance E_OUTOFMEMORY etc.
62 
63         hr = GetHGlobalFromStream(pStm.get(), &m_hGlobal );
64 
65         OSL_ENSURE( E_INVALIDARG != hr, "invalid stream passed to GetHGlobalFromStream" );
66 
67         // if the marshalling failed we free the
68         // global memory again and set m_hGlobal
69         // to a defined value
70         if (FAILED(hr_marshal))
71         {
72             OSL_FAIL("marshalling failed");
73 
74             HGLOBAL hGlobal =
75                 GlobalFree(m_hGlobal);
76             OSL_ENSURE(nullptr == hGlobal, "GlobalFree failed");
77             m_hGlobal = nullptr;
78         }
79     }
80 }
81 
~CAPNDataObject()82 CAPNDataObject::~CAPNDataObject( )
83 {
84     if (m_hGlobal)
85     {
86         IStreamPtr pStm;
87         HRESULT  hr = CreateStreamOnHGlobal(m_hGlobal, FREE_HGLOB_ON_RELEASE, &pStm);
88 
89         OSL_ENSURE( E_INVALIDARG != hr, "invalid args passed to CreateStreamOnHGlobal" );
90 
91         if (SUCCEEDED(hr))
92         {
93             hr = CoReleaseMarshalData(pStm.get());
94             OSL_ENSURE(SUCCEEDED(hr), "CoReleaseMarshalData failed");
95         }
96     }
97 }
98 
99 // IUnknown->QueryInterface
100 
QueryInterface(REFIID iid,void ** ppvObject)101 STDMETHODIMP CAPNDataObject::QueryInterface( REFIID iid, void** ppvObject )
102 {
103     OSL_ASSERT( nullptr != ppvObject );
104 
105     if ( nullptr == ppvObject )
106         return E_INVALIDARG;
107 
108     HRESULT hr = E_NOINTERFACE;
109     *ppvObject = nullptr;
110 
111     if ( ( __uuidof( IUnknown ) == iid ) || ( __uuidof( IDataObject ) == iid ) )
112     {
113         *ppvObject = static_cast< IUnknown* >( this );
114         static_cast<LPUNKNOWN>(*ppvObject)->AddRef( );
115         hr = S_OK;
116     }
117 
118     return hr;
119 }
120 
121 // IUnknown->AddRef
122 
STDMETHODIMP_(ULONG)123 STDMETHODIMP_(ULONG) CAPNDataObject::AddRef( )
124 {
125     return static_cast< ULONG >( InterlockedIncrement( &m_nRefCnt ) );
126 }
127 
128 // IUnknown->Release
129 
STDMETHODIMP_(ULONG)130 STDMETHODIMP_(ULONG) CAPNDataObject::Release( )
131 {
132     // we need a helper variable because it's not allowed to access
133     // a member variable after an object is destroyed
134     ULONG nRefCnt = static_cast< ULONG >( InterlockedDecrement( &m_nRefCnt ) );
135 
136     if ( 0 == nRefCnt )
137         delete this;
138 
139     return nRefCnt;
140 }
141 
142 // IDataObject->GetData
143 
GetData(FORMATETC * pFormatetc,STGMEDIUM * pmedium)144 STDMETHODIMP CAPNDataObject::GetData( FORMATETC * pFormatetc, STGMEDIUM * pmedium )
145 {
146     HRESULT hr = m_rIDataObjectOrg->GetData( pFormatetc, pmedium );
147 
148     if (RPC_E_WRONG_THREAD == hr)
149     {
150         IDataObjectPtr pIDOTmp;
151         hr = MarshalIDataObjectIntoCurrentApartment(&pIDOTmp);
152 
153         if (SUCCEEDED(hr))
154             hr = pIDOTmp->GetData(pFormatetc, pmedium);
155     }
156     return hr;
157 }
158 
159 // IDataObject->EnumFormatEtc
160 
EnumFormatEtc(DWORD dwDirection,IEnumFORMATETC ** ppenumFormatetc)161 STDMETHODIMP CAPNDataObject::EnumFormatEtc( DWORD dwDirection, IEnumFORMATETC** ppenumFormatetc )
162 {
163     HRESULT hr = m_rIDataObjectOrg->EnumFormatEtc(dwDirection, ppenumFormatetc);
164 
165     if (RPC_E_WRONG_THREAD == hr)
166     {
167         IDataObjectPtr pIDOTmp;
168         hr = MarshalIDataObjectIntoCurrentApartment(&pIDOTmp);
169 
170         if (SUCCEEDED(hr))
171             hr = pIDOTmp->EnumFormatEtc(dwDirection, ppenumFormatetc);
172     }
173     return hr;
174 }
175 
176 // IDataObject->QueryGetData
177 
QueryGetData(FORMATETC * pFormatetc)178 STDMETHODIMP CAPNDataObject::QueryGetData( FORMATETC * pFormatetc )
179 {
180     HRESULT hr = m_rIDataObjectOrg->QueryGetData( pFormatetc );
181 
182     if (RPC_E_WRONG_THREAD == hr)
183     {
184         IDataObjectPtr pIDOTmp;
185         hr = MarshalIDataObjectIntoCurrentApartment( &pIDOTmp );
186 
187         if (SUCCEEDED(hr))
188             hr = pIDOTmp->QueryGetData(pFormatetc);
189     }
190     return hr;
191 }
192 
193 // IDataObject->GetDataHere
194 
GetDataHere(FORMATETC * pFormatetc,STGMEDIUM * pmedium)195 STDMETHODIMP CAPNDataObject::GetDataHere( FORMATETC * pFormatetc, STGMEDIUM * pmedium )
196 {
197     HRESULT hr = m_rIDataObjectOrg->GetDataHere(pFormatetc, pmedium);
198 
199     if (RPC_E_WRONG_THREAD == hr)
200     {
201         IDataObjectPtr pIDOTmp;
202         hr = MarshalIDataObjectIntoCurrentApartment(&pIDOTmp);
203 
204         if (SUCCEEDED(hr))
205             hr = pIDOTmp->GetDataHere(pFormatetc, pmedium);
206     }
207     return hr;
208 }
209 
210 // IDataObject->GetCanonicalFormatEtc
211 
GetCanonicalFormatEtc(FORMATETC * pFormatectIn,FORMATETC * pFormatetcOut)212 STDMETHODIMP CAPNDataObject::GetCanonicalFormatEtc(FORMATETC * pFormatectIn, FORMATETC * pFormatetcOut)
213 {
214     HRESULT hr = m_rIDataObjectOrg->GetCanonicalFormatEtc( pFormatectIn, pFormatetcOut );
215 
216     if (RPC_E_WRONG_THREAD == hr)
217     {
218         IDataObjectPtr pIDOTmp;
219         hr = MarshalIDataObjectIntoCurrentApartment(&pIDOTmp);
220 
221         if (SUCCEEDED(hr))
222             hr = pIDOTmp->GetCanonicalFormatEtc(pFormatectIn, pFormatetcOut);
223     }
224     return hr;
225 }
226 
227 // IDataObject->SetData
228 
SetData(FORMATETC * pFormatetc,STGMEDIUM * pmedium,BOOL fRelease)229 STDMETHODIMP CAPNDataObject::SetData( FORMATETC * pFormatetc, STGMEDIUM * pmedium, BOOL fRelease )
230 {
231     HRESULT hr = m_rIDataObjectOrg->SetData( pFormatetc, pmedium, fRelease );
232 
233     if (RPC_E_WRONG_THREAD == hr)
234     {
235         IDataObjectPtr pIDOTmp;
236         hr = MarshalIDataObjectIntoCurrentApartment(&pIDOTmp);
237 
238         if (SUCCEEDED(hr))
239             hr = pIDOTmp->SetData(pFormatetc, pmedium, fRelease);
240     }
241     return hr;
242 }
243 
244 // IDataObject->DAdvise
245 
DAdvise(FORMATETC * pFormatetc,DWORD advf,IAdviseSink * pAdvSink,DWORD * pdwConnection)246 STDMETHODIMP CAPNDataObject::DAdvise( FORMATETC * pFormatetc, DWORD advf, IAdviseSink * pAdvSink, DWORD * pdwConnection )
247 {
248     HRESULT hr = m_rIDataObjectOrg->DAdvise(pFormatetc, advf, pAdvSink, pdwConnection);
249 
250     if (RPC_E_WRONG_THREAD == hr)
251     {
252         IDataObjectPtr pIDOTmp;
253         hr = MarshalIDataObjectIntoCurrentApartment(&pIDOTmp);
254 
255         if (SUCCEEDED(hr))
256             hr = pIDOTmp->DAdvise(pFormatetc, advf, pAdvSink, pdwConnection);
257     }
258     return hr;
259 }
260 
261 // IDataObject->DUnadvise
262 
DUnadvise(DWORD dwConnection)263 STDMETHODIMP CAPNDataObject::DUnadvise( DWORD dwConnection )
264 {
265     HRESULT hr = m_rIDataObjectOrg->DUnadvise( dwConnection );
266 
267     if (RPC_E_WRONG_THREAD == hr)
268     {
269         IDataObjectPtr pIDOTmp;
270         hr = MarshalIDataObjectIntoCurrentApartment(&pIDOTmp);
271 
272         if (SUCCEEDED(hr))
273             hr = pIDOTmp->DUnadvise(dwConnection);
274     }
275     return hr;
276 }
277 
278 // IDataObject->EnumDAdvise
279 
EnumDAdvise(IEnumSTATDATA ** ppenumAdvise)280 STDMETHODIMP CAPNDataObject::EnumDAdvise( IEnumSTATDATA ** ppenumAdvise )
281 {
282     HRESULT hr = m_rIDataObjectOrg->EnumDAdvise(ppenumAdvise);
283 
284     if (RPC_E_WRONG_THREAD == hr)
285     {
286         IDataObjectPtr pIDOTmp;
287         hr = MarshalIDataObjectIntoCurrentApartment(&pIDOTmp);
288 
289         if (SUCCEEDED(hr))
290             hr = pIDOTmp->EnumDAdvise(ppenumAdvise);
291     }
292     return hr;
293 }
294 
295 // for our convenience
296 
operator IDataObject*()297 CAPNDataObject::operator IDataObject*( )
298 {
299     return static_cast< IDataObject* >( this );
300 }
301 
302 // helper function
303 
MarshalIDataObjectIntoCurrentApartment(IDataObject ** ppIDataObj)304 HRESULT CAPNDataObject::MarshalIDataObjectIntoCurrentApartment( IDataObject** ppIDataObj )
305 {
306     OSL_ASSERT(nullptr != ppIDataObj);
307 
308     *ppIDataObj = nullptr;
309     HRESULT hr = E_FAIL;
310 
311     if (m_hGlobal)
312     {
313         IStreamPtr pStm;
314         hr = CreateStreamOnHGlobal(m_hGlobal, KEEP_HGLOB_ON_RELEASE, &pStm);
315 
316         OSL_ENSURE(E_INVALIDARG != hr, "CreateStreamOnHGlobal with invalid args called");
317 
318         if (SUCCEEDED(hr))
319         {
320             hr = CoUnmarshalInterface(pStm.get(), __uuidof(IDataObject), reinterpret_cast<void**>(ppIDataObj));
321             OSL_ENSURE(CO_E_NOTINITIALIZED != hr, "COM is not initialized");
322         }
323     }
324     return hr;
325 }
326 
327 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
328