/** @file * MS COM / XPCOM Abstraction Layer - ErrorInfo class declaration. */ /* * Copyright (C) 2006-2022 Oracle and/or its affiliates. * * This file is part of VirtualBox base platform packages, as * available from https://www.virtualbox.org. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation, in version 3 of the * License. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . * * The contents of this file may alternatively be used under the terms * of the Common Development and Distribution License Version 1.0 * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included * in the VirtualBox distribution, in which case the provisions of the * CDDL are applicable instead of those of the GPL. * * You may elect to license modified versions of this file under the * terms and conditions of either the GPL or the CDDL or both. * * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0 */ #ifndef VBOX_INCLUDED_com_ErrorInfo_h #define VBOX_INCLUDED_com_ErrorInfo_h #ifndef RT_WITHOUT_PRAGMA_ONCE # pragma once #endif #include "VBox/com/ptr.h" #include "VBox/com/string.h" #include "VBox/com/Guid.h" #include "VBox/com/assert.h" /** @defgroup grp_com_errinfo ErrorInfo Classes * @ingroup grp_com * @{ */ COM_STRUCT_OR_CLASS(IProgress); COM_STRUCT_OR_CLASS(IVirtualBoxErrorInfo); namespace com { /** * General discussion: * * In COM all errors are stored on a per thread basis. In general this means * only _one_ active error is possible per thread. A new error will overwrite * the previous one. To prevent this use MultiResult or ErrorInfoKeeper (see * below). The implementations in MSCOM/XPCOM differ slightly, but the details * are handled by this glue code. * * We have different classes which are involved in the error management. I try * to describe them separately to make clear what they are there for. * * ErrorInfo: * * This class is able to retrieve the per thread error and store it into its * member variables. This class can also handle non-VirtualBox errors (like * standard COM errors). * * ProgressErrorInfo: * * This is just a simple wrapper class to get the ErrorInfo stored within an * IProgress object. That is the error which was stored when the progress * object was in use and not an error produced by IProgress itself. * * IVirtualBoxErrorInfo: * * The VirtualBox interface class for accessing error information from Main * clients. This class is also used for storing the error information in the * thread context. * * ErrorInfoKeeper: * * A helper class which stores the current per thread info internally. After * calling methods which may produce other errors it is possible to restore * the previous error and therefore restore the situation before calling the * other methods. * * MultiResult: * * Creating an instance of MultiResult turns error chain saving on. All errors * which follow will be saved in a chain for later access. * * COMErrorInfo (Qt/Gui only): * * The Qt GUI does some additional work for saving errors. Because we create * wrappers for _every_ COM call, it is possible to automatically save the * error info after the execution. This allow some additional info like saving * the callee. Please note that this error info is saved on the client side * and therefore locally to the object instance. See COMBaseWithEI, * COMErrorInfo and the generated COMWrappers.cpp in the GUI. * * Errors itself are set in VirtualBoxBase::setErrorInternal. First a * IVirtualBoxErrorInfo object is created and the given error is saved within. * If MultiResult is active the current per thread error is fetched and * attached to the new created IVirtualBoxErrorInfo object. Next this object is * set as the new per thread error. * * Some general hints: * * - Always use setError, especially when you are working in an asynchronous thread * to indicate an error. Otherwise the error information itself will not make * it into the client. * */ /** * The ErrorInfo class provides a convenient way to retrieve error * information set by the most recent interface method, that was invoked on * the current thread and returned an unsuccessful result code. * * Once the instance of this class is created, the error information for * the current thread is cleared. * * There is no sense to use instances of this class after the last * invoked interface method returns a success. * * The class usage pattern is as follows: * * IFoo *foo; * ... * HRESULT rc = foo->SomeMethod(); * if (FAILED(rc)) { * ErrorInfo info(foo); * if (info.isFullAvailable()) { * printf("error message = %ls\n", info.getText().raw()); * } * } * * * This class fetches error information using the IErrorInfo interface on * Win32 (MS COM) or the nsIException interface on other platforms (XPCOM), * or the extended IVirtualBoxErrorInfo interface when when it is available * (i.e. a given IErrorInfo or nsIException instance implements it). * Currently, IVirtualBoxErrorInfo is only available for VirtualBox components. * * ErrorInfo::isFullAvailable() and ErrorInfo::isBasicAvailable() determine * what level of error information is available. If #isBasicAvailable() * returns true, it means that only IErrorInfo or nsIException is available as * the source of information (depending on the platform), but not * IVirtualBoxErrorInfo. If #isFullAvailable() returns true, it means that all * three interfaces are available. If both methods return false, no error info * is available at all. * * Here is a table of correspondence between this class methods and * and IErrorInfo/nsIException/IVirtualBoxErrorInfo attributes/methods: * * ErrorInfo IErrorInfo nsIException IVirtualBoxErrorInfo * -------------------------------------------------------------------- * getResultCode -- result resultCode * getIID GetGUID -- interfaceID * getComponent GetSource -- component * getText GetDescription message text * * '--' means that this interface does not provide the corresponding portion * of information, therefore it is useless to query it if only * #isBasicAvailable() returns true. As it can be seen, the amount of * information provided at the basic level, depends on the platform * (MS COM or XPCOM). */ class ErrorInfo { public: /** * Constructs a new, "interfaceless" ErrorInfo instance that takes * the error information possibly set on the current thread by an * interface method of some COM component or by the COM subsystem. * * This constructor is useful, for example, after an unsuccessful attempt * to instantiate (create) a component, so there is no any valid interface * pointer available. */ explicit ErrorInfo() : mIsBasicAvailable(false), mIsFullAvailable(false), mResultCode(S_OK), mResultDetail(0), m_pNext(NULL) { init(); } ErrorInfo(IUnknown *pObj, const GUID &aIID) : mIsBasicAvailable(false), mIsFullAvailable(false), mResultCode(S_OK), mResultDetail(0), m_pNext(NULL) { init(pObj, aIID); } /** Specialization for the IVirtualBoxErrorInfo smart pointer */ ErrorInfo(const ComPtr &aPtr) : mIsBasicAvailable(false), mIsFullAvailable(false) , mResultCode(S_OK), mResultDetail(0) { init(aPtr); } /** * Constructs a new ErrorInfo instance from the IVirtualBoxErrorInfo * interface pointer. If this pointer is not NULL, both #isFullAvailable() * and #isBasicAvailable() will return |true|. * * @param aInfo pointer to the IVirtualBoxErrorInfo interface that * holds error info to be fetched by this instance */ ErrorInfo(IVirtualBoxErrorInfo *aInfo) : mIsBasicAvailable(false), mIsFullAvailable(false) , mResultCode(S_OK), mResultDetail(0) { init(aInfo); } ErrorInfo(const ErrorInfo &x) { copyFrom(x); } virtual ~ErrorInfo() { cleanup(); } ErrorInfo& operator=(const ErrorInfo& x) { cleanup(); copyFrom(x); return *this; } /** * Returns whether basic error info is actually available for the current * thread. If the instance was created from an interface pointer that * supports basic error info and successfully provided it, or if it is an * "interfaceless" instance and there is some error info for the current * thread, the returned value will be true. * * See the class description for details about the basic error info level. * * The appropriate methods of this class provide meaningful info only when * this method returns true (otherwise they simply return NULL-like values). */ bool isBasicAvailable() const { return mIsBasicAvailable; } /** * Returns whether full error info is actually available for the current * thread. If the instance was created from an interface pointer that * supports full error info and successfully provided it, or if it is an * "interfaceless" instance and there is some error info for the current * thread, the returned value will be true. * * See the class description for details about the full error info level. * * The appropriate methods of this class provide meaningful info only when * this method returns true (otherwise they simply return NULL-like values). */ bool isFullAvailable() const { return mIsFullAvailable; } /** * Returns the COM result code of the failed operation. */ HRESULT getResultCode() const { return mResultCode; } /** * Returns the (optional) result detail code of the failed operation. */ LONG getResultDetail() const { return mResultDetail; } /** * Returns the IID of the interface that defined the error. */ const Guid& getInterfaceID() const { return mInterfaceID; } /** * Returns the name of the component that generated the error. */ const Bstr& getComponent() const { return mComponent; } /** * Returns the textual description of the error. */ const Bstr& getText() const { return mText; } /** * Returns the next error information object or @c NULL if there is none. */ const ErrorInfo* getNext() const { return m_pNext; } /** * Returns the name of the interface that defined the error */ const Bstr& getInterfaceName() const { return mInterfaceName; } /** * Returns the IID of the interface that returned the error. * * This method returns a non-null IID only if the instance was created * using template \ ErrorInfo(I *i) or * template \ ErrorInfo(const ComPtr &i) constructor. * * @todo broken ErrorInfo documentation links, possibly misleading. */ const Guid& getCalleeIID() const { return mCalleeIID; } /** * Returns the name of the interface that returned the error * * This method returns a non-null name only if the instance was created * using template \ ErrorInfo(I *i) or * template \ ErrorInfo(const ComPtr &i) constructor. * * @todo broken ErrorInfo documentation links, possibly misleading. */ const Bstr& getCalleeName() const { return mCalleeName; } HRESULT getVirtualBoxErrorInfo(ComPtr &pVirtualBoxErrorInfo); /** * Resets all collected error information. #isBasicAvailable() and * #isFullAvailable will return @c true after this method is called. */ void setNull() { cleanup(); } protected: ErrorInfo(bool /* aDummy */) : mIsBasicAvailable(false), mIsFullAvailable(false), mResultCode(S_OK), m_pNext(NULL) { } void copyFrom(const ErrorInfo &x); void cleanup(); void init(bool aKeepObj = false); void init(IUnknown *aUnk, const GUID &aIID, bool aKeepObj = false); void init(IVirtualBoxErrorInfo *aInfo); bool mIsBasicAvailable : 1; bool mIsFullAvailable : 1; HRESULT mResultCode; LONG mResultDetail; Guid mInterfaceID; Bstr mComponent; Bstr mText; ErrorInfo *m_pNext; Bstr mInterfaceName; Guid mCalleeIID; Bstr mCalleeName; ComPtr mErrorInfo; }; /** * A convenience subclass of ErrorInfo that, given an IProgress interface * pointer, reads its errorInfo attribute and uses the returned * IVirtualBoxErrorInfo instance to construct itself. */ class ProgressErrorInfo : public ErrorInfo { public: /** * Constructs a new instance by fetching error information from the * IProgress interface pointer. If the progress object is not NULL, * its completed attribute is true, resultCode represents a failure, * and the errorInfo attribute returns a valid IVirtualBoxErrorInfo pointer, * both #isFullAvailable() and #isBasicAvailable() will return true. * * @param progress the progress object representing a failed operation */ ProgressErrorInfo(IProgress *progress); }; /** * A convenience subclass of ErrorInfo that allows to preserve the current * error info. Instances of this class fetch an error info object set on the * current thread and keep a reference to it, which allows to restore it * later using the #restore() method. This is useful to preserve error * information returned by some method for the duration of making another COM * call that may set its own error info and overwrite the existing * one. Preserving and restoring error information makes sense when some * method wants to return error information set by other call as its own * error information while it still needs to make another call before return. * * Instead of calling #restore() explicitly you may let the object destructor * do it for you, if you correctly limit the object's lifetime. * * The usage pattern is: * * rc = foo->method(); * if (FAILED(rc)) * { * ErrorInfoKeeper eik; * ... * // bar may return error info as well * bar->method(); * ... * // no need to call #restore() explicitly here because the eik's * // destructor will restore error info fetched after the failed * // call to foo before returning to the caller * return rc; * } * */ class ErrorInfoKeeper : public ErrorInfo { public: /** * Constructs a new instance that will fetch the current error info if * @a aIsNull is @c false (by default) or remain uninitialized (null) * otherwise. * * @param aIsNull @c true to prevent fetching error info and leave * the instance uninitialized. */ ErrorInfoKeeper(bool aIsNull = false) : ErrorInfo(false), mForgot(aIsNull) { if (!aIsNull) init(true /* aKeepObj */); } /** * Constructs a new instance from an ErrorInfo object, to inject a full * error info created elsewhere. * * @param aInfo @c true to prevent fetching error info and leave * the instance uninitialized. */ ErrorInfoKeeper(const ErrorInfo &aInfo) : ErrorInfo(false), mForgot(false) { copyFrom(aInfo); } /** * Destroys this instance and automatically calls #restore() which will * either restore error info fetched by the constructor or do nothing * if #forget() was called before destruction. */ ~ErrorInfoKeeper() { if (!mForgot) restore(); } /** * Tries to (re-)fetch error info set on the current thread. On success, * the previous error information, if any, will be overwritten with the * new error information. On failure, or if there is no error information * available, this instance will be reset to null. */ void fetch() { setNull(); mForgot = false; init(true /* aKeepObj */); } /** * Restores error info fetched by the constructor and forgets it * afterwards. Does nothing if the error info was forgotten by #forget(). * * @return COM result of the restore operation. */ HRESULT restore(); /** * Forgets error info fetched by the constructor to prevent it from * being restored by #restore() or by the destructor. */ void forget() { mForgot = true; } /** * Forgets error info fetched by the constructor to prevent it from * being restored by #restore() or by the destructor, and returns the * stored error info object to the caller. */ ComPtr takeError() { mForgot = true; return mErrorInfo; } private: bool mForgot : 1; }; } /* namespace com */ /** @} */ #endif /* !VBOX_INCLUDED_com_ErrorInfo_h */