-
Turning API Errors into Basic Errors
I was searching thru the MSDN, and I happened to stumble upon a section called
"Turning API Errors into Basic Errors" from Bruce McKinney's Hardcore VB
book. In it, he demonstrates how errors from API calls can be raised to
calling routines via the Err.Raise method (example code follows). I was
wondering how many people follow this strategy and how often. Usually I
don't expect an API to fail so I rarely even check return values, let alone
raise them back as errors to the calling routines. This method has always
worked for me but now that I think about, it seems kind of foolish of me
to ignore return values. On the other hand, I honestly cannot think of any
situation where this lack of error handling has hurt me.
- Jim
c = GetTempPath(cMaxPath, sRet)
If c = 0 Then ApiRaise Err.LastDllError
Sub ApiRaise(ByVal e As Long)
Err.Raise vbObjectError + 29000 + e, _
App.ExeName & “.Windows”, ApiError(e)
End Sub
Function ApiError(ByVal e As Long) As String
Dim s As String, c As Long
s = String(256, 0)
c = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM Or _
FORMAT_MESSAGE_IGNORE_INSERTS, _
pNull, e, 0&, s, Len(s), ByVal pNull)
If c Then ApiError = Left$(s, c)
End Function
-
Re: Turning API Errors into Basic Errors
Jim -
Basically, it is worth checking return values when it is possible that a
memory allocation might fail, and when a device might not be available.
--
---------------------------------------
Mark Alexander Bertenshaw
Programmer/Analyst
Prime Response
Brentford
UK
"Jim Pragit" <James.Pragit@BakerNet.com> wrote in message
news:38f60cd8$1@news.devx.com...
>
> I was searching thru the MSDN, and I happened to stumble upon a section
called
> "Turning API Errors into Basic Errors" from Bruce McKinney's Hardcore VB
> book. In it, he demonstrates how errors from API calls can be raised to
> calling routines via the Err.Raise method (example code follows). I was
> wondering how many people follow this strategy and how often. Usually I
> don't expect an API to fail so I rarely even check return values, let
alone
> raise them back as errors to the calling routines. This method has always
> worked for me but now that I think about, it seems kind of foolish of me
> to ignore return values. On the other hand, I honestly cannot think of
any
> situation where this lack of error handling has hurt me.
>
> - Jim
>
> c = GetTempPath(cMaxPath, sRet)
> If c = 0 Then ApiRaise Err.LastDllError
>
> Sub ApiRaise(ByVal e As Long)
> Err.Raise vbObjectError + 29000 + e, _
> App.ExeName & ".Windows", ApiError(e)
> End Sub
>
> Function ApiError(ByVal e As Long) As String
> Dim s As String, c As Long
> s = String(256, 0)
> c = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM Or _
> FORMAT_MESSAGE_IGNORE_INSERTS, _
> pNull, e, 0&, s, Len(s), ByVal pNull)
> If c Then ApiError = Left$(s, c)
> End Function
>
-
Re: Turning API Errors into Basic Errors
Jim -
Basically, it is worth checking return values when it is possible that a
memory allocation might fail, and when a device might not be available.
--
---------------------------------------
Mark Alexander Bertenshaw
Programmer/Analyst
Prime Response
Brentford
UK
"Jim Pragit" <James.Pragit@BakerNet.com> wrote in message
news:38f60cd8$1@news.devx.com...
>
> I was searching thru the MSDN, and I happened to stumble upon a section
called
> "Turning API Errors into Basic Errors" from Bruce McKinney's Hardcore VB
> book. In it, he demonstrates how errors from API calls can be raised to
> calling routines via the Err.Raise method (example code follows). I was
> wondering how many people follow this strategy and how often. Usually I
> don't expect an API to fail so I rarely even check return values, let
alone
> raise them back as errors to the calling routines. This method has always
> worked for me but now that I think about, it seems kind of foolish of me
> to ignore return values. On the other hand, I honestly cannot think of
any
> situation where this lack of error handling has hurt me.
>
> - Jim
>
> c = GetTempPath(cMaxPath, sRet)
> If c = 0 Then ApiRaise Err.LastDllError
>
> Sub ApiRaise(ByVal e As Long)
> Err.Raise vbObjectError + 29000 + e, _
> App.ExeName & ".Windows", ApiError(e)
> End Sub
>
> Function ApiError(ByVal e As Long) As String
> Dim s As String, c As Long
> s = String(256, 0)
> c = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM Or _
> FORMAT_MESSAGE_IGNORE_INSERTS, _
> pNull, e, 0&, s, Len(s), ByVal pNull)
> If c Then ApiError = Left$(s, c)
> End Function
>
-
Re: Turning API Errors into Basic Errors
Mark,
Thanks for the tips, those are good things to keep in mind. I take it
from the lack of response, that not many people follow Bruce McKinney's suggestion.
- Jim
"Mark Alexander Bertenshaw" <Mark.Bertenshaw@virgin.net> wrote:
>Jim -
>
>Basically, it is worth checking return values when it is possible that a
>memory allocation might fail, and when a device might not be available.
>
>--
>
>---------------------------------------
>Mark Alexander Bertenshaw
>Programmer/Analyst
>Prime Response
>Brentford
>UK
>"Jim Pragit" <James.Pragit@BakerNet.com> wrote in message
>news:38f60cd8$1@news.devx.com...
>>
>> I was searching thru the MSDN, and I happened to stumble upon a section
>called
>> "Turning API Errors into Basic Errors" from Bruce McKinney's Hardcore
VB
>> book. In it, he demonstrates how errors from API calls can be raised
to
>> calling routines via the Err.Raise method (example code follows). I was
>> wondering how many people follow this strategy and how often. Usually
I
>> don't expect an API to fail so I rarely even check return values, let
>alone
>> raise them back as errors to the calling routines. This method has always
>> worked for me but now that I think about, it seems kind of foolish of
me
>> to ignore return values. On the other hand, I honestly cannot think of
>any
>> situation where this lack of error handling has hurt me.
>>
>> - Jim
>>
>> c = GetTempPath(cMaxPath, sRet)
>> If c = 0 Then ApiRaise Err.LastDllError
>>
>> Sub ApiRaise(ByVal e As Long)
>> Err.Raise vbObjectError + 29000 + e, _
>> App.ExeName & ".Windows", ApiError(e)
>> End Sub
>>
>> Function ApiError(ByVal e As Long) As String
>> Dim s As String, c As Long
>> s = String(256, 0)
>> c = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM Or _
>> FORMAT_MESSAGE_IGNORE_INSERTS, _
>> pNull, e, 0&, s, Len(s), ByVal pNull)
>> If c Then ApiError = Left$(s, c)
>> End Function
-
Re: Turning API Errors into Basic Errors
Mark,
Thanks for the tips, those are good things to keep in mind. I take it
from the lack of response, that not many people follow Bruce McKinney's suggestion.
- Jim
"Mark Alexander Bertenshaw" <Mark.Bertenshaw@virgin.net> wrote:
>Jim -
>
>Basically, it is worth checking return values when it is possible that a
>memory allocation might fail, and when a device might not be available.
>
>--
>
>---------------------------------------
>Mark Alexander Bertenshaw
>Programmer/Analyst
>Prime Response
>Brentford
>UK
>"Jim Pragit" <James.Pragit@BakerNet.com> wrote in message
>news:38f60cd8$1@news.devx.com...
>>
>> I was searching thru the MSDN, and I happened to stumble upon a section
>called
>> "Turning API Errors into Basic Errors" from Bruce McKinney's Hardcore
VB
>> book. In it, he demonstrates how errors from API calls can be raised
to
>> calling routines via the Err.Raise method (example code follows). I was
>> wondering how many people follow this strategy and how often. Usually
I
>> don't expect an API to fail so I rarely even check return values, let
>alone
>> raise them back as errors to the calling routines. This method has always
>> worked for me but now that I think about, it seems kind of foolish of
me
>> to ignore return values. On the other hand, I honestly cannot think of
>any
>> situation where this lack of error handling has hurt me.
>>
>> - Jim
>>
>> c = GetTempPath(cMaxPath, sRet)
>> If c = 0 Then ApiRaise Err.LastDllError
>>
>> Sub ApiRaise(ByVal e As Long)
>> Err.Raise vbObjectError + 29000 + e, _
>> App.ExeName & ".Windows", ApiError(e)
>> End Sub
>>
>> Function ApiError(ByVal e As Long) As String
>> Dim s As String, c As Long
>> s = String(256, 0)
>> c = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM Or _
>> FORMAT_MESSAGE_IGNORE_INSERTS, _
>> pNull, e, 0&, s, Len(s), ByVal pNull)
>> If c Then ApiError = Left$(s, c)
>> End Function
-
Re: Turning API Errors into Basic Errors
Jim Pragit James.Pragit wrote in message <38f74486$1@news.devx.com>...
>
>Mark,
> Thanks for the tips, those are good things to keep in mind. I take it
>from the lack of response, that not many people follow Bruce McKinney's
suggestion.
>
>- Jim
>
I tend to encapsulate my API calls, and check their return values for errors
on a regular basis actually. If I do get a non-expected return value, I'll
call FormatMessage and GetLastError (I use a win.tlb and my own typelib for
API calls, so GetLastError is actually useful).
I've done this ever since I had a program reading from the registry to get
modem information, and it failed on non-administrator accounts because those
keys were read-only, but I was requesting read/write permissions. Since I
wasn't doing any error checking then, all I knew was there was a problem I
couldn't reproduce (having administrator rights) with getting the registry
handles. After debugging it, putting in error checks to the API became
second nature.
--
Colin McGuigan
-
Re: Turning API Errors into Basic Errors
Jim Pragit James.Pragit wrote in message <38f74486$1@news.devx.com>...
>
>Mark,
> Thanks for the tips, those are good things to keep in mind. I take it
>from the lack of response, that not many people follow Bruce McKinney's
suggestion.
>
>- Jim
>
I tend to encapsulate my API calls, and check their return values for errors
on a regular basis actually. If I do get a non-expected return value, I'll
call FormatMessage and GetLastError (I use a win.tlb and my own typelib for
API calls, so GetLastError is actually useful).
I've done this ever since I had a program reading from the registry to get
modem information, and it failed on non-administrator accounts because those
keys were read-only, but I was requesting read/write permissions. Since I
wasn't doing any error checking then, all I knew was there was a problem I
couldn't reproduce (having administrator rights) with getting the registry
handles. After debugging it, putting in error checks to the API became
second nature.
--
Colin McGuigan
-
Re: Turning API Errors into Basic Errors
What intrigued me about Bruce McKinney's code was the prospect of making APIs
work like VB statements. That is, instead of returning an error code, they
would raise a run-time error. Right now, I usually ignore the return value.
So I usually call an API like this:
Call SHFileOperation(udtSHFileOp)
If this API returned an error code, I would be none the wiser. Alternatively,
I could code this:
lngReturn = SHFileOperation(udtSHFileOp)
If lngReturn <> 0 Then
'do something to handle error
End If
However, I'm not too crazy about constantly checking return codes like this.
(I know this is just a matter of opinion but to me it obfuscates the routine's
logic.) But what if I coded something like this:
CallAPI SHFileOperation(udtSHFileOp)
And then in some standard module, I had these two utility routines:
Public Sub CallAPI(ByVal ErrorCode As Long)
'*****************************************************************
'* This routine is designed to turn a Win32 API error code into *
'* a Visual Basic run-time error. *
'*****************************************************************
Const WIN_ERROR_BASE As Long = 29000 'Bruce McKinney's error base
Dim lngErrNbr As Long
Dim strErrMsg As String
If ErrorCode <> 0 Then
lngErrNbr = vbObjectError + WIN_ERROR_BASE + ErrorCode
strErrMsg = APIErrorMsg(ErrorCode)
Err.Raise lngErrNbr, "Windows", strErrMsg
End If
End Sub
Private Function APIErrorMsg(ByVal ErrorCode As Long) As String
'*****************************************************************
'* This function will return an error message given the return *
'* code from a Windows API call. *
'*****************************************************************
Const FORMAT_MESSAGE_IGNORE_INSERTS As Long = 512
Const FORMAT_MESSAGE_FROM_SYSTEM As Long = 4096
Dim strMsg As String
Dim lngErrorCode As Long
strMsg = Space$(256)
lngErrorCode = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM Or _
FORMAT_MESSAGE_IGNORE_INSERTS, 0&, ErrorCode, 0&, _
strMsg, Len(strMsg), 0&)
If lngErrorCode <> 0 Then
APIErrorMsg = Left$(strMsg, lngErrorCode)
End If
End Function
This would allow me to call APIs almost the same way I do now, yet provide
much improved error handling. And if I decide to go back and change all
my existing code to use this strategy, I need only change all my Call statements
to CallAPI. Any and all thoughts welcome. 
- Jim
-
Re: Turning API Errors into Basic Errors
What intrigued me about Bruce McKinney's code was the prospect of making APIs
work like VB statements. That is, instead of returning an error code, they
would raise a run-time error. Right now, I usually ignore the return value.
So I usually call an API like this:
Call SHFileOperation(udtSHFileOp)
If this API returned an error code, I would be none the wiser. Alternatively,
I could code this:
lngReturn = SHFileOperation(udtSHFileOp)
If lngReturn <> 0 Then
'do something to handle error
End If
However, I'm not too crazy about constantly checking return codes like this.
(I know this is just a matter of opinion but to me it obfuscates the routine's
logic.) But what if I coded something like this:
CallAPI SHFileOperation(udtSHFileOp)
And then in some standard module, I had these two utility routines:
Public Sub CallAPI(ByVal ErrorCode As Long)
'*****************************************************************
'* This routine is designed to turn a Win32 API error code into *
'* a Visual Basic run-time error. *
'*****************************************************************
Const WIN_ERROR_BASE As Long = 29000 'Bruce McKinney's error base
Dim lngErrNbr As Long
Dim strErrMsg As String
If ErrorCode <> 0 Then
lngErrNbr = vbObjectError + WIN_ERROR_BASE + ErrorCode
strErrMsg = APIErrorMsg(ErrorCode)
Err.Raise lngErrNbr, "Windows", strErrMsg
End If
End Sub
Private Function APIErrorMsg(ByVal ErrorCode As Long) As String
'*****************************************************************
'* This function will return an error message given the return *
'* code from a Windows API call. *
'*****************************************************************
Const FORMAT_MESSAGE_IGNORE_INSERTS As Long = 512
Const FORMAT_MESSAGE_FROM_SYSTEM As Long = 4096
Dim strMsg As String
Dim lngErrorCode As Long
strMsg = Space$(256)
lngErrorCode = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM Or _
FORMAT_MESSAGE_IGNORE_INSERTS, 0&, ErrorCode, 0&, _
strMsg, Len(strMsg), 0&)
If lngErrorCode <> 0 Then
APIErrorMsg = Left$(strMsg, lngErrorCode)
End If
End Function
This would allow me to call APIs almost the same way I do now, yet provide
much improved error handling. And if I decide to go back and change all
my existing code to use this strategy, I need only change all my Call statements
to CallAPI. Any and all thoughts welcome. 
- Jim
-
Re: Turning API Errors into Basic Errors
Jim Pragit wrote in message <38f764e1$1@news.devx.com>...
>
>What intrigued me about Bruce McKinney's code was the prospect of making
APIs
>work like VB statements. That is, instead of returning an error code, they
>would raise a run-time error. Right now, I usually ignore the return
value.
> So I usually call an API like this:
>
> Call SHFileOperation(udtSHFileOp)
>
>If this API returned an error code, I would be none the wiser.
Alternatively,
>I could code this:
>
> lngReturn = SHFileOperation(udtSHFileOp)
> If lngReturn <> 0 Then
> 'do something to handle error
> End If
>
>However, I'm not too crazy about constantly checking return codes like
this.
>(I know this is just a matter of opinion but to me it obfuscates the
routine's
>logic.) But what if I coded something like this:
<snip code>
>This would allow me to call APIs almost the same way I do now, yet provide
>much improved error handling. And if I decide to go back and change all
>my existing code to use this strategy, I need only change all my Call
statements
>to CallAPI. Any and all thoughts welcome. 
>
>- Jim
The problem with this idea as provided is that you've got a severe lack of
standardization amongst the APIs. For example, for a number of them, a
non-zero value does indeed indicate an error code. However, another large
majority use a zero value to indicate an error, and a non-zero value to
indicate success (eg, GetDiskFreeSpace[Ex], GetComputerName, etc), and a
number of others use the return value to return a handle or a length of a
string (eg, GetPrivateProfileString).
You could, of course, have two subroutines, one for '0 = success' and one
for '0 = error'...but that gets into problems of it's own. This is why I
tend to encapsulate my API calls.
--
Colin McGuigan
-
Re: Turning API Errors into Basic Errors
Jim Pragit wrote in message <38f764e1$1@news.devx.com>...
>
>What intrigued me about Bruce McKinney's code was the prospect of making
APIs
>work like VB statements. That is, instead of returning an error code, they
>would raise a run-time error. Right now, I usually ignore the return
value.
> So I usually call an API like this:
>
> Call SHFileOperation(udtSHFileOp)
>
>If this API returned an error code, I would be none the wiser.
Alternatively,
>I could code this:
>
> lngReturn = SHFileOperation(udtSHFileOp)
> If lngReturn <> 0 Then
> 'do something to handle error
> End If
>
>However, I'm not too crazy about constantly checking return codes like
this.
>(I know this is just a matter of opinion but to me it obfuscates the
routine's
>logic.) But what if I coded something like this:
<snip code>
>This would allow me to call APIs almost the same way I do now, yet provide
>much improved error handling. And if I decide to go back and change all
>my existing code to use this strategy, I need only change all my Call
statements
>to CallAPI. Any and all thoughts welcome. 
>
>- Jim
The problem with this idea as provided is that you've got a severe lack of
standardization amongst the APIs. For example, for a number of them, a
non-zero value does indeed indicate an error code. However, another large
majority use a zero value to indicate an error, and a non-zero value to
indicate success (eg, GetDiskFreeSpace[Ex], GetComputerName, etc), and a
number of others use the return value to return a handle or a length of a
string (eg, GetPrivateProfileString).
You could, of course, have two subroutines, one for '0 = success' and one
for '0 = error'...but that gets into problems of it's own. This is why I
tend to encapsulate my API calls.
--
Colin McGuigan
-
Re: Turning API Errors into Basic Errors
Good point. I took another look at Bruce's code and he pretty much uses the
two subroutines approach. (Actually, he inlines the error handling when 0
indicates failure). I'm not sure if he follows this strategy throughout
the whole book or just for this section on API error handling.
- Jim
"Colin McGuigan" <colin@chicor.com> wrote:
>The problem with this idea as provided is that you've got a severe lack
of
>standardization amongst the APIs. For example, for a number of them, a
>non-zero value does indeed indicate an error code. However, another large
>majority use a zero value to indicate an error, and a non-zero value to
>indicate success (eg, GetDiskFreeSpace[Ex], GetComputerName, etc), and a
>number of others use the return value to return a handle or a length of
a
>string (eg, GetPrivateProfileString).
>
>You could, of course, have two subroutines, one for '0 = success' and one
>for '0 = error'...but that gets into problems of it's own. This is why
I
>tend to encapsulate my API calls.
>--
>Colin McGuigan
-
Re: Turning API Errors into Basic Errors
Good point. I took another look at Bruce's code and he pretty much uses the
two subroutines approach. (Actually, he inlines the error handling when 0
indicates failure). I'm not sure if he follows this strategy throughout
the whole book or just for this section on API error handling.
- Jim
"Colin McGuigan" <colin@chicor.com> wrote:
>The problem with this idea as provided is that you've got a severe lack
of
>standardization amongst the APIs. For example, for a number of them, a
>non-zero value does indeed indicate an error code. However, another large
>majority use a zero value to indicate an error, and a non-zero value to
>indicate success (eg, GetDiskFreeSpace[Ex], GetComputerName, etc), and a
>number of others use the return value to return a handle or a length of
a
>string (eg, GetPrivateProfileString).
>
>You could, of course, have two subroutines, one for '0 = success' and one
>for '0 = error'...but that gets into problems of it's own. This is why
I
>tend to encapsulate my API calls.
>--
>Colin McGuigan
-
Re: Turning API Errors into Basic Errors
Jim,
Sorry this is a bit belated but I also use ApiRaise and ApiError. Which I
supplement with the following
<Code>
' Use with API routines that return a Win32 error value
Public Sub ApiRaiseIf(ByVal vlError As Long)
If vlError <> 0 Then ApiRaise vlError
End Sub
' Use with API routines that set lastdllerror and return zero to indicate
' failure
Public Function ApiRaiseIfNull(ByVal ApiRetVal As Long) As Long
If ApiRetVal = 0 Then
ApiRaise Err.LastDllError
Else
ApiRaiseIfNull = ApiRetVal
End If
End Function
</Code>
Also in place of vbObjectError + 29000 I use HRESULT_WIN32 (&H80070000).
--
Anthony Jones
Secta Group Ltd
AnthonyWJones@msn.com
-
Re: Turning API Errors into Basic Errors
Jim,
Sorry this is a bit belated but I also use ApiRaise and ApiError. Which I
supplement with the following
<Code>
' Use with API routines that return a Win32 error value
Public Sub ApiRaiseIf(ByVal vlError As Long)
If vlError <> 0 Then ApiRaise vlError
End Sub
' Use with API routines that set lastdllerror and return zero to indicate
' failure
Public Function ApiRaiseIfNull(ByVal ApiRetVal As Long) As Long
If ApiRetVal = 0 Then
ApiRaise Err.LastDllError
Else
ApiRaiseIfNull = ApiRetVal
End If
End Function
</Code>
Also in place of vbObjectError + 29000 I use HRESULT_WIN32 (&H80070000).
--
Anthony Jones
Secta Group Ltd
AnthonyWJones@msn.com
Posting Permissions
- You may not post new threads
- You may not post replies
- You may not post attachments
- You may not edit your posts
Forum Rules
|
Top DevX Stories
Easy Web Services with SQL Server 2005 HTTP Endpoints
JavaOne 2005: Java Platform Roadmap Focuses on Ease of Development, Sun Focuses on the "Free" in F.O.S.S.
Wed Yourself to UML with the Power of Associations
Microsoft to Add AJAX Capabilities to ASP.NET
IBM's Cloudscape Versus MySQL
|
Bookmarks