Version 7.6.3.2-android, tag libreoffice-7.6.3.2-android
[LibreOffice.git] / include / systools / win32 / comtools.hxx
blob1d618051d2c7db8cddedf422496a2cd9392675bc
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 #pragma once
22 #include <string>
23 #include <string_view>
24 #include <stdexcept>
25 #include <type_traits>
26 #include <utility>
28 #include <prewin.h>
29 #include <objbase.h>
30 #include <postwin.h>
32 namespace sal::systools
34 /* Simple exception class for propagating COM errors */
35 class ComError : public std::runtime_error
37 public:
38 ComError(const std::string& message, HRESULT hr) :
39 std::runtime_error(message),
40 hr_(hr)
43 HRESULT GetHresult() const { return hr_; }
45 private:
46 HRESULT hr_;
49 /* Convert failed HRESULT to thrown ComError */
50 inline void ThrowIfFailed(HRESULT hr, std::string_view msg)
52 if (FAILED(hr))
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
61 public:
62 enum class WhenFailed
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)
78 std::abort();
80 mbUninit = SUCCEEDED(hr);
82 CoInitializeGuard(const CoInitializeGuard&) = delete; // non-construction-copyable
83 void operator=(const CoInitializeGuard&) = delete; // non-copyable
84 ~CoInitializeGuard()
86 if (mbUninit)
87 ::CoUninitialize();
90 private:
91 bool mbUninit;
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>
102 class COMReference
104 public:
105 /* Explicitly controllable whether AddRef will be called or not */
106 COMReference(T* comptr = nullptr, bool bAddRef = true) :
107 com_ptr_(comptr)
109 if (bAddRef)
110 addRef(com_ptr_);
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)
132 : com_ptr_(nullptr)
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_)
146 clear();
147 std::swap(com_ptr_, other.com_ptr_);
149 return *this;
152 COMReference<T>& operator=(T* comptr)
154 assign(comptr);
155 return *this;
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
163 T2* ip = nullptr;
164 HRESULT hr = E_POINTER;
165 if (com_ptr_)
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)
183 T* ip;
184 HRESULT hr = ::CoCreateInstance(clsid, pOuter, nCtx, IID_PPV_ARGS(&ip));
185 if (SUCCEEDED(hr))
186 release(std::exchange(com_ptr_, ip));
187 return hr;
190 HRESULT CoGetClassObject(REFCLSID clsid, DWORD nCtx = CLSCTX_ALL)
192 T* ip;
193 HRESULT hr = ::CoGetClassObject(clsid, nCtx, nullptr, IID_PPV_ARGS(&ip));
194 if (SUCCEEDED(hr))
195 release(std::exchange(com_ptr_, ip));
196 return hr;
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**' */
205 T** operator&()
207 clear();
208 return &com_ptr_;
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(); }
219 private:
220 static void addRef(T* ptr)
222 if (ptr)
223 ptr->AddRef();
226 static void release(T* ptr)
228 if (ptr)
229 ptr->Release();
232 void assign(T* ptr)
234 if (com_ptr_ == ptr)
235 return;
236 addRef(ptr);
237 release(std::exchange(com_ptr_, ptr));
240 T* com_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
247 public:
248 ~CoTaskMemAllocated() { CoTaskMemFree(m_pv); }
250 T** operator&()
252 CoTaskMemFree(std::exchange(m_pv, nullptr));
253 return &m_pv;
256 operator T*() { return m_pv; }
258 private:
259 T* m_pv = nullptr;
262 } // sal::systools
264 /* Typedefs for some popular COM interfaces */
265 typedef sal::systools::COMReference<IDataObject> IDataObjectPtr;
267 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */