/** @file
* MS COM / XPCOM Abstraction Layer - ErrorInfo class declaration.
*/
/*
* Copyright (C) 2006-2022 Oracle Corporation
*
* This file is part of VirtualBox Open Source Edition (OSE), as
* available from http://www.virtualbox.org. This file is free software;
* you can redistribute it and/or modify it under the terms of the GNU
* General Public License (GPL) as published by the Free Software
* Foundation, in version 2 as it comes in the "COPYING" file of the
* VirtualBox OSE distribution. VirtualBox OSE is distributed in the
* hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
*
* The contents of this file may alternatively be used under the terms
* of the Common Development and Distribution License Version 1.0
* (CDDL) only, as it comes in the "COPYING.CDDL" file of the
* VirtualBox OSE 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.
*/
#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 */