Recuperar el número de error original de un método COM llamado a través de la reflexión

StackOverflow https://stackoverflow.com/questions/153438

  •  03-07-2019
  •  | 
  •  

Pregunta

Tengo un componente VB6 COM al que necesito llamar desde mi método .Net. Utilizo la reflexión para crear una instancia del objeto COM y activarlo de la siguiente manera:

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

Necesito usar GetTypeFromProgID en lugar de usar tlbimp para crear una biblioteca contra la DLL COM, ya que el ProgId del tipo que necesito crear una instancia puede variar. Luego uso Type.InvokeMember para llamar al método COM en mi código como:

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

Capto cualquier excepción TargetInvocationException para el registro y puedo obtener la descripción detallada del error desde el campo TargetInvocationException.InnerException. Sin embargo, sé que el componente COM usa Error.Raise para generar un número de error y de alguna manera necesito obtener esto en mi aplicación .Net de llamadas.

El problema parece deberse a que la excepción TargetInvocationException no contiene el número de error como cabría esperar si fuera una excepción COMException normal:

¿Cómo puedo obtener el Número de error del objeto COM en mi código .Net?

o

¿Puedo hacer esta misma llamada de manera que cause una excepción COM (que contiene el número de error) en lugar de una excepción de excepción de identificación de destino cuando falla el componente COM?

Tenga en cuenta también que la plataforma de destino es .Net 2.0 y tengo acceso al código fuente de VB6, pero consideraría que modificar el mensaje de error que surgió de VB6 para contener el código de error como parte del texto es un poco hackear.

¿Fue útil?

Solución

Observé tu código un poco más de cerca y, con la reflexión, manejas la excepción TargetInvocationException y trabajas con la excepción interna que es una excepción COM ... El ejemplo de código a continuación (también lo ejecuté y lo probé):

    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); }
    }

Otros consejos

Manejará COMException y usará la propiedad ErrorCode de ese objeto de excepción. Normalmente en las DLL básicas visuales, si está lanzando errores personalizados, generaría el error con: Err.Raise vbObjectError + 88, " Project1.Class1.Test3 () " ;, " Prueba de error forzado "

Si este es el caso, deberá restar vbobjecterror (-2147221504) del Código de error de excepciones para obtener el número de error real. Si no solo usa el valor ErrorCode.

Código dll VB de ejemplo: (de Project1.Class1)

Public Sub Test3 ()

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

End Sub

Ejemplo de código de manejo de consumo de C #:

    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); }
    }

El código de error en esta prueba que acabo de completar devuelve 88 como se esperaba.

Solo quiero ofrecer una actualización al código de captura de @ sharvell. A menos que esté absolutamente seguro de que InnerException es una excepción COM, es mejor probarla primero con seguridad. De lo contrario, tendrá una excepción en su controlador de excepciones. 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
}
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top