From: Don on
When using Visual Basic .NET with a reference to Interop.Outlook, is there a
way to get more detailed information about an error other than
Exception.Message or Exception.ToString? For example, Outlook's
MailItem.SaveAs method can return an error message of "The operation
failed", but I have no idea what that means. As a test, I tried SaveAs using
a filename that contained illegal characters and got the error message
"Internal Application Error", which isn't helpful for explaining that
particular error either. I suppose I could use a series of "Catch e As"
derived Exception Types, but I'm not even sure what to include. Instead, can
the "real" error simply be found in some property related to the base
Exception Class object? Thanks!


From: Walter Wang [MSFT] on
Hi Don,

Managed and unmanaged code can work together to handle exceptions. If a
method throws an exception in managed code, the common language runtime can
pass an HRESULT to a COM object. If a method fails in unmanaged code by
returning a failure HRESULT, the runtime throws an exception that can be
caught by managed code.

The runtime automatically maps the HRESULT from COM interop to more
specific exceptions. For example, E_ACCESSDENIED becomes
UnauthorizedAccessException, E_OUTOFMEMORY becomes OutOfMemoryException,
and so on.

If the HRESULT is a custom result or if it is unknown to the runtime, the
runtime passes a generic COMException to the client. The ErrorCode property
of the COMException contains the HRESULT value.

In your specific case, the behavior is correct since the error returned
from MailItem.SaveAs itself doesn't contain useful information. You can
verify this by creating a simple macro in Outlook VBA and the error will be
the same "The operation failed":

Sub test()
Dim f As Folder
Set f = Application.GetNamespace("MAPI").GetDefaultFolder(olFolderInbox)
Dim i As MailItem
For Each i In f.Items
i.SaveAs ("c:::1")
Exit For
Next
End Sub


Exception thrown from COM should be available in .NET through the
IErrorInfo interface automatically. For example, if you create following
COM component in VB6 which throws a exception:

Public Sub Test()
Err.Raise 12345, "Project1.Class1.Test()", "Custom exception from VB6"
End Sub

When you call it from VB.NET:

Try
Dim x As New Project1.Class1
x.Test()
Catch e As Exception
If TypeOf e Is System.Runtime.InteropServices.COMException Then
Dim ce As System.Runtime.InteropServices.COMException =
CType(e, System.Runtime.InteropServices.COMException)
Stop
End If
End Try

You will see:

e.Message = "Custom exception from VB6"
e.Source = "Project1.Class1.Test()"
ce.ErrorCode = &H800A3039

The error code is composed of several parts, the last part is 12345
(&H3039):

#Heath Stewart's Blog : Deciphering an HRESULT
http://blogs.msdn.com/heaths/archive/2005/07/21/441391.aspx

For more information why we need to check the exception type is a
COMException or not:

#Handling COM Interop Exceptions (.NET Framework Developer's Guide)
http://msdn.microsoft.com/library/en-us/cpguide/html/cpconhandlingcominterop
exceptions.asp?frame=true


Hope this helps.

Sincerely,
Walter Wang (wawang(a)online.microsoft.com, remove 'online.')
Microsoft Online Community Support

==================================================
Get notification to my posts through email? Please refer to
http://msdn.microsoft.com/subscriptions/managednewsgroups/default.aspx#notif
ications. If you are using Outlook Express, please make sure you clear the
check box "Tools/Options/Read: Get 300 headers at a time" to see your reply
promptly.

Note: The MSDN Managed Newsgroup support offering is for non-urgent issues
where an initial response from the community or a Microsoft Support
Engineer within 1 business day is acceptable. Please note that each follow
up response may take approximately 2 business days as the support
professional working with you may need further investigation to reach the
most efficient resolution. The offering is not appropriate for situations
that require urgent, real-time or phone-based interactions or complex
project analysis and dump analysis issues. Issues of this nature are best
handled working with a dedicated Microsoft Support Engineer by contacting
Microsoft Customer Support Services (CSS) at
http://msdn.microsoft.com/subscriptions/support/default.aspx.
==================================================

This posting is provided "AS IS" with no warranties, and confers no rights.


From: Don on
Hi Walter,

Still having a problem... My test case calls MailItem.SaveAs using a
filename having illegal characters. The error code returned in VB6 is
x800300FC with a description of "Internal Application Error". I did find
that passing that code to FormatMessage returns "The name %1 is not valid"
which is much more useful (requires the use of the
FORMAT_MESSAGE_IGNORE_INSERTS flag), so I'll do that in VB.NET. But the
HRESULT returned in COMException.ErrorCode is different every time I run the
application. I've gotten xA72300FC, xA0F300FC, xA30300FC, xA51300FC, etc.
While the "300FC" portion of each HRESULT matches the "300FC" portion of the
VB6 Error Code, the rest does not, so the lookup in FormatMessage fails. One
solution apears to be to do a bit-wise AND of the HRESULT using x800FFFFF,
which results in the proper code of x800300FC for each different HRESULT
returned by the COMException. Is that a good solution, or is there another
method that I should use to derive the proper Error Code for use with
FormatMessage? Also, if using AND is correct, then is x800FFFFF okay, or to
preserve both severity bits should it be xC00FFFFF?

Thanks for your help,
Don


"Walter Wang [MSFT]" <wawang(a)online.microsoft.com> wrote...
>
> Exception thrown from COM should be available in .NET through the
> IErrorInfo interface automatically. For example, if you create following
> COM component in VB6 which throws a exception:
>
> Public Sub Test()
> Err.Raise 12345, "Project1.Class1.Test()", "Custom exception from VB6"
> End Sub
>
> When you call it from VB.NET:
>
> Try
> Dim x As New Project1.Class1
> x.Test()
> Catch e As Exception
> If TypeOf e Is System.Runtime.InteropServices.COMException
Then
> Dim ce As System.Runtime.InteropServices.COMException =
> CType(e, System.Runtime.InteropServices.COMException)
> Stop
> End If
> End Try
>
> You will see:
>
> e.Message = "Custom exception from VB6"
> e.Source = "Project1.Class1.Test()"
> ce.ErrorCode = &H800A3039
>
> The error code is composed of several parts, the last part is 12345
> (&H3039):
>
> Hope this helps.
>
> Sincerely,
> Walter Wang (wawang(a)online.microsoft.com, remove 'online.')
> Microsoft Online Community Support
>


From: Walter Wang [MSFT] on
Hi Don,

A HRESULT is made up of following fields:

* A 1-bit code indicating severity, where zero represents success and 1
represents failure.
* A 4-bit reserved value.
* An 11-bit code indicating responsibility for the error or warning, also
known as a facility code.
* A 16-bit code describing the error or warning.

#HRESULT
http://msdn2.microsoft.com/en-us/library/ms526450.aspx


In case of COMException, you could use Marshal.GetExceptionForHR to convert
the HRESULT to an exception again. It will return correct message for it:


Imports Microsoft.Office.Interop.Outlook
Imports System.Runtime.InteropServices

Module Module1
Sub Main()
Dim app As New Application()
Dim inbox As MAPIFolder =
app.GetNamespace("MAPI").GetDefaultFolder(OlDefaultFolders.olFolderInbox)
For Each item As MailItem In inbox.Items
Try
item.SaveAs("?.msg")
Catch e As System.Exception
If TypeOf e Is COMException Then
Dim ce As COMException = CType(e, COMException)
Dim e2 As System.Exception =
Marshal.GetExceptionForHR(ce.ErrorCode)
Console.Write(e2.Message)
Else
Console.Write(e.Message)
End If
End Try
Exit For
Next
End Sub

End Module


Hope this helps.

Regards,
Walter Wang (wawang(a)online.microsoft.com, remove 'online.')
Microsoft Online Community Support

==================================================
When responding to posts, please "Reply to Group" via your newsreader so
that others may learn and benefit from your issue.
==================================================

This posting is provided "AS IS" with no warranties, and confers no rights.

From: Don on
Hi Walter,

I'm still having a problem getting the correct message. Using my
MailItem.SaveAs test with illegal characters in the filename,
Marshal.GetExceptionForHR(ce.ErrorCode) returned an Exception.Message as
follows:

"Exception from HRESULT: 0x8E6300FC"

Ran the test again and got:

"Exception from HRESULT: 0x928300FC"

The HRESULT is different each time, and none of them appear to map to a
description. Since the VB6 Error Code for this test is &H800300FC, don't I
still need to convert ce.ErrorCode to a true COM Code when using
GetExceptionForHR? For example, I tried
Marshal.GetExceptionForHR(ce.ErrorCode And &H800FFFFF) instead, and the
resulting Exception.Message was:

"The name is not valid. (Exception from HRESULT: 0x800300FC
(STG_E_INVALIDNAME))"

which is basically what I'm looking for. So, is using "ce.ErrorCode And
&H800FFFFF" the proper solution? And is &H800FFFFF the appropriate value for
the filter?

Thanks,
Don


"Walter Wang [MSFT]" <wawang(a)online.microsoft.com> wrote in message
news:evemlUuOHHA.2304(a)TK2MSFTNGHUB02.phx.gbl...
> In case of COMException, you could use Marshal.GetExceptionForHR to
convert
> the HRESULT to an exception again. It will return correct message for it:
>
>
> Dim e2 As System.Exception =
> Marshal.GetExceptionForHR(ce.ErrorCode)
> Console.Write(e2.Message)