Click here to Skip to main content
15,867,330 members
Articles / Desktop Programming / ATL
Article

Small ATL Tricks: Part One

Rate me:
Please Sign up or sign in to vote.
4.74/5 (15 votes)
1 Oct 20025 min read 118.2K   1.3K   50   14
This series of articles demonstrates how to use some of the cool features of ATL in a time and energy saving manner.

Sample Image - SmallATLTricks1.jpg

Introduction

This article is the first in the series of articles demonstrating some of the ATL's wonderful features in the form of small techniques used for implementation. This article covers the mechanism of error dispatch. Many programmers (atleast myself) run away from this feature because of the complications involved in implementing this mechanism of throwing every small error occuring here and there. The methods provided in this article enable a developer to implement a strong design strategy in an extensible and usable fashion. This article primarily aims at writing the code in ATL 3.0 and using the code in VB6 or .NET.

Error Dispatch mechanism in ATL

This feature involves the use of three interfaces ISupportErrorInfo, IErrorInfo and ICreateErrorInfo. The following are steps of making your COM component error information enabled:

  1. If you are using the ATL wizard for creation of your component, then you need to tick on the check box with the text Support ISupportErrorInfo while creating your Simple Object. This is as shown in the picture below:

    Image 2

    This step automatically adds the name of ISupportErrorInfo in the base class list of your component and implements the function InterfaceSupportsErrorInfo. Shown below are the lines generated as a result, and the names of the files in the source project in which they can be found:

    Foo.h

    class ATL_NO_VTABLE CFoo : 
      ...
      ...
      public ISupportErrorInfo,
      ...
    {
      ...
      ...
    
      BEGIN_COM_MAP(CFoo)
        ...
        COM_INTERFACE_ENTRY(ISupportErrorInfo)
        ...
      END_COM_MAP()
    
      ...
      ...
    
    // ISupportsErrorInfo
      STDMETHOD(InterfaceSupportsErrorInfo)(REFIID riid);
    };

    Foo.cpp

    STDMETHODIMP CFoo::InterfaceSupportsErrorInfo(REFIID riid)
    {
      static const IID* arr[] = 
      {
        &IID_IFoo
      };
      for (int i=0; i < sizeof(arr) / sizeof(arr[0]); i++)
      {
        if (InlineIsEqualGUID(*arr[i],riid))
          return S_OK;
      }
      return S_FALSE;
    }

    The implementation of this function has got nothing special. It just tells the caller that the object supports the error information and hence, if any of the methods of this component fails, then caller can query the error information from the component. The querying mechanism in C++ will take up another article itself and is thus not covered here. Moreover since the code is aimed to be used in VB6 and .NET, the error handling is easy enough because the error is raised as a run time exception automatically in the programs written in these environments.

  2. The second step involves writing a little of your own code for dispatching the error. This code will be used from a lot of places in the code, hence a small class is written for the dispatching purpose.

    May I have the pleasure of introducing our very own home made CError class. This class can be found in Error.h and Error.cpp files in the source archive. This class is nothing but a collection of 2 static functions that help in dispatching the errors. The first and main workhorse function is the DispatchError() function. The prototype of the function is as follows:

    HRESULT DispatchError(HRESULT hError, REFCLSID clsid,
      LPCTSTR szSource, LPCTSTR szDescription,
      DWORD dwHelpContext, LPCTSTR szHelpFileName);

    The function has most of the components required by the ATL error dispatch mechanism in C++ programmer friendly datatypes, i.e TCHAR strings. The function accepts an HRESULT as the error identifier. This can be filled in as E_FAIL for most of our custom errors for which we just want to display an error message to the programmer using our component. But, if by any chance the error is the one that might occur frequently during the runtime of the program and has to be shown to the user, then we must create an HRESULT using the macro MAKE_HRESULT to make it's identification easy for the programmer working on our component. The errors generated using this function gets into the Err object under VB6, from which the error information can be retreived from it's properties. A typical VB code for trapping errors and displaying proper message boxes is as shown:

    VBScript
    Public Sub MySub()
            'We must have a look at every error
            On Error GoTo ErrLabel
            ....
            ....
            
            'If everything goes fine, avoid error handling
            'by exiting from the subroutine
            Exit Sub
        
    ErrLabel:
            If Err.Number = MY_ERROR_CODE Then
                'Code to display user friendly message
            Else
                'We must throw back any unknown errors
                Err.Raise Err.Number, Err.Source, Err.Description, _
                        Err.HelpFile, Err.HelpContext
            End If
    End Sub

    Coming back to DispatchError(), the first thing that this function does is that it converts each string into LPOLESTR, which is understood by the error dispatch interfaces. But before doing so, it checks if any of the strings are NULL, which ofcourse if is the case it does not set the corresponding information in LPOLESTRs. If the description provided is NULL, the function checks if it can obtain some error information by itself. It checks if the error is a standard Win32 error code in the following line:

    if(HRESULT_FACILITY(hError) == FACILITY_WIN32)
    {
      // Code to get the Win32 error message
    }

    After confirming that the error is a standard Win32 error, it gets the message using the FormatMessage() API call.

  3. Finally we are ready with everything that ATL needs to dispatch an error. We use the CreateErrorInfo API call to create an object of ICreateErrorInfo and fill it with the error information, as shown below:

    // Get the ICreateErrorInfo Interface
    ICreateErrorInfo *pCreateErrorInfo = NULL;
    HRESULT hSuccess = CreateErrorInfo(&pCreateErrorInfo);
    ATLASSERT(SUCCEEDED(hSuccess));
    
    // Fill the error information into it
    pCreateErrorInfo->SetGUID(clsid);
    if(wszError != NULL)
      pCreateErrorInfo->SetDescription(wszError);
    if(wszSource != NULL)
      pCreateErrorInfo->SetSource(wszSource);
    if(wszHelpFile != NULL)
      pCreateErrorInfo->SetHelpFile(wszHelpFile);
    pCreateErrorInfo->SetHelpContext(dwHelpContext);

    After this step we query the ICreateErrorInfo object for IErrorInfo object, which has got the Get equivalents of all the Set functions used above.

    // Get the IErrorInfo interface
    IErrorInfo *pErrorInfo = NULL;
    hSuccess = pCreateErrorInfo->QueryInterface(IID_IErrorInfo,
          (LPVOID *)&pErrorInfo);

    This error object is then associated with the current thread, so that whenever any function returns an error code instead of S_OK, the caller can query the error information from the thread. The error object is associated with the current thread using the following API call:

    // Set this error information in the current thread
    hSuccess = SetErrorInfo(0, pErrorInfo);

    After the class for dispatching errors is ready, throwing the error from a component is a one line task:

    Foo.cpp

    STDMETHODIMP CFoo::GenerateError(BSTR Message)
    {
      ...
      ...
      
      return CError::DispatchError(E_FAIL, // This represents the error
        CLSID_Foo,  // This is the GUID of component throwing error
        _T("Foo"),  // This is generally displayed as the title
        szMessage2, // This is the description
        0,      // This is the context in the help file
        NULL);      // This is the path to the help file
    }

    The other function that now remains to be introduced is DispatchWin32Error(). This function is a simple wrapper around DispatchError(), for making it easier for the component designer to throw Win32 errors. The implementation of the function is as shown below:

    HRESULT CError::DispatchWin32Error(DWORD dwError, REFCLSID clsid,
      LPCTSTR szSource, DWORD dwHelpContext, LPCTSTR szHelpFileName)
    {
      // Dispatch the requested error message
      return DispatchError(
        HRESULT_FROM_WIN32(dwError), // Convert error no. to HRESULT
        clsid, szSource, NULL, dwHelpContext,
        szHelpFileName);
    }

    The function converts the error number into HRESULT containing the Win32 facility code using the macro HRESULT_FROM_WIN32. It also fills NULL in the szDescription parameter, because it is aware of the fact that the function DispatchError() looks out for the message corresponding to Win32 error code embedded in the HRESULT.

Conclusion

This concludes the error dispatch mechanism for ATL. We will move on to the next small technique of creating noncreatable objects in ATL in the next article.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here


Written By
Web Developer
India India
Ashutosh Arya is an Electrical Engineer. He is currently working for Softcrylic Inc.

Comments and Discussions

 
QuestionLicense Pin
Member 896106528-May-12 23:24
Member 896106528-May-12 23:24 
GeneralCustom Error codes Pin
Ajay E. Sonawane30-Dec-04 2:05
sussAjay E. Sonawane30-Dec-04 2:05 
GeneralRe: Custom Error codes Pin
Anonymous2-Jan-05 18:30
Anonymous2-Jan-05 18:30 
HRESULT structure basically contains information about Severity, Context, Facility & Error code. Following URL contains complete information about the HRESULT structure for 16 bit and 32 bit platforms http://msdn.microsoft.com/library/default.asp?url=/library/en-us/com/htm/error_5g6r.asp. To create our own HRESULT we need to set proper bits in this as per the specification. One can put the error information into this structure to create our own HRESULT.

Let me know if you have any further questions.
GeneralRe: Custom Error codes Pin
Anonymous3-Jan-05 3:26
Anonymous3-Jan-05 3:26 
GeneralRe: Custom Error codes Pin
sonawane ajay3-Jan-05 3:27
sonawane ajay3-Jan-05 3:27 
GeneralRe: Custom Error codes Pin
logicaldna21-Nov-05 19:11
logicaldna21-Nov-05 19:11 
GeneralAtlReportError Pin
Matt Berther1-Oct-02 9:04
Matt Berther1-Oct-02 9:04 
GeneralRe: AtlReportError Pin
Ashutosh Arya1-Oct-02 18:45
Ashutosh Arya1-Oct-02 18:45 
GeneralRe: AtlReportError Pin
Matt Berther1-Oct-02 19:36
Matt Berther1-Oct-02 19:36 
GeneralRe: AtlReportError Pin
Ashutosh Arya2-Oct-02 0:13
Ashutosh Arya2-Oct-02 0:13 
GeneralRe: AtlReportError Pin
Rashid Thadha1-Oct-02 23:13
Rashid Thadha1-Oct-02 23:13 
GeneralRe: AtlReportError Pin
Ashutosh Arya2-Oct-02 0:17
Ashutosh Arya2-Oct-02 0:17 
GeneralRe: AtlReportError Pin
Ryno Rijnsburger20-Jan-03 21:14
Ryno Rijnsburger20-Jan-03 21:14 
GeneralRe: AtlReportError Pin
Member 221140021-Aug-05 1:06
Member 221140021-Aug-05 1:06 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.