One method (for more detail, look up Controlled types
in an Ada book, or see section 7.4 of the Ada 95 Rationale):


with Finalization;

........

   type A is access .....;

   type Needs_Guaranteed_Heap_Cleanup is
      new Finalization.Controlled with
      record
         Pointer : A;
      end record;

....

   procedure Finalize (Item : Needs_Guaranteed_Heap_Cleanup) is
   -- automatically called when Item exits scope.
   -- client of Item cannot prevent it (or forget it).
   begin
      Free (Item.Pointer);
      Finalize (Controlled(Item));
   end Finalize;

   type File_That_Needs_Guaranteed_Closing is
      new Finalization.Controlled with
      record
         File : XXX_IO.File_Type;
      end record;

....

   procedure Finalize (Item : File_That_Needs_Guaranteed_Closing) is
   -- automatically called when Item exits scope.
   -- client of Item cannot prevent it (or forget it).
   begin
      if XXX_IO.Is_Open (Item.File) then
         XXX_IO.Close (Item.File);
      end if;
      Finalize (Controlled(Item));
   end Finalize;

Another method is described in an article on the Web.  URI recently posted
in comp.lang.ada but I don't remember it.