Posted by: Christian Wimmer in: JEDI Windows API Headers
In my last article “Jwscl and FreeAndNil” there were some great comments on the source design. Oliver told me to use a guarded memory page so the memory is always invalid. With his information I wrote a source code that creates a pointer which always triggers an access violation.
Unfortunately, Access Violations are rather common and thus nobody can tell why it happens at a first glance. So I added an exception handler that maps this special access violation to a separate exception class (I called it EJwsclAccessedGuardPointerException).
uses ExceptionLog, //JwaWindows, //or JwaWinBase, JwaWinNT, JwaNtStatus, SysUtils; type EJwsclAccessedGuardPointerException = class(Exception) public ExceptionRecord: PExceptionRecord; end; resourcestring SGuardPageException = 'It was tried to access a guarded pointer. This '+ 'pointer was freed previously and is now accessed without initialization. '+ 'JWSCL may guard its class members in this way. You should check for '+ ' accesses to e.g. class members which instance was freed.'#13#10+ 'Exception was raised at this address: $%p'; var GuardPtr : Pointer; OldExcpObj : function (P: PExceptionRecord): Exception; OldExitProc : procedure; procedure MyExitProcessProc; begin VirtualFree(GuardPtr, 0, MEM_RELEASE); if @OldExitProc <> nil then OldExitProc; end; function MyGetExceptionObject(P: PExceptionRecord): Exception; var ErrorCode : Integer; begin if (P.ExceptionCode = STATUS_ACCESS_VIOLATION) and (P.ExceptObject = GuardPtr) then begin result := EJwsclAccessedGuardPointerException.CreateFmt(SGuardPageException, [P.ExceptionAddress]); EJwsclAccessedGuardPointerException(Result).ExceptionRecord := P; end else begin result := OldExcpObj(P); end; end; var T : TObject; SysInfo : TSystemInfo; begin OldExcpObj := ExceptObjProc; ExceptObjProc := @MyGetExceptionObject; OldExitProc := ExitProcessProc; ExitProcessProc := MyExitProcessProc; //On 32Bit OS call //GetSystemInfo(@SysInfo); //On WOW64Bit OS call (use IsWow64Process) GetNativeSystemInfo(@SysInfo); GuardPtr := VirtualAlloc( Pointer($BADC0DE),// __in_opt LPVOID lpAddress, SysInfo.dwPageSize,// __in SIZE_T dwSize, MEM_RESERVE,// __in DWORD flAllocationType, PAGE_NOACCESS// __in DWORD flProtect ); if GuardPtr = nil then RaiseLastOSError; T := TObject(Pointer(GuardPtr)); T.Free; end.
Since the object has this guard pointer value (which may be dynamic) a call to Free (or Destroy) will fail with the exception EJwsclAccessedGuardPointerException. Be aware that there is no actual memory allocated. It is all about virtual memory.
The MyExitProcessProc is called right before the process exits. There is another exit handler called ExitProc that is called before the units are finalized. So I’m stuck with ExitProcessProc which is fine though.
I must say that I like this approach since it seems to be safer than just using a nearly arbitrary magic value. The value is still there so you can see it as an invalid memory address in a debugger. However, Assigned or similar workarounds will not work because the memory location can vary.
And of course the exception message clearly states what happened here. There could added more information like stack trace but that I leave to you (or the JCL).
Be aware that there still might be errors. This code is hot.
What is your opinion?