Неправильное исключение после вызова .net4.0 com сервера из приложения delphi - PullRequest
4 голосов
/ 23 февраля 2011

Мы переносим нашу кодовую базу из BDS2006 в Rad Studio XE, и мы обнаружили очень странное поведение: если мы делаем недопустимую операцию с плавающей запятой (т.е. деление на ноль) после создания какого-либо объекта из COM-сервера, реализованного в .Net4.0 , мы не получаем нормальное исключение (т.е. EDivisionByZero), но EStackOverflow.

Нам удалось подготовить очень простой пример: ComErrorExample

Существует сборка .net 4.0 с com-интерфейсом (одна функция возвращает строку) и простым приложением delphi:

var
  a, b: Double;
  Stored8087CW: Word;

begin
  CoInitialize(nil);

  try
    b := 0;
    a := 1 / b;
  except
    on E: Exception do
      Writeln(E.ClassName, ': ', E.Message, ' (Expected type of exception)');
  end;

  Stored8087CW := Get8087CW;
  Writeln('Code from .NET COM: ', CoExampleOfCOM.Create.DoSomething);
  Set8087CW(Stored8087CW); //it's only to show that 8087 control word doesn't change

  try
    b := 0;
    a := 1 / b;
  except
    on E: Exception do
      Writeln(E.ClassName, ': ', E.Message, ' (Unexpected type of exception! Why stack overflow?)');
  end;

  Readln;

  CoUninitialize;

end.

Как видите, мы делаем два деления на ноль - первое, перед созданием com-объекта, выбрасывает EDivisionByZero, второе - EStackOverflow.

Мы протестировали это на win7 x64 и winXP x32 - без разницы. Но когда мы переключили com сервер с .net4.0 на .net3.5 - все работает нормально.

Вопрос: мы делаем что-то не так? Можем ли мы что-то сделать, чтобы решить эту проблему?

Переключение на .net3.5 или сброс Delphi для нас не вариант.

UPDATE: Мы проверяли конфигурацию с плавающей запятой (Set8087CW ()) раньше, но безуспешно. ОБНОВЛЕНИЕ2: Я расширил пример с восстановлением конфигурации с плавающей запятой.

Ответы [ 2 ]

2 голосов
/ 23 февраля 2011

Похоже, что-то в COM DLL меняет конфигурацию процессора с плавающей запятой. См. Default8087CW и Set8087CW в справке Delphi.

Вы можете сохранить его перед выполнением каких-либо действий с COM DLL и впоследствии восстановить.

var
  Saved8087CW: Word;
begin
  Saved8087CW := Default8087CW;
  // If you want, disable all fpu exceptions 
  // with the next line.
  Set8087CW($133F);
  DoYourComOperationHere;
  Set8087CW(Saved8087CW);
end;
1 голос
/ 19 сентября 2011

Я не могу комментировать первоначальный вопрос, поэтому извините за этот ответ, который является скорее набором вопросов. Вы когда-нибудь находили приемлемое решение этой проблемы? Вы регистрировали проблему Delphi в Quality Central, и если да, то есть ли у вас номер инцидента?

Я вижу, что я думаю, что это связанная проблема, где на стороне Delphi я устанавливаю FPU CW на $ 133f, а затем на стороне C # у меня было

    try
    {
        Double.IsNaN(Double.NaN);
    }
    catch (ArithmeticException)
    {
        /* Intentionally empty. .NET will initialise the FPU at this point. */
    }

Раньше все работало нормально в 2.0 Framework, но сейчас я получаю огромное количество 0xC0000092: проверка стека с плавающей запятой. а затем исключения StackOverflow.

Добавление вышеуказанного кода в открытые COM-методы вызывает стек вызовов

clr.dll!CLRVectoredExceptionHandlerShim()  + 0xa3 bytes 
ntdll.dll!_RtlpCallVectoredHandlers@12()  - 0xef1c bytes    
ntdll.dll!_RtlCallVectoredExceptionHandlers@8()  + 0x12 bytes   
ntdll.dll!_RtlDispatchException@8()  + 0x19 bytes   
ntdll.dll!_KiUserExceptionDispatcher@8()  + 0xf bytes   
clr.dll!CLRVectoredExceptionHandler()  + 0x9a bytes 
clr.dll!CLRVectoredExceptionHandlerShim()  + 0xa3 bytes 
...