View Issue Details

IDProjectCategoryView StatusLast Update
0012974FPCCompilerpublic2018-07-07 12:45
ReporterCarlo KokAssigned To 
PrioritynormalSeveritymajorReproducibilityalways
Status newResolutionopen 
PlatformOSWindowsOS Version5
Product Version2.2.2Product Build2.2.2 final 
Target VersionFixed in Version 
Summary0012974: FPC can't catch windows exceptions (av's) in a try/except in a dll call
DescriptionFPC doesn't set up fs:0 for a try/except but uses it's own stack instead, this causes av's to be passed to the host app, and completely ignore any try's in the dll functions (including dynamic type freeing).

Steps To ReproduceCompile the code below and run. Notice that the dll try/finally is ignored.

{$MODE DELPHI}
program MYStartTest;

type mydelegate = procedure;
function GetDelegate: MyDElegate; stdcall; external 'test.dll';

var
  del: MyDelegate;
begin
  Del := getdelegate;
  del();
end.
-------------------------------------------------------------
{$MODE DELPHI}
Library test;
uses
  Sysutils;

type
  MyDelegate = procedure;

procedure CauseAV;
var
  r: ^Integer;
begin
  R := nil;
  IntToStr(r^);
end;

procedure CatchAV;
begin
  try
    CauseAV;
  except
  end;
end;

function GetDelegate: MyDElegate; stdcall;
begin
  result := CatchAV;
end;

exports
  GetDelegate;

begin
end.
Additional Information10:36 <oliebol> Does something need to be done for exceptions to work over module bounderies?
10:36 <oliebol> Like initialize SEH per module or so?
10:37 <Thorsten[NX]> ki9a, that's ok, as long as the capabilities are the same as under D2007 and older it'll be ok
10:38 <ki9a> oliebol: The Host fpc app can catch the av that the dll can't.
10:39 <Thorsten[NX]> under windows SEH doesn't need to be specially initialized, the current handler is identified by a pointer in the thread specific data (fs selector)
10:39 <ki9a> Thorsten[NX]: it works yeah. The only thing that I don't support atm is fpc on win64 and linux64.
10:39 <Thorsten[NX]> a try/finally or try/except block should be installing it's handler by saving the original value somewhere and putting a pointer to itself into that
10:40 <oliebol> ki9a: so FreeBSD64 is supported? Brave!
10:40 <ki9a> funny
10:40 <ki9a> Thorsten[NX]: Finally isn't called either.
10:40 <neli> hehe
10:40 <neli> almost nothing is aware of dll boundaries afaik
10:41 <oliebol> So it could be that it works but the attempt to unwind the stack fails?
10:41 <ki9a> it's like it doesn't think there is an exception at all.
10:41 <Thorsten[NX]> to get the initial handler under SEH in windows shouldn't involve the stack at all
10:42 <ki9a> if I raise an Exception
10:42 <ki9a> it does catch it in the dll
10:42 <Thorsten[NX]> take a look at the asm code created for your "try" statement in the DLL, it should be emitting code putting a value into [fs:???]
10:42 <ki9a> a plain raise Exception.Create();
10:44 <ki9a> Thorsten[NX]: it calls FPC_PUSHEXCEPTADDR and FPC_SETJMP
10:51 <Thorsten[NX]> I don't have a reasonable current version of the FPC source on this computer currently
10:51 <ki9a> Thorsten[NX]: problem is that raise exception('') does get caught
10:52 <ki9a> so its only fails to do its work for AVs
10:52 <Thorsten[NX]> what should happen on a try is that a pointer to a 16 byte structure is placed into fs:0 on the try
10:53 <ki9a> divide by zero doesn't get caught either.
10:54 <Thorsten[NX]> that 16 byte structure has as first value the old value from fs:0, as 2nd value the ebp, as 3rd a pointer to code which should be executed in case of an exception and as 4th value a
                     paramter which will be put into eax when calling the 3rd pointer
10:55 <Thorsten[NX]> the result is that you get a linked sequence of these handler descriptors by starting with fs:0 and following from there
10:55 <ki9a> Thorsten[NX]: since it can properly catch an exception class, that seems to be oke.
10:55 <Thorsten[NX]> ok.. then the issues is probably somewhere in the handler code that is being called
10:56 <Thorsten[NX]> meaning whatever code is behind that 3rd value in the exception frame descriptor
10:56 <fpcfan-work> looking at the RTL source, I would say that FS is only filled at startup
10:57 <Thorsten[NX]> I'm not sure about FPC right now, but in delphi that's usually just a jump to _HandleAnyException, _HandleFinally or _HandleOnException (depending on context)
10:58 <Thorsten[NX]> hm, fs:0 would need to be written to on every try (to install the new exception frame) and every finally/except (to remove it)
10:58 <fpcfan-work> why?
10:58 <fpcfan-work> a new exception frame can be installed without filling fs:0?
10:59 <Thorsten[NX]> fs:0 always contains the pointer to the most current active exception frame, this is where the OS exception handling code will look for the active handler and call it
10:59 <fpcfan-work> for example on hte heap?
10:59 <Thorsten[NX]> fs:0 contains the pointer to the 16 byte exception frame descriptor
10:59 <fpcfan-work> and put generic code in fs:0, which calls teh current exception frame pointer
10:59 <Thorsten[NX]> which in turn as first entry contains the pointer to the next outer excpetion frame descriptor
11:00 <Thorsten[NX]> fs:0 does not contain code
11:00 <Thorsten[NX]> it contains a pointer to an exception frame descriptor
11:00 <ki9a> fpcfan-work: that would mean a dll written in fpc can never catch exceptions if the host isn't written in fpc ?
11:01 <fpcfan-work> maybe
11:02 <fpcfan-work> I am just looking at the windows RTL and drawing some conclusions
11:02 <fpcfan-work> I see this code for the first time
11:02 <fpcfan-work> and it doesn't seem to map on the description from Thorsten[NX]
11:02 <Thorsten[NX]> http://www.rohitab.com/sourcecode/seh.html this shows relatively well how SEH is supposed to work under windows
11:04 <Thorsten[NX]> as you can see, on "try" it installes a new pointer into fs:0, and the first pointer in that memory needs to be the old value of fs:0
11:04 <Thorsten[NX]> this is what chaines all the exception handlers togehter
11:04 <Thorsten[NX]> uninstalling the exception handler on finally/except is done by restoring the original value of fs:0
11:06 <ki9a> reading this, fpc indeed seems to install it only once and do it's own tree.
11:06 <Thorsten[NX]> that is wrong for windows which has exception handling integrated into the OS expecting a very specific structure
11:07 <Thorsten[NX]> and that would naturally explain why the DLL can't catch any exceptions, it doesn't have an handler installed, instead the OS looks at fs:0 and will find the last handler installed in
                     the current codepath which is probably somewhere in the host
11:09 -!- Chain|iB [n=charlie@catv-80-99-83-145.catv.broadband.hu] has quit ["[.life.support.system.failure.]"]
11:10 <ki9a> i suppose the same issue exists on linux
11:10 <ki9a> and other platforms, and it only installs a signal handler once?
11:11 <Thorsten[NX]> SEH under linux is a more complex proposition IIRC because it's not part of the OS as such but handled at the RTL level
11:11 <ki9a> right
11:11 <ki9a> but the linux signal handlers are used for av's and other errors aren't they?
11:11 <Thorsten[NX]> (that might not be true anymore, it's been a couple of years that I last looked at this)
11:11 <Thorsten[NX]> AFAIK, yes
11:13 <ki9a> if those are installed only once, like the "signals.pp" unit on windows does
11:13 <ki9a> the same problem would exist there.
11:14 <Thorsten[NX]> you could get away with using your own stack for exception frames as long as as the OS level exception handler is installed on every entry point that can be called from another module
11:15 <ki9a> technically that can be at any outer try
11:15 <Thorsten[NX]> but even then, under windows tools like e.g. WinDbg depend on this linked list of exception handler descriptors starting at fs:0
11:16 <Thorsten[NX]> ki9a, if you acknowledge the possibility that your code might hand out pointers directly to specific methods, then yes, it can be any try, which means that the best solution is to use
                     SEH the way it's intended under windows
11:19 <ki9a> the code that originally showed this requires delphi prism to be installed, vs.net installed and is loaded by the vs.net debugger during debugging.
11:19 <ki9a> i thought this was easier.
11:26 <Thorsten[NX]> http://www.microsoft.com/msj/0197/Exception/Exception.aspx
11:26 <Thorsten[NX]> that seems to explain it pretty well, at least in regards to Win32
11:26 <Thorsten[NX]> I think under Win64 it works a bit different
11:26 <ki9a> hm. I know this same code fails in win64 equally bad
11:27 <Thorsten[NX]> it would
11:28 <Thorsten[NX]> as far as I know under win64 the fs:0 is not used, instead it's necessary to walk the stack to find the exception handlers, the structure of the stack under win64 is much stricter
                     regulated under win64 then win32 which makes this possible
11:30 <Thorsten[NX]> only mention I found about exception handling under win64 is this:
11:30 <Thorsten[NX]> "On amd64, the stack needs to be examined. There are no exception registration records; the return addresses are walked to find the call stack and the exception handling context for
                     each call frame.
11:30 <Thorsten[NX]> So if you're a compiler, you must not generate code like this:
11:30 <Thorsten[NX]> 0000000100001C6E: FF 15 A4 F4 FF FF call qword ptr [__imp_DoSomething]
11:30 <Thorsten[NX]> 0000000100001C74: 66 C7 03 00 00 mov word ptr [rbx],0
11:30 <Thorsten[NX]> ... because then the return address of the call would be the mov instruction, and exceptions raised in DoSomething() would be in the same context as exceptions raised by the mov
                     instruction.
11:30 <Thorsten[NX]> So the compiler adds a nop:
11:30 <Thorsten[NX]> 0000000100001C6E: FF 15 A4 F4 FF FF call qword ptr [__imp_DoSomething]
11:30 <Thorsten[NX]> 0000000100001C74: 90 nop
11:30 <Thorsten[NX]> 0000000100001C75: 66 C7 03 00 00 mov word ptr [rbx],0
11:30 <Thorsten[NX]> That nop's sole purpose in life (so far as I can determine, anyway) is to provide a return address from the call."
11:31 <Thorsten[NX]> I haven't done anything with win64 myself yet, so I'm not sure about the in's and out's of exception handling there
11:32 <Thorsten[NX]> but for win32 I'm 100% sure you need to properly install/uninstall EXCEPTION_REGISTRATION records into fs:0
11:37 <ki9a> k
11:37 <ki9a> i suppose I should creat ean issue for this then.
11:41 <ki9a> this also means strings are never freed
11:42 <fpcfan-work> never freed, when an exception occurred, you mean?
11:42 <ki9a> yes
11:42 <ki9a> as the finally isn't ever called
11:42 <ki9a> presuming fpc does a try/finally for that too
11:43 <fpcfan-work> right
Tagsdynamic library, Exception, SEH
Fixed in Revision
FPCOldBugId
FPCTarget
Attached Files
  • tbsystemexception.pas (538 bytes)
    {$APPTYPE CONSOLE}
    program tbsystemexception;
    
    uses sysutils
        , windows
        ;
    
    var
        fileh : thandle;
    begin
        try
            fileh := FileOpen('tbsystemexception.pas', fmOpenRead);
            FileClose(fileh);
            FileClose(fileh);
            writeln('fileclosed normaly');
    
            fileh := FileOpen('tbsystemexception.pas', fmOpenRead);
            CloseHandle(fileh);
            CloseHandle(fileh);
            writeln('handleclosed normaly');
        except
            ShowException(ExceptObject, ExceptAddr);
        end;
    end.
    
    
    tbsystemexception.pas (538 bytes)
  • exceptiondll.rar (2,509 bytes)
  • x64ExceptionTrap.pas (5,074 bytes)
    unit x64ExceptionTrap;
    
    {$mode Delphi}
    
    interface
    
    implementation
    
    uses
      Windows, SysUtils;
    
    
    type
      M128A = record
        Low : QWord;
        High : Int64;
      end;
    
      PContext = ^TContext;
      TContext = record
        P1Home : QWord;
        P2Home : QWord;
        P3Home : QWord;
        P4Home : QWord;
        P5Home : QWord;
        P6Home : QWord;
        ContextFlags : DWord;
        MxCsr : DWord;
        SegCs : word;
        SegDs : word;
        SegEs : word;
        SegFs : word;
        SegGs : word;
        SegSs : word;
        EFlags : DWord;
        Dr0 : QWord;
        Dr1 : QWord;
        Dr2 : QWord;
        Dr3 : QWord;
        Dr6 : QWord;
        Dr7 : QWord;
        Rax : QWord;
        Rcx : QWord;
        Rdx : QWord;
        Rbx : QWord;
        Rsp : QWord;
        Rbp : QWord;
        Rsi : QWord;
        Rdi : QWord;
        R8 : QWord;
        R9 : QWord;
        R10 : QWord;
        R11 : QWord;
        R12 : QWord;
        R13 : QWord;
        R14 : QWord;
        R15 : QWord;
        Rip : QWord;
        Header : array[0..1] of M128A;
        Legacy : array[0..7] of M128A;
        Xmm0 : M128A;
        Xmm1 : M128A;
        Xmm2 : M128A;
        Xmm3 : M128A;
        Xmm4 : M128A;
        Xmm5 : M128A;
        Xmm6 : M128A;
        Xmm7 : M128A;
        Xmm8 : M128A;
        Xmm9 : M128A;
        Xmm10 : M128A;
        Xmm11 : M128A;
        Xmm12 : M128A;
        Xmm13 : M128A;
        Xmm14 : M128A;
        Xmm15 : M128A;
        VectorRegister : array[0..25] of M128A;
        VectorControl : QWord;
        DebugControl : QWord;
        LastBranchToRip : QWord;
        LastBranchFromRip : QWord;
        LastExceptionToRip : QWord;
        LastExceptionFromRip : QWord;
      end;
    
    type
      PExceptionRecord = ^TExceptionRecord;
      TExceptionRecord = record
        ExceptionCode   : DWord;
        ExceptionFlags  : DWord;
        ExceptionRecord : PExceptionRecord;
        ExceptionAddress : Pointer;
        NumberParameters : DWord;
        ExceptionInformation : array[0..EXCEPTION_MAXIMUM_PARAMETERS-1] of Pointer;
      end;
    
      PExceptionPointers = ^TExceptionPointers;
      TExceptionPointers = packed record
        ExceptionRecord   : PExceptionRecord;
        ContextRecord     : PContext;
      end;
    
    function AddVectoredExceptionHandler(FirstHandler: DWORD; VectoredHandler: pointer): pointer; stdcall;
      external 'kernel32.dll' name 'AddVectoredExceptionHandler';
    function RemoveVectoredExceptionHandler(VectoredHandlerHandle: pointer): ULONG; stdcall;
      external 'kernel32.dll' name 'RemoveVectoredExceptionHandler';  
    function GetModuleHandleEx(dwFlags: DWORD; lpModuleName: pointer; var hModule: THandle): BOOL; stdcall;
      external 'kernel32.dll' name 'GetModuleHandleExA';
    
    const
      GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT = 2;
      GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS       = 4;
    
    // These entries are linked from FPC's RTL.
    // If the RTL changes, the entries should be changed accordingly.
    function syswin64_x86_64_exception_handler(excep : pointer) : Longint; external name 'SYSTEM_SYSWIN64_X86_64_EXCEPTION_HANDLER$PEXCEPTIONPOINTERS$$LONGINT';
    var _fltused: int64 external name '_fltused';
    
    // Test if the exception address resides in our DLL.
    function CheckOurModule(p: Pointer): boolean;
    var
      ModuleWithException: THandle;
      OurModule: THandle;
      Flags: DWORD;
    begin
      Result := False;
    
      { It's necessary to keep refcount intact. }
      Flags := GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS or GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT;
    
      with PExceptionPointers(p)^.ExceptionRecord^ do
        Result := GetModuleHandleEx(Flags, ExceptionAddress, ModuleWithException) and
          GetModuleHandleEx(Flags, @CheckOurModule, OurModule) and (ModuleWithException = OurModule);
    end;
    
    function ProcessException(p: Pointer): longint; stdcall;
    var
      _SS: PCardinal;
      Saved: TExceptionRecord;
    begin
      Result := 0;
    
      if CheckOurModule(p) then
      begin
        Saved := PExceptionPointers(p)^.ExceptionRecord^;
    
        with PExceptionPointers(p)^.ExceptionRecord^ do
        begin
          // Dirty hack - in system.pp, private variable _SS is just after public _fltused. This might change in the future.
          _SS := @_fltused;
          inc(PBYTE(_SS), sizeof(int64));
          _SS^ := PExceptionPointers(p)^.ContextRecord^.SegSs;
    
          // Trying to unwind the stack in FPC's way - by walking the linked list of exception handers.
          Result := syswin64_x86_64_exception_handler(p);
        end;
    
        if Result <> 0 then
        begin
          // The FPC's unwind failed for some reason.
          // Restoring the Exception record, so the program's exception handlers can try to recover from the exception.
          PExceptionPointers(p)^.ExceptionRecord^ := Saved;
    
          // You can insert some kind of logging etc here.
          // ...
        end;
      end;
    end;
    
    var
      VEHandler: pointer = Nil;
    procedure InstallExceptionHandler;
    begin
      VEHandler := AddVectoredExceptionHandler(1, @ProcessException);
    end;
    
    procedure UninstallExceptionHandler;
    begin
      if Assigned(VEHandler) then
      begin
        RemoveVectoredExceptionHandler(VEHandler);
        VEHandler := Nil;
      end;
    end;
    
    initialization
      InstallExceptionHandler;
    finalization
      UninstallExceptionHandler;
    end.
    
    x64ExceptionTrap.pas (5,074 bytes)
  • fpc_w32_dll_error_solved.zip (59,718 bytes)

Relationships

related to 0025363 closedSergei Gorelkin FPC win32 per thread SEH implemantaion. 
related to 0024908 new FPC Handling of Windows Exceptions as signals makes catching them impossible 
has duplicate 0004605 closedJonas Maebe FPC Wrong exceptions handling in DLL 
has duplicate 0010421 closedJonas Maebe FPC try... block in a Win32 DLL doesn't catch AVs 
related to 0014731 resolvedMarco van de Voort FPC Exceptions in Windows x64 
related to 0017280 resolvedReinier Olislagers FPC C++ exception not handled 
related to 0023449 resolvedJuha Manninen Lazarus Lazarus 1.0.4 32 bit crashes on selecting images for image list 
related to 0024012 closedSergei Gorelkin FPC Enable Win64 SEH by default so exceptions in DLLs can properly be caught 
related to 0023026 closedJesus Reyes Lazarus Program crashes if printer units are added to project under windows 64bit 
related to 0025312 assignedSven Barth FPC Unhandled exception when using w32 fiber API 
related to 0028756 new FPC Enable Win32 SEH by default 

Activities

Florian

2009-01-13 13:20

administrator   ~0024407

The problem why I hesitated so far to implement this is that there is afaik no offical documentation about the exception handling using fs:0. Neither the structures are documented, nor there are function calls in the win api to work with them. Last but not least this approach doesn't help for shared libs on unix like systems. IMO a sharemem like approach would be more cross platform.

Carlo Kok

2009-01-13 14:20

reporter   ~0024414

Last edited: 2009-01-13 14:30

on unix, maybe. On Windows a sharemem approach would block any chance of try being usable in a plugin to any non-fpc application.

some links on msdn about this, though I must admit there isn't much:

http://msdn.microsoft.com/en-us/library/ms253960.aspx
http://www.microsoft.com/msj/0197/Exception/Exception.aspx

Florian

2009-01-13 14:44

administrator   ~0024416

Another helpful link might be: http://www.jorgon.freeserve.co.uk/Except/Except.htm

AlexRayne

2009-01-28 09:19

reporter   ~0024812

hallow all!
i got an testbench of this problem wich shows how one leads to incompatibility with Delphi under Windows at least (delphi 7 normaly catches windows system exceptions), see attached file.
explanation of test code:
   second CloseFile\CloseHandle respond by exception from system CloseHandle. Delphi normaly catches one and execute exception block. FPC handles exception somwhere else and restore excution from failure point - so it look like there is no exception occures, and whole code executes plain.

2009-01-28 09:20

 

tbsystemexception.pas (538 bytes)
{$APPTYPE CONSOLE}
program tbsystemexception;

uses sysutils
    , windows
    ;

var
    fileh : thandle;
begin
    try
        fileh := FileOpen('tbsystemexception.pas', fmOpenRead);
        FileClose(fileh);
        FileClose(fileh);
        writeln('fileclosed normaly');

        fileh := FileOpen('tbsystemexception.pas', fmOpenRead);
        CloseHandle(fileh);
        CloseHandle(fileh);
        writeln('handleclosed normaly');
    except
        ShowException(ExceptObject, ExceptAddr);
    end;
end.

tbsystemexception.pas (538 bytes)

Eric Heijnen

2009-02-06 00:53

reporter   ~0025151

another useful link for 64-bit exception handling in windows:
http://www.tortall.net/projects/yasm/manual/html/objfmt-win64-exception.html

Also adding myself to the list of people that would love this implemented. (It's for a plugin dll I wrote in delphi that I want to port to 64-bit)

Alex Belyakov

2009-02-18 10:57

reporter   ~0025543

Not only plugns are affected by this error.
x64 program crashes, when AV occurs in a hook DLL installed with SetWindowsHookEx. Proper try/except handlers are present in the DLL.

The same hook DLL compiled for Win32 works fine.

Alex Belyakov

2009-02-19 06:04

reporter   ~0025569

The SEH in Win64 relies on .pdata and optionally on .xdata segments.
Unfortunately, there's no good description of these segments for Windows x64 (only for Tru64 UNIX). A bit of info can be found here: http://msdn.microsoft.com/en-us/library/1eyas8tf.aspx , and something is here: http://www.nasm.us/doc/nasmdoc7.html#section-7.4.2

Eric Heijnen

2010-02-01 03:46

reporter   ~0034120

Small update, in my dll I've been using the windows api AddVectoredExceptionHandler (winXP +) to capture exceptions caused by the dll

Seth Grover

2010-03-15 15:18

reporter   ~0035561

This feature is a gaping hole in a project I'm working on. If it's not too much trouble, would you mind attaching a simple .dll project illustrating how you've used AddVectoredExceptionHandler to capture exceptions in the .dll?

2010-03-17 02:16

 

exceptiondll.rar (2,509 bytes)

Eric Heijnen

2010-03-17 02:19

reporter   ~0035682

Last edited: 2010-03-17 02:32

I've added a small example dll on how to use AddVectoredExceptionHandler
You can also just use the exceptionhandler.pas in your project if you want to.

It is a little annoying having to declare the Try, Except and End as a label and initializing them each time before doing something that can cause an exception, but it's at least something. (Not sure if fpc supports macro's because that could be used to make it a bit easier)

Also, it'll probably not work if the error occurs in a function called from within the try/except block and optimization might cause a problem. (Tip for expansion: Add in a stacktrace that checks if it eventually does end up in the try/except region)

Alexander Belyakov

2010-03-19 06:09

reporter   ~0035788

Last edited: 2010-03-19 06:11

I've also made a small unit which can be added to the DLL's project to solve the problem (please refer to the attached x64ExceptionTrap.pas unit).

The unit also uses AddVectoredExceptionHandler to trap the exceptions.
The stack unwinding is made in RTL's way (just like in a standalone program), so native try/except/finally can safely be used, and even nested try/except/finally works fine.
And there's no need for use any labels or anything else.

The unit heavily relies on a couple of symbols exported by RTL's system.pp, namely syswin64_x86_64_exception_handler and _fltused, and it was only tested and used with FPC 2.2.2.

2010-03-19 06:10

 

x64ExceptionTrap.pas (5,074 bytes)
unit x64ExceptionTrap;

{$mode Delphi}

interface

implementation

uses
  Windows, SysUtils;


type
  M128A = record
    Low : QWord;
    High : Int64;
  end;

  PContext = ^TContext;
  TContext = record
    P1Home : QWord;
    P2Home : QWord;
    P3Home : QWord;
    P4Home : QWord;
    P5Home : QWord;
    P6Home : QWord;
    ContextFlags : DWord;
    MxCsr : DWord;
    SegCs : word;
    SegDs : word;
    SegEs : word;
    SegFs : word;
    SegGs : word;
    SegSs : word;
    EFlags : DWord;
    Dr0 : QWord;
    Dr1 : QWord;
    Dr2 : QWord;
    Dr3 : QWord;
    Dr6 : QWord;
    Dr7 : QWord;
    Rax : QWord;
    Rcx : QWord;
    Rdx : QWord;
    Rbx : QWord;
    Rsp : QWord;
    Rbp : QWord;
    Rsi : QWord;
    Rdi : QWord;
    R8 : QWord;
    R9 : QWord;
    R10 : QWord;
    R11 : QWord;
    R12 : QWord;
    R13 : QWord;
    R14 : QWord;
    R15 : QWord;
    Rip : QWord;
    Header : array[0..1] of M128A;
    Legacy : array[0..7] of M128A;
    Xmm0 : M128A;
    Xmm1 : M128A;
    Xmm2 : M128A;
    Xmm3 : M128A;
    Xmm4 : M128A;
    Xmm5 : M128A;
    Xmm6 : M128A;
    Xmm7 : M128A;
    Xmm8 : M128A;
    Xmm9 : M128A;
    Xmm10 : M128A;
    Xmm11 : M128A;
    Xmm12 : M128A;
    Xmm13 : M128A;
    Xmm14 : M128A;
    Xmm15 : M128A;
    VectorRegister : array[0..25] of M128A;
    VectorControl : QWord;
    DebugControl : QWord;
    LastBranchToRip : QWord;
    LastBranchFromRip : QWord;
    LastExceptionToRip : QWord;
    LastExceptionFromRip : QWord;
  end;

type
  PExceptionRecord = ^TExceptionRecord;
  TExceptionRecord = record
    ExceptionCode   : DWord;
    ExceptionFlags  : DWord;
    ExceptionRecord : PExceptionRecord;
    ExceptionAddress : Pointer;
    NumberParameters : DWord;
    ExceptionInformation : array[0..EXCEPTION_MAXIMUM_PARAMETERS-1] of Pointer;
  end;

  PExceptionPointers = ^TExceptionPointers;
  TExceptionPointers = packed record
    ExceptionRecord   : PExceptionRecord;
    ContextRecord     : PContext;
  end;

function AddVectoredExceptionHandler(FirstHandler: DWORD; VectoredHandler: pointer): pointer; stdcall;
  external 'kernel32.dll' name 'AddVectoredExceptionHandler';
function RemoveVectoredExceptionHandler(VectoredHandlerHandle: pointer): ULONG; stdcall;
  external 'kernel32.dll' name 'RemoveVectoredExceptionHandler';  
function GetModuleHandleEx(dwFlags: DWORD; lpModuleName: pointer; var hModule: THandle): BOOL; stdcall;
  external 'kernel32.dll' name 'GetModuleHandleExA';

const
  GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT = 2;
  GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS       = 4;

// These entries are linked from FPC's RTL.
// If the RTL changes, the entries should be changed accordingly.
function syswin64_x86_64_exception_handler(excep : pointer) : Longint; external name 'SYSTEM_SYSWIN64_X86_64_EXCEPTION_HANDLER$PEXCEPTIONPOINTERS$$LONGINT';
var _fltused: int64 external name '_fltused';

// Test if the exception address resides in our DLL.
function CheckOurModule(p: Pointer): boolean;
var
  ModuleWithException: THandle;
  OurModule: THandle;
  Flags: DWORD;
begin
  Result := False;

  { It's necessary to keep refcount intact. }
  Flags := GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS or GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT;

  with PExceptionPointers(p)^.ExceptionRecord^ do
    Result := GetModuleHandleEx(Flags, ExceptionAddress, ModuleWithException) and
      GetModuleHandleEx(Flags, @CheckOurModule, OurModule) and (ModuleWithException = OurModule);
end;

function ProcessException(p: Pointer): longint; stdcall;
var
  _SS: PCardinal;
  Saved: TExceptionRecord;
begin
  Result := 0;

  if CheckOurModule(p) then
  begin
    Saved := PExceptionPointers(p)^.ExceptionRecord^;

    with PExceptionPointers(p)^.ExceptionRecord^ do
    begin
      // Dirty hack - in system.pp, private variable _SS is just after public _fltused. This might change in the future.
      _SS := @_fltused;
      inc(PBYTE(_SS), sizeof(int64));
      _SS^ := PExceptionPointers(p)^.ContextRecord^.SegSs;

      // Trying to unwind the stack in FPC's way - by walking the linked list of exception handers.
      Result := syswin64_x86_64_exception_handler(p);
    end;

    if Result <> 0 then
    begin
      // The FPC's unwind failed for some reason.
      // Restoring the Exception record, so the program's exception handlers can try to recover from the exception.
      PExceptionPointers(p)^.ExceptionRecord^ := Saved;

      // You can insert some kind of logging etc here.
      // ...
    end;
  end;
end;

var
  VEHandler: pointer = Nil;
procedure InstallExceptionHandler;
begin
  VEHandler := AddVectoredExceptionHandler(1, @ProcessException);
end;

procedure UninstallExceptionHandler;
begin
  if Assigned(VEHandler) then
  begin
    RemoveVectoredExceptionHandler(VEHandler);
    VEHandler := Nil;
  end;
end;

initialization
  InstallExceptionHandler;
finalization
  UninstallExceptionHandler;
end.
x64ExceptionTrap.pas (5,074 bytes)

Seth Grover

2010-03-22 15:35

reporter   ~0035915

Alexander, thank you very much, this looks very interesting and promising.

Bernd Engelhardt

2010-03-24 11:07

reporter   ~0036018

@Alexander: Is AddVectoredExceptionHandler only working, if the parent application (exe) is not a FPC application? I am trying to use your code in FPC 2.5.1, but can't catch any exception in my dll. The exception is always catched by the parent application (exe). The registered function "ProcessException" never gets the exception.

Alexander Belyakov

2010-03-30 07:03

reporter   ~0036257

@Bernd: AddVectoredExceptionHandler's work does not depend on the host application's compiler.
We inject our DLL (which uses the x64ExceptionTrap.pas) into processes with SetWindowsHookEx, and ProcessException sucessfully catches the exceptions, and unwinds the stack.

There can possibly be the situation when AddVectoredExceptionHandler won't be able to intercept the exception - when the host application installs its own handler, and returns EXCEPTION_CONTINUE_EXECUTION from it. In this case, ProcessException is simply skipped.

Are you sure that ProcessException is not called? Perhaps, ProcessException incorrectly locates _SS variable when compiled with FPC 2.5.1.
Have you tried to check what's going on in a debugger?

Bernd Kreuss

2010-08-28 22:01

reporter   ~0040573

I have experimentd with Eric Heijnen's approach of using AddVectoredExceptionHandler and this seems to work for simple things but I have a question: Isn't it somehow possible to replace the host application's exception handler with the one from FPC only for the duration of one function call?

I am on win32 and I have a host application that does its own exception handling (it will pop up a message box with not much useful info and then immediately terminate, in other words: It will simply crash) and I need to write a plugin for it. The Host application will call a function in my DLL giving it some pointers (that are sometimes invalid once a week for unknown reasons and I would like to detect and handle this) and I would be nice if the following could somehow made possible:

When entering the dll function I do something (fs:0 ?) that puts FPC's own exception handling in place and saves whatever was there before. Then I would use as many nested Try/Except as I want and call other functions in my dll and raise exceptions as I like without crashing the host application and before my exported dll function returns i would do something to restore the original exception handler and then let it return to the host application.

I have tried to look into the system and sysutils units to find the place where exactly (and how) FPC makes its exception handler known to the OS so I could try to do the same after entering my dll function but I could not find anything.

I would really love to have a solution to this exception problem, It would save me a lot of headaches.

Sven Barth

2010-08-29 14:50

manager   ~0040590

FPC uses a system independant exception handling scheme which uses a frame based approach like SEH. You can find this implementation in rtl/inc/except.inc (but you might need some time to understand it. I hope to somewhen find the time to document it in the Wiki ^^)

The interface to SEH on Windows in FPC works as follows: In the initialization section of the system unit (rtl/win32/system.pp) install_exception_handlers is called which in turn uses the WinAPI function SetUnhandledExceptionFilter ( http://msdn.microsoft.com/en-us/library/ms680634%28VS.85%29.aspx ) to set up a handler for unhandled SEH exceptions. This handler is called by Windows if a SEH exception is raised (not done by FPC's exception code, but can be done with RaiseException ( http://msdn.microsoft.com/en-us/library/ms680552%28v=VS.85%29.aspx )) and is not handled by a SEH handler.

You might take a look at the syswin32_i386_exception_handler function directly above the install_exception_handlers function, which is the handler mentioned above. The code either does special things (Ctrl-C handler, illegal instruction, conversion to runtime error) or tries to pass control to the FPC exception handlers.

In theory(!) it should be sufficient to surround the code in all your exported functions with a "try ... except ... end", so that no FPC exceptions are passed beyond the DLL boundary. If you really need to pass exceptions past the DLL boundary you might convert the exceptions to SEH exceptions by catching them and passing their information with RaiseException to the calling code (but if your calling code is FPC code this exception might not be handled by the unhandled exception handler).

Regards,
Sven

Bernd Kreuss

2010-08-29 15:54

reporter   ~0040592

I aleady tried to simply surround them with try/except but they wont get caught. whatever I do, the exceptions always are directly propagated to the host application, FPC's own exception handling doesn't ever see the exception, the host application's handler will pop up its message box and then terminate the whole application.

I did some further experimenting with the AddVectoredExceptionHandler function and it seems I was too early with my previous comment. It works in examples but today I tried it on the real application and there it fails. What happens is the following:

My newly installed exception handler gets called (I can verify this by popping up my own messagebox but then when I try to write (or even read) ExcInfo^.ContextRecord^.Eip to tell it where to continue this will trigger another access violation.

This is what I used to debug this:

function ExceptionHandler(ExcInfo: PEXCEPTION_POINTERS): ULONG;
begin
  RemoveVectoredExceptionHandler(ExHandle);
  MessageBox(0, PChar(IntToHex(PtrUInt(@ExcInfo^.ContextRecord^.Eip), 8)), 'old',0);
  MessageBox(0, PChar(IntToHex(PtrUInt(ExcInfo^.ContextRecord^.Eip), 8)), 'old',0);

I immediately remove my own handler to see the next exception displayed and formattd in the host application's handler.

Then I provoke an access violation and what the above code then does is it will pop up a messagebox showing me the address of ExcInfo^.ContextRecord^.Eip (which means that at least the ExcInfo Pointer is pointing to somewhere valid) and then when trying to pop up the second messagebox it will trigger another exception (which will then be caught by the host application) and it shows me that I tried to read from ExcInfo^.ContextRecord^.Eip

(created by the host application's exception handler:)
Exception : C0000005
Address : 10001593
Access Type : read
Access Addr : 5D8935A4

5D8935A4 is the address my first messagebox showed me as the address of ExcInfo^.ContextRecord^.Eip

My Exception handler gets called and it is given a valid PEXCEPTION_POINTERS but I am not allowed to write or read anything from in the ContextRecord. What on earth could this be?

Bernd Kreuss

2010-08-29 16:23

reporter   ~0040593

This doesn't work either:

function ExceptionHandler(ExcInfo: PEXCEPTION_POINTERS): ULONG;
begin
  RemoveVectoredExceptionHandler(ExHandle);
  asm
    mov esp, ExStack;
    jmp ExContinue;
  end;
end;


procedure ...(...) // my exported function
label
  lblExCont;

begin
  ExContinue := PtrUInt(@LblExCont);
  asm
    mov ExStack, esp;
  end;
  ExHandle := AddVectoredExceptionHandler(1, @ExceptionHandler);


  // provoke access violation
  buf_bal[9999999] := 42;


  [...] // all the rest of the code


lblExCont:
  // to here we jump on any exception
  MessageBox(0, 'bar', 'bar',0);
  //RemoveVectoredExceptionHandler(ExHandle);
end;

will jump to my label and show the messagebox but crash the application when returning to the host app.

Sven Barth

2010-08-29 16:26

manager   ~0040594

I can't test it at the moment.

Is your handler defined as stdcall?

Regards,
Sven

Bernd Kreuss

2010-08-29 20:34

reporter   ~0040611

OK, there were two errors: I forgot to define the handler as stdcall and there occured another exception *before* my provoked access violation, it was a debug print exception that was silently ignored by the host application but my handler picked it up and this was where it crashed.

For the record: my handler now looks like this:

function ExceptionHandler(Info: PEXCEPTION_POINTERS): ULONG; stdcall;
begin
  if Info^.ExceptionRecord^.ExceptionCode = STATUS_ACCESS_VIOLATION then begin
    Info^.ContextRecord^.Eip := ExContinue;
    Info^.ContextRecord^.Esp := ExEsp;
    ExTriggered := True;
    Result := EXCEPTION_CONTINUE_EXECUTION;
  end else begin
    Result := EXCEPTION_CONTINUE_SEARCH;
  end;
end;

and I use it the following way: immediately after entering my dll function i do this:

  ExTriggered := False;
  ExContinue := PtrUInt(@lblExCont);
  asm
    mov ExEsp, esp;
  end;
  ExHandle := AddVectoredExceptionHandler(1, @ExceptionHandler);

These are all global variables (I still have no idea how i could make this thread safe). After this handler is in place I do all the dangerous operations with the pointers i have got from the host application. At the very end of the function I have this:

lblExCont:
  RemoveVectoredExceptionHandler(ExHandle);
  if ExTriggered then begin
    OutputDebugString(PChar(Format('AV occured: bars: %d, trade_count: %d', [bars, trade_count])));
  end;

This seems to work now. It does not matter where exactly the exception occurs, I tried triggering it somewhere inside another function call, the handler will restore the stack pointer and make it continue at the label lblExCont.

But this is really cumbersome. It would really be nice if I could simply catch them with try/except. Maybe the 64 bit example x64ExceptionTrap.pas by Alexander Belyakov could somehow be adapted for 32 bit windows but this is way over my head.

Maybe implement 2 functions in the RTL that one could easily call to turn the FPC exception handling on and off in a dll function.

Bernd Engelhardt

2010-08-30 10:52

reporter   ~0040626

@Bernd Kreuss: Could you please post a sample sample application? Did you build it for Win32 or Win64? Did you define you own PEXCEPTION_POINTERS structure, because I can't find ".eip" or ".esp" on Win64 (FPC 2.5.1)

The problem with the global variables could be handled via TLS (Thread Local Storage): http://msdn.microsoft.com/en-us/library/ms686749(VS.85).aspx

Bernd Kreuss

2010-08-30 12:41

reporter   ~0040633

Last edited: 2010-08-30 13:00

@Bernd Engelhardt: it is win32. I used only structures from the windows unit. This problem exists on not only on win64 it is also on win32 (and probably other platforms too).

eip and esp are the names of the i386 processor registers holding the instruction pointer and the stack pointer on i386. I deduced from the other example posted earlier that Windows will fill this with the current values of the registers at the time the exception happened and later again initialize the processor registers from this structure when it is about to let execution continue. In my handler I simply set the instruction pointer to the address of my label (the equivalent of Except) and the stack pointer to the value it should have when execution is at this point (the same value it had when I installed the handler).

When I have managed to install a C compiler on my windows I will try to make a simple test application in C and a DLL in FPC because the DLL i am currently working on is part of a plugin for the poprietary software Metatrader 4 (metaquotes.net) and it is a bit cumbersome to setup a simple test case with this if you never used it before (this software has a built in scripting language and from these scripts one can load external DLLs and import their functions).

If you already have a C compiler around it should be simple to reproduce: make an application that imports a function from a DLL built with FPC and inside this DLL function provoke an access violation and try to catch it inside the DLL.

Bernd Engelhardt

2010-08-30 14:12

reporter   ~0040645

Last edited: 2010-08-30 14:14

@Bernd Kreus: I have already a sample application (C++), because I have the same problem with Lotus Domino (see 0017280). In this ticket is already a C++ sample for Visual Studio 2010 Express. You can download everything from this ticket and you will find the links for the compiler in there.

I have the problem on Win64. I have a DLL which throws an exception, the exception is caught in the DLL, but FPC still receives the exception. Very strange situation.

I want to port your sample to Win64, but have problems to find the right structure definition.

Bernd Kreuss

2010-08-30 15:20

reporter   ~0040649

Last edited: 2010-08-30 15:44

@Bernd Engelhardt: It seems your problem is exactly the other way around: You have a DLL written in C++ and the host application is built with FPC. And your problem seems even more strange (and unexplainable) than what I have (at least I *think* I have now understood the nature of my problem and what exactly happens). I have a DLL written in FPC and the host app is something else (closed source, probably C++) and the host app has registered the handler with the highest precedence and is just handling *everything*.

Unfortunately I have no idea how the processor registers on x64 are named and supposed to work and I don't have one to try but I think you will at least need the ones with the instruction pointer and the stack pointer and your handler should return EXCEPTION_CONTINUE_EXECUTION which should then completely clear the exception and make it continue where you tell it (by setting the instruction pointer). If you don't set the instruction pointer to somewhere behind the faulty instruction it will (on win32) try (and raise the same exception) again and again and again many times until windows decides to just terminate it.

On win32 it seems (from what I have read) the Handlers set by AddVectoredExceptionHandler() always have precedence over any other handlers set by using the older fs:0 method. If such a handler handles the exception and returns EXCEPTION_CONTINUE_EXECUTION it should be done and not call any of the other handlers anymore.

Bernd Engelhardt

2010-08-30 15:51

reporter   ~0040653

Is it possible for you to build a small sample (EXE application) which works on Win32? I will then try to convert it to Win64. You posted some code fragments above, but is it possible to provide a complete sample? I have the Win64 SDK on my machine and I have found the structure in WinNT.h. Maybe I can convert these structures.

Sven Barth

2010-08-30 17:05

manager   ~0040657

@Bernd K.:
The order the exception handlers are called in is roughly the following:

1. an attached debugger gets the chance to handle the exception
2. the system walks to frame to find a handler using the fs:[0] register
3. if all handlers returned EXCEPTION_CONTINUE_SEARCH it asks each handler added with AddVectoredExceptionHandler (this is done by FPC on Win64)
4. if also all handlers returned EXCEPTION_CONTINUE_SEARCH it calls the method specified by SetUnhandledExceptionFilter (this is the approach done by FPC on all Windows platforms)
5. a debugger gets a second chance to handle the exception
6. the system handles the exception by showing the "an error has occured" dialog (or - in kernelmode - by bugchecking the system (bluescreen))

@Bernd E:
The stack pointer is RSP and the instruction pointer is RIP. You can also find the structures in the system unit in "rtl/win64/system.pp". The one with the CPU context is named "TContext".

Regards,
Sven

Bernd Kreuss

2010-08-30 19:09

reporter   ~0040660

Last edited: 2010-08-30 19:47

I have a complete example for Windows XP 32 bit. Here is the DLL:

library testdll;

{$mode objfpc}{$H+}
{$ASMMODE INTEL}

uses
  sysutils, windows;

var
  ExExcept: Pointer; // the address of the simulated except statement
  ExEsp: DWORD; // saved stack pointer
  ExEbp: DWORD; // saved base pointer
  ExHandle: Pointer; // the handle needed to remove the handler again
  ExTriggered: Boolean; // did we catch an exception?

function AddVectoredExceptionHandler(FirstHandler: DWORD; VectoredHandler: pointer): pointer; stdcall; external 'kernel32.dll' name 'AddVectoredExceptionHandler';
function RemoveVectoredExceptionHandler(VectoredHandlerHandle: pointer): ULONG; stdcall; external 'kernel32.dll' name 'RemoveVectoredExceptionHandler';

function ExceptionHandler(Info: PEXCEPTION_POINTERS): ULONG; stdcall;
begin
  writeln('dll: handler caught exception ' + IntToHex(Info^.ExceptionRecord^.ExceptionCode, 8));
  if Info^.ExceptionRecord^.ExceptionCode = EXCEPTION_ACCESS_VIOLATION then begin
    ExTriggered := True;

    // tell it where to continue
    Info^.ContextRecord^.Eip := PtrUInt(ExExcept);

    // restore the stack frame
    Info^.ContextRecord^.Esp := ExEsp;
    Info^.ContextRecord^.Ebp := ExEbp;

    // clear the exception and let execution continue
    Result := EXCEPTION_CONTINUE_EXECUTION;

  end else begin
    // no AV, not our business, let the next handler deal with it.
    Result := EXCEPTION_CONTINUE_SEARCH;
  end;
end;

procedure ProvokeAccessViolation;
var
  P : ^integer;
begin
  p := nil;
  p^ := 42;
end;

function test1: LongInt; stdcall;
begin
  Result := 0;
  try
    writeln('dll: inside test1, provoking an access violation now.');
    ProvokeAccessViolation;
  except
    writeln('dll: exception caught inside the dll');
    Result := -1;
  end;
end;

function test2: LongInt; stdcall;
label
  lblExExcept;
begin
  Result := 0;

  // begin our simulated try statement:
  ExTriggered := False;
  ExExcept := @lblExExcept;
  asm
  mov ExEsp, esp;
  mov ExEbp, ebp;
  end;
  ExHandle := AddVectoredExceptionHandler(1, @ExceptionHandler);
  // end of try statement

  writeln('dll: inside test2, provoking an access violation now.');
  ProvokeAccessViolation;

  // begin our simulated except statement
  lblExExcept:
  RemoveVectoredExceptionHandler(ExHandle);
  if ExTriggered then begin;
    writeln('dll: exception caught inside the dll');
    Result := -1;
  end;
  // end of simulated except statement

end;

exports
  test1, test2;

{$R *.res}

begin
end.



compile this to testdll.dll and use the following Python 2.6 program to load the dll and call the 2 exported functions:



from ctypes import *

print(".py: calling test1")
try:
    print(".py: dll function returned " + str(cdll.testdll.test1()))
except Exception as e:
    print(".py: exception caught outside dll: " + str(e))

print(".py: python program still running");


print("\n.py: calling test2")
try:
    print(".py: function returned " + str(cdll.testdll.test2()))
except Exception as e:
    print(".py: exception caught outside dll: " + str(e))

print(".py: python program still running");


if I run the python program then it will output the following:

C:\dlltest>test.py
.py: calling test1
dll: inside test1, provoking an access violation now.
.py: exception caught outside dll: exception: access violation writing 0x00000000
.py: python program still running

.py: calling test2
dll: inside test2, provoking an access violation now.
dll: handler caught exception C0000005
dll: exception caught inside the dll
.py: function returned -1
.py: python program still running

C:\dlltest>

The functions test1 and test2 do essentially the same, only test1 uses try/except but the exception will not be caught and instead propagated to the python program (this is a demonstration of this bug) and test2 is the dirty workaround, it explicitely installs its own temporary exception handler, simulating the try/except and this time it will be able to catch the exception. I had to save not only esp but also ebp, the previous example is wrong and only works coincidentally, depending on what optimization level is used.

Bernd Kreuss

2010-08-30 19:31

reporter   ~0040662

Of course this workaround has some problems, for example if there was an exception that is not processed by our handler it will go to the next handler which is probably in the host application and leave the dll and we have no chance to remove our temporary handler again. I only use this as a last resort to prevent the host application from crashing and because the only exceptions that can ever occur in my dll are access violations.

Bernd Kreuss

2010-08-31 15:53

reporter   ~0040681

this is relevant, its another workaround for this bug:

http://www.pascalgamedevelopment.com/forum/index.php?topic=6062.0

It is even more elegant (and IMHO less hackish) than the previously posted workarounds. It will catch all access violations with a handler but instead of trying to handle them and mess around with the stack it will simply clear and re-raise them as native pascal exceptions. These will then be easily caught by any try/except inside the dll.

The handler must be installed only once in the initialization of the dll and can stay there forever.

Bernd Kreuss

2010-08-31 16:29

reporter   ~0040683

Last edited: 2010-08-31 16:45

Inspired by the above mentioned link I have now a new complete workaround fo windows 32. The following is my completely self-contained all-in-one universal exception handler function. Put it into the dll, install it on initialization and remove it on finalization:

function ExceptionHandler(Info: PEXCEPTION_POINTERS): LongInt; stdcall;
  function GetModuleByAddr(addr: pointer): THandle;
  var
    Tmm: TMemoryBasicInformation;
  begin
    if VirtualQuery(addr, @Tmm, SizeOf(Tmm)) <> sizeof(Tmm)
      then Result:=0
      else Result:= THandle(Tmm.AllocationBase);
  end;

label
  lblRaiseAccessViolation;

begin
  // did it happen in the same module where this function resides?
  if GetModuleByAddr(Info^.ExceptionRecord^.ExceptionAddress) =
     GetModuleByAddr(@lblRaiseAccessViolation) then begin

    // we only care about access violations
    if Info^.ExceptionRecord^.ExceptionCode = STATUS_ACCESS_VIOLATION then begin

      // clear the windows exception and point it to a place that
      // will raise a pascal exception instead.
      writeln('dll: handler: windows exception caught');
      Info^.ContextRecord^.Eip := PtrUInt(@lblRaiseAccessViolation);
      exit(EXCEPTION_CONTINUE_EXECUTION);

    end else
      exit(EXCEPTION_CONTINUE_SEARCH);

  end else
    exit(EXCEPTION_CONTINUE_SEARCH);

  // the following will never be reached during this function call,
  // instead it will be jumped to and executed *after* this function has
  // returned and windows restarts execution at the new position of eip.
  lblRaiseAccessViolation:
  writeln('dll: handler: will now reraise it as a pascal exception');
  raise EAccessViolation.Create('access violation');
end;

The handler will compare the exception address and the address of the label in the function to determine whether it actually happened inside this very same DLL and otherwise completely ignore it and at the end of the function I have a block of seemingly dead code that is only used to be jumped into from the outside (after the exception has been cleared) and re-raise the exception as a native pascal exception.

With this handler in place all access violations in the dll can be caught by ordinary try/except. With a bit more work I am sure it could be extended to also catch and re-raise a bunch of other exceptions like zero division etc.

Wouldn't such an approach be a good and easy to implement candidate for a permanent bug fix? Simply catching the few OS-specific exceptions and re-raising the same exception as a native FPC exception and then handling it the normal way would remove all need for rewriting the whole stack unwinding and walking the SEH chain or similar things for different platforms.

Bernd Engelhardt

2010-08-31 17:52

reporter   ~0040688

I have reviewed the "system.pp" today (Win64). I found the cause for my "Control-C hit" problem with the external C++ DLL (0017280). The problem is based on the implementation of the exception handling. On Win32 "SetUnhandledExceptionFilter" is used and on Win64 "AddVectoredExceptionHandler".

It seems that "AddVectoredExceptionHandler" receives all exceptions for the whole application. This include any external exception like the one from the C++ DLL. In "syswin64_x86_64_exception_handler": If I change

if ((excep^.ExceptionRecord^.ExceptionCode and SEVERITY_ERROR) = SEVERITY_ERROR) then
begin
  err := 217;
end

to

if ((excep^.ExceptionRecord^.ExceptionCode and SEVERITY_ERROR) = SEVERITY_ERROR) then
begin
  err:=0;
  res:=EXCEPTION_CONTINUE_SEARCH;
end

everything is ok.

So, the question is, why is exception handling different implemented in Win32 and Win64?

I think that "AddVectoredExceptionHandler" is not really a good way, because it receives all exceptions. I can't find any documentation that the exception handling has changed from Win32 to Win64. Why not use the same way on both platforms?

And, to register "AddVectoredExceptionHandler" as the first handler causes problems

For example:

DLL A (raise) <--call-- DLL B(try..catch) <---call-- FPC.EXE

In this case "AddVectoredExceptionHandler" interrupt the "try..catch" of DLL B, because the FPC.EXE register the handler before the handler of "DLL B". If FPC.EXE won't register a handler, the DLL B gets the exception and FPC.EXE never "sees" any exception.

Bernd Kreuss

2010-08-31 18:29

reporter   ~0040689

Last edited: 2010-08-31 18:58

If the handler returns EXCEPTION_CONTINUE_SEARCH then it will go to the next handler, so it doesn't matter that it receives everything. EXCEPTION_CONTINUE_SEARCH will make it use the next handler in the list.

This is why I asked you in the other bug (your problem is fpc-exe and c++-dll and this bug here is c++-exe and fpc-dll, I dont know why they are marked as duplicate, they are related but not duplicate) this is why I asked you to check whether the catch() block in your c++-dll will actually be triggered or whether the FPC-exe will "steal" all exception handling (and then mess it up), even the ones raised in the c++-dll with throw.

IMHO since it seems that exception handling that always works across module boundaries and different compilers are virtually impossible to implement the handler should always first check ExceptionAddress and only care about exceptions whose address is in the same module and completely ignore (EXCEPTION_CONTINUE_SEARCH) everything else. Then it would not matter whether they are registered with AddVectoredExceptionHandler or somehow different, as long as they only care about their own stuff they could all peacefully co-exist and every module had a chance to handle its own exceptions and is not hindered by the .exe owning all exception handling.

Bernd Engelhardt

2010-08-31 19:15

reporter   ~0040690

@Bernd Kreuss: Sorry, forget to answer your question. Yes, you are right, the FPC "steals" the exception from the C++ DLL. I places a message into try..catch but never see it. It seems, that "AddVectoredExceptionHandler" prevents the C++ DLL from handling the exception.

I agree with you. The best would be that a application only handles his own exceptions. I think, we have to try to change the implementation in Win64.

It works, if I let pass the exception with

 err:=0;
 res:=EXCEPTION_CONTINUE_SEARCH;

In this case the DLL can catch an handle the exception and the FPC program works as expected.

Bernd Kreuss

2010-08-31 20:56

reporter   ~0040692

Last edited: 2010-08-31 21:13

Maybe a compiler switch or directive or a define could be implemented that lets the user control whether:

- an .exe will handle all exceptions (default as it seems to be currently)
- an .exe will only handle exceptions that happen in its own address space

(or alternatively (better) make sure it is really only the very last handler in the list -> call AddVectoredExceptionHandler() with 0 and not with 1 as the first argument!)

and for DLLs let the user control whether

- a .dll will not have its own exception handler (default as it is currently)
- a .dll will have a handler for its own exceptions
- a .dll will have a handler for its own exceptions and also catch exceptions that happen in other dlls it calls.

or some public RTL functions to register or unregister the exception handler or modify its responsibility or its precedence at runtime. This would allow a wider range of workarounds around all the possible combinations of foreign compilers and the nastinesses they produce.

Bernd Kreuss

2010-08-31 21:17

reporter   ~0040693

@Bernd E.: Since you are just modifying the system.pp try to change the first argument of AddVectoredExceptionHandler() to 0 instead of 1 where it registers the handler and see if it helps. This will chain it at the very end and not the beginning and IMHO the .exe should always have its handler at the very end of the list.

Bernd Kreuss

2010-08-31 21:44

reporter   ~0040695

Last edited: 2010-08-31 21:48

@Sven: you wrote in note 0040657 that the vectored handlers are called after the frame based SEH handlers.

But this article: http://msdn.microsoft.com/en-us/magazine/cc301714.aspx suggests that it is done *before* the SEH. If this is true then FPC should never use it since IMHO the .exe should always be the last one in the list and only use SetUnhandledExceptionFilter() to give all other modules (which might be C++ and use SEH) a chance to handle it before. I am sure that this would instantly fix bug 0017280.

Sven Barth

2010-09-01 09:37

manager   ~0040697

@Bernd K.:
Regarding the article: seems that I was wrong there. But it's also more logical this way if you think about why vectored exception handling was implemented.

But if you've read the article you'd also know that it can not be guaranteed that the vectored handler of the application is always the last, because code in a DLL might add a new handler at the tail of the handler list.

The reason why AddVectoredExceptionHandler was used on Win64 is the following:
The problem was that SetUnhandledExceptionFilter in a DLL overwrites the SetUnhandledExceptionFilter in the EXE. AddVectoredExceptionHandler doesn't do this but only exists from Windows XP on. The Win64 variant of Windows (x86_64 not ia64) also exists since Windows XP. Thus it was save to use this construct in the Win64 RTL, while on i386 you'd need to check the Windows version.

That this doesn't solve all problems is another story. The best solution in my opinion would be to implement real SEH in FPC, thus switching from a target independant exception handling to a target dependant exception handling (at least on systems that provide operating system exceptions aka all Windows platforms). But this solution also means a lot of work (I've already experimented on i386) especially on Win64 and WinCE as there the exception handlers are registered inside sections in the binary instead of the stack as on i386.

Regards,
Sven

Bernd Kreuss

2010-09-01 11:56

reporter   ~0040700

Why is it considered to implement SEH in FPC as solution to these two simple bugs? If each of them can be fixed by a simple 5 line workaround that does nothing other than bringing the messed up order of handlers in the correct order which must be:

1) every module's own handler for it's own exceptions
2) the handler of the .exe to handle everything that still remains

and these simple workarounds completely fix all these bugs without even touching the inner workings of FPC stack unwinding, why then the need to completely rewrite all that?

> "The problem was that SetUnhandledExceptionFilter in a DLL overwrites the SetUnhandledExceptionFilter in the EXE" <-- does this really happen in real world examples? Where there bug reports about this? And even if there were problems with foreign DLLs *uninstalling* the existing ExceptionHandler why was this considered an FPC problem? IMHO this would be a bug in the DLL. The dll, even if it wanted to use SetUnhandledExceptionFilter(), would have to use it the *correct* way and call the old handler from within in its newly installed handler. SetUnhandledExceptionFilter() will return the previously installed handler and so they can be easily chained and every DLL that choses to use this API will do it the documented way and if not it is clearly not an FPC bug.

The easiest solution IMHO would be to leave everything as it is (don't break a running system) and only make sure that it is not accidentally eating exceptions that are none of its business before the DLL even had a chance to see them.

Nobody wants to handle exceptions that happen in other modules, these two bugs are about desperately trying to catch exceptions where they happen and not letting them escape the module but either FPC installs a handler that is stealing all exceptions and disables all DLL handlers (bug 0017280) or when making a DLL it won't install any handler at all (this bug).

Fpr bug 0017280 I vote for continuing to use SetUnhandledExceptionFilter() on all windows platforms as it was all the time and not using AddVectoredExceptionHandler(). Putting a handler for the .exe (which should be the last one) in front of all others and trying to handle the C++ exceptions instead of letting the DLL do this is an error. The .exe needs to do its handling *after* all other handlers and this automatically forbids any usage of AddVectoredExceptionHandler().

And for this bug here (FPC .dll, other .exe) I vote for (properly) installing a handler when the dll is loaded that will check the ExceptioAddress and only cares about its own exceptions and calls the old handler if it finds itself not responsible.

> "The best solution in my opinion would be to implement real SEH in FPC," <-- the solution to what? There is no problem with how it works, it already works. It was just forgotten to install a handler when making a DLL (this bug) and the handler of an exe is in the wrong place in the chain and stealing the exceptions that don't belong to it (bug 0017280). None of these two bugs needs any change in the exception handling or even implementing SEH.

Additionally I vote for implementing some public RTL functions or compiler directives that let the user control on a per-module basis whether the handler should be installed or not (and where) and whether to handle all exceptions or whether it should handle only handle those that happened in the same module.

Sven Barth

2010-09-01 13:49

manager   ~0040703

Implementing SEH is considered by no one besides me (and I'm not a FPC dev).

Without using SEH you have only two solutions to handle Windows exceptions (also including exception raised by MS C/C++, Borland C++ and Delphi code):
1) SetUnhandledExceptionFilter
2) AddVectoredExceptionHandler

You asked whether there is an example regarding the SetUnhandledExceptionFilter and the overwriting of it in a DLL. I don't have an example about where this happens, but I have an example why it wasn't done in the first place: FPC's Win32 RTL.

In FPC Win32 DLLs the unhandled exception filter isn't set. Thus if you load this DLL in e.g. a C++ or Delphi application and you get an AV in your code this exception WON'T be handled by your DLLs try...except blocks, because the unhandled exception filter isn't executed. And C++ and Delphi don't use SetUnhandledExceptionFilter, because they correctly implement stack based SEH.

But I may have found a solution to this problem: when calling SetUnhandledExceptionFilter we should save the returned handler (if one was installed it isn't Nil) and call that if we don't know what to do with an exception (or if we're able to detect that the exception didn't happen inside our module boundaries).

Before the addition of AddVectoredExceptionHandler to the Win64 RTL you also weren't able to catch exceptions in a DLL that were not raised with "raise", but e.g. by the OS (like AVs). But I agree that AddVectoredExceptionHandler might have been a bad idea, because it's the first one (after the debugger) that can handle an exception and only after that the normal SEH mechanism kicks in. So the same solution as above might be applied (the same might be done on WinCE).

But there is a problem with SetUnhandledExceptionFilter: if someone (may be in a DLL) installs his/her own handler and doesn't call the returned handler, we're doomed, because now FPC won't be notified of AVs and such anymore. This is a good reason to implement SEH, because SEH is "enforced" by the compiler or the runtime, while the correct implementation of an exception filter in an user's code can't be forced.
Also I need SEH support in my NativeNT port to handle exceptions in kernel mode, because I can't simply use SetUnhandledExceptionFilter there (and unhandled kernel mode exceptions end in a bluescreen).

Regards,
Sven

Bernd Engelhardt

2010-09-01 16:08

reporter   ~0040714

Using AddVectoredExceptionHandler with "0" makes no difference.

As described in http://msdn.microsoft.com/en-us/magazine/cc301714.aspx the AddVectoredExceptionHandler is called before SEH:

>How does vectored exception handling coexist with structured exception
>handling? When an exception occurs in Windows XP, the vectored exception
>handler list is processed before the normal SEH list.

I think, it's the same on Win7x64. And in "syswin64_x86_64_exception_handler" the code

if ((excep^.ExceptionRecord^.ExceptionCode and SEVERITY_ERROR) = SEVERITY_ERROR) then
err := 217;

is excecuted and in the following code the result is set to

res := EXCEPTION_CONTINUE_EXECUTION

So, and exception is thrown in the EXE and the DLL gets no chance to hanle the original exception with try...catch.

Using "AddVectoredExceptionHandler" on Win64 is really bad ... the only solution would be to rewrite the exception handling.

Sven Barth

2010-09-01 16:37

manager   ~0040715

The difference with "1" and "0" is only visible if multiple handlers are added with AddVectoredExceptionHandler. The new handler is either added to the end or the head of the current handler list depending on the argument. But of course it is not guaranteed that your handler will be the last or the first one called, because someone else might add another handler before or after you.

And SEH is always executed after the vectored handlers. The parameter only influences the order IN the list of vectored handlers.

And yes, using AddVectoredExceptionHandler as a replacement for SetUnhandledExceptionFilter was a bad idea in my opinion as well.

Regards,
Sven

Bernd Engelhardt

2010-09-01 18:16

reporter   ~0040717

Last edited: 2010-09-01 18:31

@Sven Barth: I tried "0" and "1" because Bernd Kreuss asked me to do so.

For me (0017280) the problem is till the order (on Win64). I have an external DLL with try...catch and the FPC.exe catches the exception because it uses " AddVectoredExceptionHandler". No chance for the DLL to catch the excpetion. And the second problem for me is, that the routine "syswin64_x86_64_exception_handler" throws the exception as 217 (Control-C hit) error.

I think, we have to collect information about exception handling on Win64 and implement a "clean" solution.

The SEH handling is different on Win64 but it seems implented a little bit easier. There is also a function "RtlAddFunctionTable()" to implement it at runtime.

http://factor-language.blogspot.com/2010/04/frame-based-structured-exception.html
http://www.osronline.com/article.cfm?article=469

Programming against the x64 exception handling support
http://www.nynaeve.net/?p=99
http://www.nynaeve.net/?p=101
http://www.nynaeve.net/?p=105
http://www.nynaeve.net/?p=106
http://www.nynaeve.net/?p=107
http://www.nynaeve.net/?p=110
http://www.nynaeve.net/?p=113

Maybe some other programers with "exception handling" experience read this thread and can contribute some useful information.

Bernd Kreuss

2010-09-01 20:10

reporter   ~0040718

Last edited: 2010-09-01 20:49

> "when calling SetUnhandledExceptionFilter we should save the returned handler (if one was installed it isn't Nil) and call that [...]" <-- exactly. This is how they can (should) be chained. The only problem is that one cannot remove a handler from in the middle of such a chain.

For DLLs made with FPC there is the problem that the DLL does not know where the .exe will have its handler installed, the .exe might be friendly like win32 FPC and use SetUnhandledExceptionHandler() so the dll can easily chain its own handler before it (save the old pointer and call it) without disturbing it or the .exe might use SEH (or even mega-unfriendly and use the vectored handler) in which case the dll must either do SEH itself or even use the vectored handler itself.

I think the real world problem is that programmers must write DLLs for all kinds of applications and different scenarios and sometimes they simply have no control over what handlers are registered by the .exe or where a foreign DLL wishes to do their handling. There is no universal solution but I think there could be a default that comes pretty close and at least minimizes the chances of problems:

For an .exe always use the handler with the lowest available precedence and chain it at the very end and for a dll at least *try* to register the handler with the highest precedence but build a filter into the handler to strictly make sure it will only handle exceptions that happened in the dll itself. And additionally provide some ways to tweak the handler registration to be able to adapt ones applications/dlls to possible real-world problems and make it easier to work around the problems without having to patch the RTL each time.

Curently the DLL handler problem (this bug) can be easily worked around by using one of the workarounds posted here which simply install an additional handler but the other bug (0017280) Bernd Engelhardt's problem is a real practical real-world problem. He has some closed source Lotus Domino API dll and how could he now remove or tame the early and overzealous FPC handler in his application at runtime to allow this closed source C++ DLL to catch its things without having to actually patch the RTL?

@Bernd E.: For your system.pp patch: Maybe it is better if you don't entirely disable the offending ExceptionCode by simply always returning continue_search, you should instead make it (at the beginning of the handler) look at ExceptionAddress and then determine from that whether it happened in the .exe or in another module.

I used this:

  function GetModuleByAddr(addr: pointer): THandle;
  var
    Tmm: TMemoryBasicInformation;
  begin
    if VirtualQuery(addr, @Tmm, SizeOf(Tmm)) <> sizeof(Tmm)
      then Result:=0
      else Result:= THandle(Tmm.AllocationBase);
  end;

for determining the module handle of an address on win32 (I don't know whether it works the same way on win64 but there must be a similar easy way).

Bernd Engelhardt

2010-09-02 14:41

reporter   ~0040735

@Bernd Kreuss: I have posted a solution to 0017280 based on your code. I have adapted the code to Win64. What do you think about it?

Bernd Kreuss

2010-09-02 22:16

reporter   ~0040757

Last edited: 2010-09-02 22:27

looks good. It contains a goto but since you needed a label for obtaining some other address┬╣ in the same module anyways then why not use it ;-)

┬╣)Maybe there is also some API function to directly get the handle of the main module so you would not need the label at all and make it an ordinary "if then begin end" but I don't have my windows PC available for the next two days.

The disadvantage is that it will now also ignore any unhandled exceptions in DLLs but as a workaround for your application it should be perfect. For an official patch I vote for using SetUnhandledExceptionFilter() without address filtering (like on win32) on all windows platforms instead of AddVectoredExceptionHandler() with address filtering until the real solution with SEH is implemented.

Seth Grover

2011-10-20 16:57

reporter   ~0053210

> The disadvantage is that it will now also ignore any unhandled exceptions
> in DLLs but as a workaround for your application it should be perfect. For
> an official patch I vote for using SetUnhandledExceptionFilter() without
> address filtering (like on win32) on all windows platforms instead of
> AddVectoredExceptionHandler() with address filtering until the real
> solution with SEH is implemented.

I know this is a year after your note, but I've recently been playing with Win64 for the first time and I wholeheartedly agree with your suggestion. For Win32 I have rolled my own address filtering solution which I have working fine with SetUnhandledExceptionFilter (and, similarly in 32-bit and 64-bit Linux, using sigaction). The AddVectoredExceptionHandler issue has thrown wrench in the whole mix for Win64. At least with SetUnhandledExceptionFilter I knew how to get the old handler so that I could save it and call it myself if needed.

Seth Grover

2011-10-21 15:05

reporter   ~0053240

Well after messing around with it for a while, I think I've finally got a solution based on Bernd's note (#0040683) that seems to work for Win64 using AddVectoredExceptionHandler. Someday a built-in solution using SEH would be very nice, but until then at least I have something that works. Bernd, thank you for that example.

Reinier Olislagers

2012-05-01 08:06

developer   ~0059156

Bump. Any news on this? Still happens with r21148

Noticed this commit:
r21110 | sergei | 2012-04-29 09:35:19 +0200 (zo, 29 apr 2012) | 1 line

* Save/restore high-level code generator when processing exception filters, fixes building in Win64 with -dTEST_WIN64_SEH, Mantis 0021879.

... would testing with -dTEST_WIN64_SEH be useful?

Sergei Gorelkin

2012-05-01 08:57

developer   ~0059160

Well yes, building compiler with -dTEST_WIN64_SEH enables platform-specific exception handling on win64.
It is known to work only internal assembling/linking, though.

obones

2012-05-24 17:22

reporter   ~0059911

For those interested and unable to use the latest nightly build, here is the Win32/Win64 version that I wrote and that can handle a bit more than just access violations.
Simply put this in a file called SystemExceptionHandling.pas and use that file as the first one in the project file for your DLL project.

Hope this helps

=================================================================
unit SystemExceptionHandling;

interface

implementation

uses
  Windows, SysUtils;

// Inspired by what is described at FPC's forums:
// http://bugs.freepascal.org/view.php?id=12974
//
// especially in comment 0040683 by Bernd Kreuss

procedure UnwindFloatingPointException;
{$IFDEF WIN32}
asm
  fnclex
  emms
end;
{$ENDIF WIN32}
{$IFDEF WIN64}
var
  ControlWord: DWORD;
asm
  stmxcsr ControlWord
  and ControlWord, $FFE0
  ldmxcsr ControlWord
end;
{$ENDIF WIN64}

function ExceptionHandler(Info: PEXCEPTION_POINTERS): LongInt; stdcall;

  function GetModuleBaseByAddr(Addr: Pointer): Pointer;
  var
    Tmm: TMemoryBasicInformation;
  begin
    if VirtualQuery(addr, @Tmm, SizeOf(Tmm)) <> SizeOf(Tmm) then
      Result := nil
    else
      Result := Tmm.AllocationBase;
  end;
  
  procedure SetContinueAddress(Addr: Pointer);
  begin
    // clear the windows exception and point it to a place that
    // will raise a pascal exception instead.
    {$IFDEF WIN32}
    Info^.ContextRecord^.Eip :=
    {$ENDIF WIN32}
    {$IFDEF WIN64}
    Info^.ContextRecord^.Rip :=
    {$ENDIF WIN64}
      {$HINTS OFF}
      PtrUInt(Addr);
      {$HINTS ON}
  end;

label
  lblRaiseAccessViolation;
label
  lblRaiseFloatDivideByZero;
label
  lblRaiseFloatInvalidOperation;
label
  lblRaiseFloatOverflow;
label
  lblRaiseFloatUnderflow;
label
  lblRaiseIntDivideByZero;
label
  lblRaiseIntOverflow;

begin
  // did it happen in the same module where this function resides?
  if GetModuleBaseByAddr(Info^.ExceptionRecord^.ExceptionAddress) = GetModuleBaseByAddr(@lblRaiseAccessViolation) then
  begin
    UnwindFloatingPointException;
    // we only care about some exceptions
    case Info^.ExceptionRecord^.ExceptionCode of
      STATUS_ACCESS_VIOLATION:
        SetContinueAddress(@lblRaiseAccessViolation);
      EXCEPTION_FLT_DIVIDE_BY_ZERO:
        SetContinueAddress(@lblRaiseFloatDivideByZero);
      EXCEPTION_FLT_INVALID_OPERATION:
        SetContinueAddress(@lblRaiseFloatInvalidOperation);
      EXCEPTION_FLT_OVERFLOW:
        SetContinueAddress(@lblRaiseFloatOverflow);
      EXCEPTION_FLT_UNDERFLOW:
        SetContinueAddress(@lblRaiseFloatUnderflow);
      EXCEPTION_INT_DIVIDE_BY_ZERO:
        SetContinueAddress(@lblRaiseIntDivideByZero);
      EXCEPTION_INT_OVERFLOW:
        SetContinueAddress(@lblRaiseIntOverflow);
      else
        Exit(EXCEPTION_CONTINUE_SEARCH);
    end;
    
    Exit(EXCEPTION_CONTINUE_EXECUTION);
  end
  else
  begin
    Exit(EXCEPTION_CONTINUE_SEARCH);
  end;

  // the following will never be reached during this function call,
  // instead it will be jumped to and executed *after* this function has
  // returned and windows restarts execution at the new position of eip/rip.
lblRaiseAccessViolation:
  raise EAccessViolation.Create('access violation');
lblRaiseFloatDivideByZero:
  UnwindFloatingPointException;
  raise EZeroDivide.Create('Divide by zero');
lblRaiseFloatInvalidOperation:
  UnwindFloatingPointException;
  raise EInvalidOp.Create('Invalid operation');
lblRaiseFloatOverflow:
  UnwindFloatingPointException;
  raise EOverflow.Create('Float overflow');
lblRaiseFloatUnderflow:
  UnwindFloatingPointException;
  raise EUnderflow.Create('Float underflow');
lblRaiseIntDivideByZero:
  raise EDivByZero.Create('Divide by zero');
lblRaiseIntOverflow:
  raise EIntOverflow.Create('Integer overflow');
end;

function AddVectoredExceptionHandler(FirstHandler: DWORD; VectoredHandler: pointer): pointer; stdcall; external 'kernel32.dll' name 'AddVectoredExceptionHandler';
function RemoveVectoredExceptionHandler(VectoredHandlerHandle: pointer): ULONG; stdcall; external 'kernel32.dll' name 'RemoveVectoredExceptionHandler';

const
  CALL_FIRST = 1;

var
  ExceptionHandle: Pointer;

initialization
  ExceptionHandle := AddVectoredExceptionHandler(CALL_FIRST, @ExceptionHandler);

finalization
  RemoveVectoredExceptionHandler(ExceptionHandle);

end.

Marco van de Voort

2013-03-08 19:35

manager   ~0066125

Last edited: 2013-03-08 19:35

View 2 revisions

SEH is now default for win64 in 2.7.x

Bart Broersma

2013-04-04 01:00

reporter   ~0066795

@Vincent: same as 0012742, so fixed in fpc trunk?

Sven Barth

2013-11-19 21:39

manager   ~0071402

Last edited: 2013-11-19 21:40

View 2 revisions

Just in case: the problems with exception handling might be solved on Win64, but they still exist in case of Win32 and WinCE.
And if we'd implement arm-win32 or arm-winrt support somewhen in the future we'd definitely have the same problems as ARM Windows (both WinRT and WinCE) uses a similar exception handling mechanism as x86_64 Windows.

Edit: and it would also be very useful for kernel mode of NativeNT on all three platforms :) (despite ARM and x86_64 not being supported yet...)

Regards,
Sven

Do-wan Kim

2013-11-21 10:46

reporter  

fpc_w32_dll_error_solved.zip (59,718 bytes)

Do-wan Kim

2013-11-21 10:47

reporter   ~0071467

Win32 Per thread SEH solves problem.
See uploaded sample.

Do-wan Kim

2013-11-24 11:42

reporter   ~0071530

New patch for fpc 2.7.1 win32 SEH uploaded 0025363 ;D

Max Nazhalov

2016-08-22 14:05

reporter   ~0094256

Some fresh clarifications from the "other side" ;)
http://blogs.msdn.microsoft.com/oldnewthing/20160817-00/?p=94106

Is there any chance that TEST_WIN32_SEH will be default in the future?

(see also: http://bugs.freepascal.org/view.php?id=28756)

Issue History

Date Modified Username Field Change
2009-01-13 11:47 Carlo Kok New Issue
2009-01-13 11:48 Marco van de Voort Tag Attached: dynamic library
2009-01-13 11:48 Marco van de Voort Tag Attached: Exception
2009-01-13 13:16 Florian Relationship added related to 0004605
2009-01-13 13:20 Florian Note Added: 0024407
2009-01-13 14:20 Carlo Kok Note Added: 0024414
2009-01-13 14:30 Carlo Kok Note Edited: 0024414
2009-01-13 14:44 Florian Note Added: 0024416
2009-01-15 18:06 Jonas Maebe Relationship replaced has duplicate 0004605
2009-01-15 18:07 Jonas Maebe Relationship added has duplicate 0010421
2009-01-28 09:19 AlexRayne Note Added: 0024812
2009-01-28 09:20 AlexRayne File Added: tbsystemexception.pas
2009-02-06 00:53 Eric Heijnen Note Added: 0025151
2009-02-18 10:57 Alex Belyakov Note Added: 0025543
2009-02-19 06:04 Alex Belyakov Note Added: 0025569
2009-10-06 15:59 Jonas Maebe Relationship added related to 0014731
2010-02-01 03:46 Eric Heijnen Note Added: 0034120
2010-02-27 19:35 Marco van de Voort Tag Attached: SEH
2010-03-15 15:18 Seth Grover Note Added: 0035561
2010-03-17 02:16 Eric Heijnen File Added: exceptiondll.rar
2010-03-17 02:19 Eric Heijnen Note Added: 0035682
2010-03-17 02:26 Eric Heijnen Note Edited: 0035682
2010-03-17 02:32 Eric Heijnen Note Edited: 0035682
2010-03-19 06:09 Alexander Belyakov Note Added: 0035788
2010-03-19 06:10 Alexander Belyakov File Added: x64ExceptionTrap.pas
2010-03-19 06:11 Alexander Belyakov Note Edited: 0035788
2010-03-22 15:35 Seth Grover Note Added: 0035915
2010-03-24 11:07 Bernd Engelhardt Note Added: 0036018
2010-03-30 07:03 Alexander Belyakov Note Added: 0036257
2010-08-26 17:02 Jonas Maebe Relationship added has duplicate 0017280
2010-08-28 22:01 Bernd Kreuss Note Added: 0040573
2010-08-29 14:50 Sven Barth Note Added: 0040590
2010-08-29 15:54 Bernd Kreuss Note Added: 0040592
2010-08-29 16:23 Bernd Kreuss Note Added: 0040593
2010-08-29 16:26 Sven Barth Note Added: 0040594
2010-08-29 20:34 Bernd Kreuss Note Added: 0040611
2010-08-30 10:52 Bernd Engelhardt Note Added: 0040626
2010-08-30 12:41 Bernd Kreuss Note Added: 0040633
2010-08-30 12:56 Bernd Kreuss Note Edited: 0040633
2010-08-30 13:00 Bernd Kreuss Note Edited: 0040633
2010-08-30 14:12 Bernd Engelhardt Note Added: 0040645
2010-08-30 14:12 Bernd Engelhardt Note Edited: 0040645
2010-08-30 14:14 Bernd Engelhardt Note Edited: 0040645
2010-08-30 15:20 Bernd Kreuss Note Added: 0040649
2010-08-30 15:25 Bernd Kreuss Note Edited: 0040649
2010-08-30 15:37 Bernd Kreuss Note Edited: 0040649
2010-08-30 15:44 Bernd Kreuss Note Edited: 0040649
2010-08-30 15:51 Bernd Engelhardt Note Added: 0040653
2010-08-30 17:05 Sven Barth Note Added: 0040657
2010-08-30 19:09 Bernd Kreuss Note Added: 0040660
2010-08-30 19:31 Bernd Kreuss Note Added: 0040662
2010-08-30 19:47 Bernd Kreuss Note Edited: 0040660
2010-08-31 15:53 Bernd Kreuss Note Added: 0040681
2010-08-31 16:29 Bernd Kreuss Note Added: 0040683
2010-08-31 16:44 Bernd Kreuss Note Edited: 0040683
2010-08-31 16:45 Bernd Kreuss Note Edited: 0040683
2010-08-31 17:52 Bernd Engelhardt Note Added: 0040688
2010-08-31 18:29 Bernd Kreuss Note Added: 0040689
2010-08-31 18:37 Bernd Kreuss Note Edited: 0040689
2010-08-31 18:53 Bernd Kreuss Note Edited: 0040689
2010-08-31 18:58 Bernd Kreuss Note Edited: 0040689
2010-08-31 19:15 Bernd Engelhardt Note Added: 0040690
2010-08-31 20:56 Bernd Kreuss Note Added: 0040692
2010-08-31 21:03 Bernd Kreuss Note Edited: 0040692
2010-08-31 21:13 Bernd Kreuss Note Edited: 0040692
2010-08-31 21:17 Bernd Kreuss Note Added: 0040693
2010-08-31 21:44 Bernd Kreuss Note Added: 0040695
2010-08-31 21:48 Bernd Kreuss Note Edited: 0040695
2010-09-01 09:37 Sven Barth Note Added: 0040697
2010-09-01 11:56 Bernd Kreuss Note Added: 0040700
2010-09-01 13:49 Sven Barth Note Added: 0040703
2010-09-01 16:08 Bernd Engelhardt Note Added: 0040714
2010-09-01 16:37 Sven Barth Note Added: 0040715
2010-09-01 18:16 Bernd Engelhardt Note Added: 0040717
2010-09-01 18:21 Bernd Engelhardt Note Edited: 0040717
2010-09-01 18:30 Bernd Engelhardt Note Edited: 0040717
2010-09-01 18:31 Bernd Engelhardt Note Edited: 0040717
2010-09-01 20:10 Bernd Kreuss Note Added: 0040718
2010-09-01 20:21 Bernd Kreuss Note Edited: 0040718
2010-09-01 20:21 Bernd Kreuss Note Edited: 0040718
2010-09-01 20:23 Bernd Kreuss Note Edited: 0040718
2010-09-01 20:24 Bernd Kreuss Note Edited: 0040718
2010-09-01 20:37 Bernd Kreuss Note Edited: 0040718
2010-09-01 20:49 Bernd Kreuss Note Edited: 0040718
2010-09-02 13:07 Jonas Maebe Relationship replaced related to 0017280
2010-09-02 14:41 Bernd Engelhardt Note Added: 0040735
2010-09-02 22:16 Bernd Kreuss Note Added: 0040757
2010-09-02 22:26 Bernd Kreuss Note Edited: 0040757
2010-09-02 22:27 Bernd Kreuss Note Edited: 0040757
2011-10-20 16:57 Seth Grover Note Added: 0053210
2011-10-21 15:05 Seth Grover Note Added: 0053240
2012-05-01 08:06 Reinier Olislagers Note Added: 0059156
2012-05-01 08:57 Sergei Gorelkin Note Added: 0059160
2012-05-24 17:22 obones Note Added: 0059911
2013-03-06 17:04 Reinier Olislagers Relationship added related to 0023449
2013-03-07 18:15 Reinier Olislagers Relationship added related to 0024012
2013-03-08 19:35 Marco van de Voort Note Added: 0066125
2013-03-08 19:35 Marco van de Voort Note Edited: 0066125 View Revisions
2013-04-04 04:34 Bart Broersma Note Added: 0066795
2013-09-29 07:04 Jesus Reyes Relationship added related to 0023026
2013-11-19 21:36 Sven Barth Relationship added related to 0025312
2013-11-19 21:39 Sven Barth Note Added: 0071402
2013-11-19 21:40 Sven Barth Note Edited: 0071402 View Revisions
2013-11-21 10:46 Do-wan Kim File Added: fpc_w32_dll_error_solved.zip
2013-11-21 10:47 Do-wan Kim Note Added: 0071467
2013-11-24 11:42 Do-wan Kim Note Added: 0071530
2013-11-24 13:51 Jonas Maebe Relationship added related to 0025363
2015-09-28 16:42 Jonas Maebe Relationship added related to 0024908
2016-08-22 14:05 Max Nazhalov Note Added: 0094256
2018-07-07 12:45 Marco van de Voort Relationship added related to 0028756