Recuperar o número de erro original de um método COM chamado por meio de reflexo
Pergunta
Eu tenho um componente VB6 COM que eu preciso para chamar de meu método .Net. I usar a reflexão para criar uma instância do objeto COM e ativá-lo da seguinte maneira:
f_oType = Type.GetTypeFromProgID(MyProgId);
f_oInstance = Activator.CreateInstance(f_oType);
Eu preciso usar GetTypeFromProgID ao invés de usar tlbimp para criar uma biblioteca contra o COM DLL como o ProgId do tipo I precisa instanciar pode variar. Eu, então, usar Type.InvokeMember para chamar o método COM no meu código como:
f_oType.InvokeMember("Process", BindingFlags.InvokeMethod, null, f_oInstance, new object[] { param1, param2, param3, param4 });
I pegar qualquer levantada TargetInvocationException de para registro e pode obter a descrição detalhada do erro do campo TargetInvocationException.InnerException. No entanto, eu sei que o componente COM utiliza Error.Raise para gerar um número de erro e eu preciso de alguma forma obter espera deste na minha aplicação .Net chamando.
O problema parece resultar do TargetInvocationException não contendo o número do erro como eu esperaria se fosse um COMException normal para:
Como posso obter o número de erro do objeto COM no meu código .net?
ou
Posso fazer essa mesma chamada de uma forma que poderia causar um COMException (contendo o número do erro) em vez de um TargetInvocationException quando o componente COM falhar?
Observe também que a plataforma de destino é .Net 2.0 e eu tenho acesso ao código fonte VB6, mas consideraria alterar a mensagem de erro elevada de VB6 para conter o código de erro como parte do texto a ser um pouco de um hack.
Solução
Eu olhei para o seu código um pouco mais e com reflexão você lida com TargetInvocationException e trabalhar com a exceção interna que é um COMException ... exemplo de código abaixo (eu corri e testou este também):
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); }
}
Outras dicas
Você vai lidar com COMException e use a propriedade ErrorCode desse objeto de exceção. Normalmente, em DLLs do Visual Basic, se você está jogando erros personalizados que elevaria o erro com: Err.Raise vbObjectError + 88, "Project1.Class1.Test3 ()", "forçado teste de erro"
Se este for o caso, você precisará subtrair vbObjectError (-2147221504) a partir das exceções ErrorCode para obter o número de erro real. Se não usar apenas o valor ErrorCode.
código Exemplo VB dll: (a partir de Project1.Class1)
Public Sub Test3 ()
MsgBox "this is a test 3"
Err.Raise vbObjectError + 88, "Project1.Class1.Test3()", "Forced error test"
End Sub
Exemplo C código de tratamento de consumo #:
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); }
}
O código de erro neste teste I retornos completado 88 como esperado.
Apenas quero oferecer uma atualização de @ sharvell código de captura. A menos que você esteja absolutamente certo InnerException é um COMException, é melhor para testá-lo com segurança em primeiro lugar. Caso contrário, você vai ter uma exceção no seu manipulador de exceção. Gritos!
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
}