I would use the Execute Around Block pattern, which is superficially similar to RAII in c++. A simple example is what I use for ensuring that streams are closed after execution:
OpenAndRead[file_String, fcn_]:=
Module[{strm, res},
strm = OpenRead[file];
res = CheckAbort[ fcn[strm], $Aborted ];
Close[strm];
If[res === $Aborted, Abort[], res] (* Edited to allow Abort to propagate *)
]
Here, I use CheckAbort to prevent Abort from propagating out of my code until it I've closed the stream. It's use is simply,
fcn[ file_String, <otherparams> ] := OpenAndRead[file, fcn[#, <otherparams>]&]
fcn[ file_InputStream, <otherparams> ] := <fcn body>
But, that is inconvenient to use. In the previous answer, I suggested a facility for automatically generating that code. Since then, I've determined that is a poor strategy, and instead suggest the creation of a Block or Module type scoping construct:
SetAttributes[ReadBlock, HoldAll];
ReadBlock[filename_String, body_]:= ReadBlock[{filename, file}, body]
ReadBlock[{filename_String,file_Symbol}, body_] :=
OpenAndRead[filename, Function[{file}, body]]
Then, the definition of fcn is
fcn[ file_String, <otherparams> ] := ReadBlock[{file, strm}, <body]
For your case, I'd set it up like this
SetAttributes[ProtectedBlock, HoldAll];
ProtectedBlock[{openfcn_, closefcn_},body_]:=
ProtectedBlock[{obj, openfcn, closefcn}, body]
ProtectedBlock[{obj_Symbol, openfcn_, closefcn_}, body_]:=
Module[{res, obj},
obj = openfcn;
res = Check[ CheckAbort[ body, $Aborted ];closefcn , $Failed ];
If[
res === $Aborted, Abort[],
res
]
]
Obviously, this needs a bit of work to properly rethrow the messages suppressed by Check, but it should be along the lines of what you are looking for.
Edit: Here's the final form of ReadBlock which I've renamed as BlockStream.
ClearAll[BlockStream];
SetAttributes[BlockStream, HoldAll];
SyntaxInformation[BlockStream] = {"ArgumentsPattern"->{_, _, _.},
"LocalVariables" -> {"Solve", {1,1}}};
BlockStream[filename_, body_]:=
BlockStream[{file, filename}, OpenRead, body]
BlockStream[{file_Symbol, filename_}, body_]:=
BlockStream[{file, filename}, OpenRead, body]
BlockStream[filename_, op:(OpenRead | OpenWrite|OpenAppend), body_]:=
BlockStream[{file, filename}, op, body]
BlockStream[{file_Symbol, filename_}, op:(OpenRead | OpenWrite|OpenAppend), body_]:=
Block[{file = op[filename], res, f, $TagLess},
If[file ===$Failed, Return[$Failed]];
(*
Catch added in case there is an uncaught Throw which would bypass
the Close[strm]. But, Catch[expr, _, f] would not catch tagless Throw,
so made Throw have dummy tag, $TagLess,
which is stripped prior to rethrowing.
*)
res = Internal`InheritedBlock[{Throw},
Unprotect[Throw];
Throw[value_] := Throw[value, $TagLess];
Catch[CheckAbort[body, $Aborted], _, f]
];
Close[file];
Which[
res === $Aborted, Abort[],
Head@res === f, If[Last@res===$TagLess, res = f[First@res] ];
Block[{f = Throw}, res],
True, res
]
]
This works by executing body within an environment where the Stream is guaranteed to be closed regardless of what the code in body does. There are two main ways to use it, with or without specifying a symbol to associate with the stream. If a symbol is not passed in, via the BlockStream[{file, filename}, ...] syntax, the symbol file is set to the stream and is exposed to the code within body. A simple usage example is
BlockStream[{strm, FileNameJoin[{$TemporaryDirectory, "writefile"}]},
OpenWrite,
Write[strm, a^2, 1 + b^2]
]
where as you can see the symbol strm is accesible within the body of the function.
Note this converges in many ways with Leonid's answer, but it uses only a single Catch. To deal with a tagless Throw, I modified throws behavior to add the tag $TagLess whenever a tag isn't specified, and I deal with that later. This seems like the best way to ensure that whether it is tagged and tagless, Throw is always caught.