Получение исходного номера ошибки из COM-метода, вызываемого через отражение
Вопрос
У меня есть COM-компонент VB6, который мне нужно вызвать из моего метода .Net.Я использую отражение для создания экземпляра COM-объекта и активации его следующим образом:
f_oType = Type.GetTypeFromProgID(MyProgId);
f_oInstance = Activator.CreateInstance(f_oType);
Мне нужно использовать GetTypeFromProgID вместо использования tlbimp для создания библиотеки в COM DLL, поскольку ProgID типа, который мне нужно создать, может отличаться.Затем я использую Type.InvokeMember для вызова метода COM в моем коде, например:
f_oType.InvokeMember("Process", BindingFlags.InvokeMethod, null, f_oInstance, new object[] { param1, param2, param3, param4 });
Я перехватываю любое вызванное TargetInvocationException для регистрации и могу получить подробное описание ошибки из TargetInvocationException.Поле InnerException.Однако я знаю, что COM-компонент использует Error.Поднимите, чтобы сгенерировать номер ошибки, и мне нужно каким-то образом получить доступ к этому в моем вызове.Сетевое приложение.
Проблема, похоже, связана с TargetInvocationException, не содержащим номер ошибки, как я бы ожидал, если бы это было обычное COMException, поэтому:
Как я могу получить номер ошибки из COM-объекта в моем .Net-коде?
или
Могу ли я выполнить этот же вызов таким образом, чтобы вызвать COMException (содержащий номер ошибки), а не TargetInvocationException при сбое COM-компонента?
Пожалуйста, также обратите внимание, что целевой платформой является .Net 2.0, и у меня есть доступ к исходному коду VB6, но я бы счел изменение сообщения об ошибке, полученного из VB6, чтобы оно содержало код ошибки как часть текста, своего рода взломом.
Решение
Я присмотрелся к вашему коду немного внимательнее, и с помощью reflection вы обрабатываете TargetInvocationException и работаете с внутренним исключением, которое является COMException...Пример кода ниже (я также запустил и протестировал это):
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); }
}
Другие советы
Вы будете обрабатывать COMException и использовать свойство ErrorCode этого объекта exception.Обычно в библиотеках DLL Visual Basic, если вы выдаете пользовательские ошибки, вы вызываете ошибку с помощью:Ошибка.Вызовите vbObjectError + 88, "Project1.Class1.Test3()", "Принудительный тест на ошибку"
Если это так, вам нужно будет вычесть vbobjecterror (-2147221504) из кода ошибки исключений, чтобы получить фактический номер ошибки.Если нет, просто используйте значение ErrorCode.
Пример кода библиотеки DLL VB:(из проекта 1.Class1)
Публичный Вспомогательный Тест3()
MsgBox "this is a test 3"
Err.Raise vbObjectError + 88, "Project1.Class1.Test3()", "Forced error test"
Конечная подстанция
Пример кода обработки потребления на 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); }
}
Код ошибки в этом тесте, который я только что завершил, возвращает 88, как и ожидалось.
Просто хочу предложить обновление кода catch от @sharvell.Если вы не абсолютно уверены, что InnerException является COMException, лучше сначала безопасно протестировать его.В противном случае у вас будет исключение в вашем обработчике исключений.Упс!
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
}