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