Unhandled exceptions that occur in Event<T>.Invoke() in Win64 builds cause app crash

Issue #292 resolved
Joachim Marder created an issue

If I put this line of code

raise EInvalidOperation.Create('test');

in an anonymous procedure passed to Event<T>.OnChanged.Add() the exception causes an app crash. The default RTL expection handler does not get active, neither does MadExcept.

This occurs only for Win64 builds. It does not occur if I build the unit Spring.Events using PUREPASCAL defined.

Comments (18)

  1. Stefan Glienke repo owner

    Please provide more information as currently I am unable to reproduce. Following code just passes fine:

    program EventCrashTest;
    
    uses
      Classes,
      Spring,
      TestFramework,
      TestInsight.DUnit;
    
    type
      {$M+}
      TMyProc = reference to procedure;
      {$M-}
    
      TEventCrashTest = class(TTestCase)
      published
        procedure Test;
      end;
    
    procedure TEventCrashTest.Test;
    var
      e: Event<TMyProc>;
    begin
      e.Add(
        procedure
        begin
          raise EInvalidOperation.Create('test');
        end);
      ExpectedException := EInvalidOperation;
      e.Invoke();
    end;
    
    begin
      RegisterTest(TEventCrashTest.Suite);
      RunRegisteredTests;
    end.
    
  2. Stefan Glienke repo owner

    I just noticed you wrote Event<T>.OnChanged.Add() but there is no such method. The OnChanged event of an Event<T> is a classic TNotifyEvent. As such you cannot add an anonymous method there so I assumed you meant Event<T>.Add()

  3. Joachim Marder reporter

    Yes, sorry, I meant Event<T>.Add().

    I will extract a sample project, I didn't think we do something special.

  4. Joachim Marder reporter

    This is the smallest project I could assemble so far.

    If you compile it for Win64 you will notice the App Crash. If you compile it for Win32 you will see the usual Delphi message box.

  5. Joachim Marder reporter

    Here's a version that is a little more compact:

    unit Unit5;
    
    interface
    
    uses
      Classes, Vcl.Forms, Spring;
    
    type
      TForm5 = class(TForm)
        procedure FormShow(Sender: TObject);
      private
        e: Event<TAction<Integer>>;
      end;
    
    var
      Form5: TForm5;
    
    implementation
    
    {$R *.dfm}
    
    procedure TForm5.FormShow(Sender: TObject);
    begin
      e.Add(
        procedure(const I: Integer)
        begin
          raise EInvalidOperation.Create('test');
        end);
      e.Invoke(1);
    end;
    
    end.
    
  6. Cesar Romero

    I ran your example here, and that is what I found:

    • it works fine on my Windows 7 64-bit Virtual Box VM, where I have Delphi installed,
    • it just crashes on my host computer that is a Windows 10 Home 64-bit, I added madExcept to the project, but it still crashes.

    Building the project with "{$DEFINE PUREPASCAL}" works on both Windows 7 and Windows 10.

  7. David Heffernan

    It might be pertinent to know which version of Delphi is being used. Perhaps the issue is related to a compiler defect.

  8. Stefan Glienke repo owner

    Looks like Windows 10 is the commonality - can you try the code I posted if it crashes?

  9. Stefan Glienke repo owner

    Looks like this can be reproduced with the RTL alone (either a compiler defect or the asm stub that is used there - which is similar to the one in Spring4D is not correct).

    Can you also reproduce the issue with this code?

    program Windows10Crash;
    
    {$APPTYPE CONSOLE}
    
    uses
      ObjAuto, SysUtils, TypInfo;
    
    type
      TMyEvent = procedure of object;
    
      TTest = class
        procedure Invoke(Params: PParameters; StackSize: Integer);
        procedure Run;
      end;
    
    procedure TTest.Invoke(Params: PParameters; StackSize: Integer);
    begin
      raise Exception.Create('test');
    end;
    
    procedure TTest.Run;
    var
      method: TMyEvent;
    begin
      TMethod(method) := CreateMethodPointer(Invoke, GetTypeData(TypeInfo(TMyEvent)));
      method;
    end;
    
    var
      t: TTest;
    begin
      try
        t := TTest.Create;
        t.Run;
      except
        on E: Exception do
          Writeln(E.ClassName, ': ', E.Message);
      end;
    end.
    
  10. Cesar Romero

    Stefan, with your Windows10Crash it is not reproducible on my pc or vm. It just prints the Exception as expect. I have it tested on Windows 7 and Windows 10 both 64-bit.

  11. Stefan Glienke repo owner

    Please verify the change just commited to hotfix/1.2.2 solves the issue. It looks like the asm stub was not ABI compliant as it modified the stack pointer within the method body. I also got rid of the nasty push/return hack on x64 as it is not needed there.

  12. Log in to comment