Click here to Skip to main content
Licence CPOL
First Posted 4 Mar 2003
Views 43,661
Bookmarked 16 times

Raising API exceptions from Visual Basic .NET

By Duncan Edwards Jones | 4 Mar 2003
How to raise API exceptions from Visual basic .NET.

1

2
1 vote, 20.0%
3
1 vote, 20.0%
4
3 votes, 60.0%
5
4.63/5 - 5 votes
μ 4.63, σa 1.57 [?]

Introduction

Although the .NET framework wraps up the majority of Windows API in an easy to use and safe framework, there are occasions when a direct call to the Windows API cannot be avoided. In such cases it is useful to be able to provide meaningful exceptions should any error occur in the API call.

Telling the system that you want to be able to get the last API error

When you declare an API function in VB.NET, you set the attribute SetsLastError to indicate that if an error occurs while calling that function it should set the last error for you. For example, the declaration of the API call MessageBeep would be declared as:

 <DllImport("user32", EntryPoint:="MessageBeep", _ 
  SetLastError:=True, _ 
  CallingConvention:=CallingConvention.StdCall)> _
   Public Shared Function MessageBeep _ 
          ByVal uType As Int32 ) As Boolean  
   End Function

According to the documentation for this function on MSDN this function returns non-zero on success and sets the last error on failure. The parameter uType only accepts certain values so passing an incorrect value will set this error value to 87 meaning that the parameter is incorrect.

Getting the last error value

In Visual Basic 5 or 6 the error used to be held in Err.LastDllError. In Visual Basic .NET this has been replaced by the GetLastWin32Error.

Imports System.Runtime.InteropServices 
'\\ Print last api error number
Debug.Write(Marshal.GetLastWin32Error)

Getting the text description of this error

The Windows API has a function, FormatMessage that can be used to turn the number of the last error to a meaningful description. This is declared as:

   <DllImport("kernel32.dll", EntryPoint:="FormatMessageA", _ 
   CharSet:=CharSet.Ansi, _ 
   ExactSpelling:=True, _ 
   CallingConvention:=CallingConvention.StdCall)> _ 
   Public Shared Function FormatMessage( _
      ByVal dwFlags As Format_Message_Flags, ByVal lpSource As Int32, _ 
      ByVal dwMessageId As Int32,  ByVal dwLanguageId As Int32,  _
      ByVal lpBuffer As StringBuilder, _ 
      ByVal nSize As Int32, ByVal Arguments As Int32) As Int32 
    End Function

and used as:

  Imports System.Text
  Private Const FORMAT_MESSAGE_FROM_SYSTEM = &H1000 
   Private Const MAX_MESSAGE_LENGTH = 512 
    Private Function GetAPIErrorMessageDescription( _
                                 ByVal ApiErrNumber As Int32) As String 
         Dim sError As New StringBuilder(MAX_MESSAGE_LENGTH) 
         Dim lErrorMessageLength AsInt32 
         lErrorMessageLength = _
            FormatMessage(Format_Message_Flags.FORMAT_MESSAGE_FROM_SYSTEM,  _ 
                           0, ApiErrNumber, 0, sError, sError.Capacity, 0)     
         If lErrorMessageLength > 0 Then
            Return sError.ToString 
         End If
    End Function

Wrapping this up in a class derived from Exception

To be of use to the VB.NET Try..Catch..Finally syntax for error handling, this code must be wrapped up in a class that is derived from Exception.

 Public Class ApiException 
    Inherits ApplicationException

And the New constructor is overridden to put the last system error in place of the base class' Message member:

 Private msMessage As String
    Public Sub New()
      '\\ Get the error message from the last error from the windows API...
      msMessage = GetAPIErrorMessageDescription(Marshal.GetLastWin32Error) 
    End Sub
    Public Overrides ReadOnly Property Message() As String 
      Get 
         Return msMessage 
      End Get
    End Property

Using the ApiException class

You can now write wrappers for your API calls that raise ApiException. E.g.:

Public Sub ApiMessageBeep(ByVal uType As Int32) 
  If Not Messagebeep(uType) Then
     Throw New ApiException
  End If
End Sub

Acknowledgements

This article was originally published on the Merrion Computing website.

Appendix: Full class code

Imports System.Text
Imports System.Runtime.InteropServices
Public Class ApiException
    Inherits ApplicationException
#Region "API Declarations"
    <DllImport("kernel32.dll", EntryPoint:="FormatMessageA", _
 CharSet:=CharSet.Ansi, _
 ExactSpelling:=True, _
 CallingConvention:=CallingConvention.StdCall)> _
    Public Shared Function FormatMessage( _
                                  ByVal dwFlags As Format_Message_Flags, _
                                  ByVal lpSource As Int32, _
                                  ByVal dwMessageId As Int32, _
                                  ByVal dwLanguageId As Int32, _
                                  ByVal lpBuffer As StringBuilder, _
                                  ByVal nSize As Int32, _
                                  ByVal Arguments As Int32) As Int32
    End Function
#End Region
#Region "Private members"
    Private Const FORMAT_MESSAGE_FROM_SYSTEM = &H1000
    Private Const MAX_MESSAGE_LENGTH = 512
#End Region
#Region "Private member variables "
    Private msFunctionName As String
    Private msMessage As String
#End Region
    Private Function GetAPIErrorMessageDescription(_
                                       ByVal ApiErrNumber As Int32) As String

        Dim sError As New StringBuilder(MAX_MESSAGE_LENGTH)
        Dim lErrorMessageLength As Int32

        lErrorMessageLength = _
                 FormatMessage(Format_Message_Flags.FORMAT_MESSAGE_FROM_SYSTEM, 
                                0, ApiErrNumber, 0, sError, sError.Capacity, 0)
        If lErrorMessageLength > 0 Then
            Return sError.ToString
        End If
    End Function
    Public Sub New(ByVal ErrNumber As Int32, ByVal FunctionName As String)

        '\\ Get the error message from the windows API...
        msMessage = GetAPIErrorMessageDescription(ErrNumber)
        msFunctionName = FunctionName
    End Sub
    Public Sub New(ByVal ErrNumber As Int32)

        '\\ Get the error message from the windows API...
        msMessage = GetAPIErrorMessageDescription(ErrNumber)
    End Sub
    Public Sub New()
        '\\ Get the error message from the last error from the windows API...
        msMessage = GetAPIErrorMessageDescription(Marshal.GetLastWin32Error)
    End Sub
    Public Sub New(ByVal FunctionName As String)
        '\\ Get the error message from the last error from the windows API...
        msMessage = GetAPIErrorMessageDescription(Marshal.GetLastWin32Error)
        msFunctionName = FunctionName
    End Sub
    Public ReadOnly Property FunctionName() As String
        Get
            Return msFunctionName
        End Get
    End Property
    Public Overrides ReadOnly Property Message() As String
        Get
            Return msMessage
        End Get
    End Property
End Class

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

About the Author

Duncan Edwards Jones

Software Developer (Senior)
JP Morgan
Ireland Ireland

Member

Follow on Twitter Follow on Twitter
C# / SQL Server developer
Microsoft MVP 2006, 2007
Visual Basic .NET

Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
You must Sign In to use this message board. (secure sign-in)
 
Search this forum  
 FAQ
    Noise  Layout  Per page   
  Refresh
GeneralA simpler solution PinmemberMaximilian Hänel4:39 6 Mar '03  
GeneralRe: A simpler solution PinmemberMaximilian Hänel4:46 6 Mar '03  
GeneralRe: A simpler solution PinmemberMerrion5:53 6 Mar '03  
GeneralRe: A simpler solution PinmemberMaximilian Hänel10:17 6 Mar '03  

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

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

Permalink | Advertise | Privacy | Mobile
Web04 | 2.5.120210.1 | Last Updated 5 Mar 2003
Article Copyright 2003 by Duncan Edwards Jones
Everything else Copyright © CodeProject, 1999-2012
Terms of Use
Layout: fixed | fluid