tags:

views:

779

answers:

3

I have a VB6 COM component which I need to call from my .Net method. I use reflection to create an instance of the COM object and activate it in the following manner:

f_oType = Type.GetTypeFromProgID(MyProgId);
f_oInstance = Activator.CreateInstance(f_oType);

I need to use GetTypeFromProgID rather than using tlbimp to create a library against the COM DLL as the ProgId of the type I need to instantiate can vary. I then use Type.InvokeMember to call the COM method in my code like:

f_oType.InvokeMember("Process", BindingFlags.InvokeMethod, null, f_oInstance, new object[] { param1, param2, param3, param4 });

I catch any raised TargetInvocationException's for logging and can get the detailed error description from the TargetInvocationException.InnerException field. However, I know that the COM component uses Error.Raise to generate an error number and I need to somehow get hold of this in my calling .Net application.

The problem seems to stem from the TargetInvocationException not containing the error number as I'd expect if it were a normal COMException so:

How can I get the Error Number from the COM object in my .Net code?

or

Can I make this same call in a way that would cause a COMException (containing the error number) rather than a TargetInvocationException when the COM component fails?

Please also note that the target platform is .Net 2.0 and I do have access to the VB6 source code but would regard altering the error message raised from VB6 to contain the error code as part of the text to be a bit of a hack.

+1  A: 

You will handle COMException and use the ErrorCode property of that exception object. Normally in visual basic DLLs if you are throwing custom errors you would raise the error with: Err.Raise vbObjectError + 88, "Project1.Class1.Test3()", "Forced error test"

If this is the case you will need to subtract vbobjecterror (-2147221504) from the exceptions ErrorCode to get the actual error number. If not just use the ErrorCode value.

Example VB dll code: (from Project1.Class1)

Public Sub Test3()

MsgBox "this is a test 3"
Err.Raise vbObjectError + 88, "Project1.Class1.Test3()", "Forced error test"

End Sub

Example C# consumption handling code:

    private void button1_Click(object sender, EventArgs e)
    {
        try
        {
            var p = new Class1();
            p.Test3();
        }
        catch (COMException ex)
        {
            int errorNumber = (ex.ErrorCode - (-2147221504));
            MessageBox.Show(errorNumber.ToString() + ": " + ex.Message);
        }
        catch(Exception ex)
        { MessageBox.Show(ex.Message); }
    }

The ErrorCode in this test I just completed returns 88 as expected.

sharvell
As stated in the original question, the code wont throw a COMException, it throws a TargetInvocationException which doesnt have an error number
Wolfwyrd
+2  A: 

I looked at your code a little closer and with reflection you handle TargetInvocationException and work with the inner exception that is a COMException... Code example below (I ran and tested this as well):

    private void button1_Click(object sender, EventArgs e)
    {
        try
        {
            var f_oType = Type.GetTypeFromProgID("Project1.Class1");
            var f_oInstance = Activator.CreateInstance(f_oType);
            f_oType.InvokeMember("Test3", BindingFlags.InvokeMethod, null, f_oInstance, new object[] {});
        }
        catch(TargetInvocationException ex)
        {
            //no need to subtract -2147221504 if non custom error etc
            int errorNumber = ((COMException)ex.InnerException).ErrorCode - (-2147221504);
            MessageBox.Show(errorNumber.ToString() + ": " + ex.InnerException.Message);
        }
        catch(Exception ex)
        { MessageBox.Show(ex.Message); }
    }
sharvell
This got it. Was thrown at first as the InnerException was not coming back as a COMException however investigation showed the COM Component was raising the error incorrectly. Fixed that and this works a treat
Wolfwyrd
+2  A: 

Just want to offer an update to @sharvell's catch code. Unless you're absolutely sure InnerException is a COMException, it's better to safely test it first. Otherwise you'll have an exception in your exception handler. Whoops!

catch(TargetInvocationException ex)
{
    if( ex.InnerException != null && ex.InnerException is COMException )
    {
        COMException ce = (COMException)ex.InnerException;
        // do something with ce - e.g. logging the error
    }
    // else InnerException not set, or it's not a COMException
}
Robert Paulson