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 .
23 #include <string_view>
25 #include <type_traits>
32 namespace sal::systools
34 /* Simple exception class for propagating COM errors */
35 class ComError
: public std::runtime_error
38 ComError(const std::string
& message
, HRESULT hr
) :
39 std::runtime_error(message
),
43 HRESULT
GetHresult() const { return hr_
; }
49 /* Convert failed HRESULT to thrown ComError */
50 inline void ThrowIfFailed(HRESULT hr
, std::string_view msg
)
53 throw ComError(std::string(msg
), hr
);
56 /* A guard class to call CoInitializeEx/CoUninitialize in proper pairs
57 * See also: o3tl::safeCoInitializeEx doing dangerous re-initialization
59 class CoInitializeGuard
64 NoThrow
, // do not throw
65 Throw
, // throw on failure
66 Abort
, // std::abort on failure
68 explicit CoInitializeGuard(DWORD dwCoInit
, bool failChangeMode
= false,
69 WhenFailed whenFailed
= WhenFailed::Throw
)
71 HRESULT hr
= ::CoInitializeEx(nullptr, dwCoInit
);
72 if (whenFailed
!= WhenFailed::NoThrow
&& FAILED(hr
)
73 && (failChangeMode
|| hr
!= RPC_E_CHANGED_MODE
))
75 if (whenFailed
== WhenFailed::Throw
)
76 throw ComError("CoInitializeEx failed", hr
);
77 else // if (whenFailed == Abort)
80 mbUninit
= SUCCEEDED(hr
);
82 CoInitializeGuard(const CoInitializeGuard
&) = delete; // non-construction-copyable
83 void operator=(const CoInitializeGuard
&) = delete; // non-copyable
94 struct COM_QUERY_TAG
{} constexpr COM_QUERY
;
95 struct COM_QUERY_THROW_TAG
{} constexpr COM_QUERY_THROW
;
96 template <typename TAG
>
97 constexpr bool is_COM_query_tag
98 = std::is_same_v
<TAG
, COM_QUERY_TAG
> || std::is_same_v
<TAG
, COM_QUERY_THROW_TAG
>;
100 /* A simple COM smart pointer template */
101 template <typename T
>
105 /* Explicitly controllable whether AddRef will be called or not */
106 COMReference(T
* comptr
= nullptr, bool bAddRef
= true) :
113 COMReference(const COMReference
<T
>& other
) :
114 COMReference(other
.com_ptr_
)
118 COMReference(COMReference
<T
>&& other
) :
119 COMReference(std::exchange(other
.com_ptr_
, nullptr), false)
123 // Query from IUnknown*, using COM_QUERY or COM_QUERY_THROW tags
124 template <typename T2
, typename TAG
>
125 COMReference(const COMReference
<T2
>& p
, TAG t
)
126 : COMReference(p
.template QueryInterface
<T
>(t
))
130 // Using CoCreateInstance
131 COMReference(REFCLSID clsid
, IUnknown
* pOuter
= nullptr, DWORD nCtx
= CLSCTX_ALL
)
134 ThrowIfFailed(CoCreateInstance(clsid
, pOuter
, nCtx
), "CoCreateInstance failed");
137 COMReference
<T
>& operator=(const COMReference
<T
>& other
)
139 return operator=(other
.com_ptr_
);
142 COMReference
<T
>& operator=(COMReference
<T
>&& other
)
144 if (com_ptr_
!= other
.com_ptr_
)
147 std::swap(com_ptr_
, other
.com_ptr_
);
152 COMReference
<T
>& operator=(T
* comptr
)
158 ~COMReference() { release(com_ptr_
); }
160 template <typename T2
, typename TAG
, std::enable_if_t
<is_COM_query_tag
<TAG
>, int> = 0>
161 COMReference
<T2
> QueryInterface(TAG
) const
164 HRESULT hr
= E_POINTER
;
166 hr
= com_ptr_
->QueryInterface(&ip
);
168 if constexpr (std::is_same_v
<TAG
, COM_QUERY_THROW_TAG
>)
169 ThrowIfFailed(hr
, "QueryInterface failed");
171 return { ip
, false };
174 template <typename T2
, typename TAG
>
175 COMReference
<T
>& set(const COMReference
<T2
>& p
, TAG t
)
177 return operator=(p
.template QueryInterface
<T
>(t
));
180 HRESULT
CoCreateInstance(REFCLSID clsid
, IUnknown
* pOuter
= nullptr,
181 DWORD nCtx
= CLSCTX_ALL
)
184 HRESULT hr
= ::CoCreateInstance(clsid
, pOuter
, nCtx
, IID_PPV_ARGS(&ip
));
186 release(std::exchange(com_ptr_
, ip
));
190 HRESULT
CoGetClassObject(REFCLSID clsid
, DWORD nCtx
= CLSCTX_ALL
)
193 HRESULT hr
= ::CoGetClassObject(clsid
, nCtx
, nullptr, IID_PPV_ARGS(&ip
));
195 release(std::exchange(com_ptr_
, ip
));
199 T
* operator->() const { return com_ptr_
; }
201 T
& operator*() const { return *com_ptr_
; }
203 /* Necessary for assigning com_ptr_ from functions like
204 CoCreateInstance which require a 'void**' */
211 T
* get() const { return com_ptr_
; }
212 operator T
* () const { return get(); }
214 void clear() { assign(nullptr); }
216 bool is() const { return (com_ptr_
!= nullptr); }
217 operator bool() const { return is(); }
220 static void addRef(T
* ptr
)
226 static void release(T
* ptr
)
237 release(std::exchange(com_ptr_
, ptr
));
243 // A class to use with functions taking an out pointer argument,
244 // that needs to be freed with CoTaskMemFree - like SHGetKnownFolderPath
245 template <typename T
> class CoTaskMemAllocated
248 ~CoTaskMemAllocated() { CoTaskMemFree(m_pv
); }
252 CoTaskMemFree(std::exchange(m_pv
, nullptr));
256 operator T
*() { return m_pv
; }
264 /* Typedefs for some popular COM interfaces */
265 typedef sal::systools::COMReference
<IDataObject
> IDataObjectPtr
;
267 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */