summaryrefslogtreecommitdiffstats
path: root/include/VBox/com/ErrorInfo.h
diff options
context:
space:
mode:
Diffstat (limited to 'include/VBox/com/ErrorInfo.h')
-rw-r--r--include/VBox/com/ErrorInfo.h545
1 files changed, 545 insertions, 0 deletions
diff --git a/include/VBox/com/ErrorInfo.h b/include/VBox/com/ErrorInfo.h
new file mode 100644
index 00000000..10631a06
--- /dev/null
+++ b/include/VBox/com/ErrorInfo.h
@@ -0,0 +1,545 @@
+/** @file
+ * MS COM / XPCOM Abstraction Layer - ErrorInfo class declaration.
+ */
+
+/*
+ * Copyright (C) 2006-2023 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 <https://www.gnu.org/licenses>.
+ *
+ * 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:
+ * <code>
+ * IFoo *foo;
+ * ...
+ * HRESULT rc = foo->SomeMethod();
+ * if (FAILED(rc)) {
+ * ErrorInfo info(foo);
+ * if (info.isFullAvailable()) {
+ * printf("error message = %ls\n", info.getText().raw());
+ * }
+ * }
+ * </code>
+ *
+ * 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<IVirtualBoxErrorInfo> &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 \<class I\> ErrorInfo(I *i) or
+ * template \<class I\> ErrorInfo(const ComPtr<I> &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 \<class I\> ErrorInfo(I *i) or
+ * template \<class I\> ErrorInfo(const ComPtr<I> &i) constructor.
+ *
+ * @todo broken ErrorInfo documentation links, possibly misleading.
+ */
+ const Bstr& getCalleeName() const
+ {
+ return mCalleeName;
+ }
+
+ HRESULT getVirtualBoxErrorInfo(ComPtr<IVirtualBoxErrorInfo> &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<IUnknown> 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:
+ * <code>
+ * 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;
+ * }
+ * </code>
+ */
+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<IUnknown> takeError() { mForgot = true; return mErrorInfo; }
+
+private:
+
+ bool mForgot : 1;
+};
+
+} /* namespace com */
+
+/** @} */
+
+#endif /* !VBOX_INCLUDED_com_ErrorInfo_h */
+