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

    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.
    
  4. 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.

  5. 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.
    
  6. 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.

  7. 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.

  8. Log in to comment