Peter said:
See below.
Personally, I would just step back out to the caller. The value is
either going to be copied into a variable, or passed to another method.
Either way, it'll wind up in some sort of named variable that you can
then modify. Things get a bit more complicated if the return value is
used in an expression, granted. Which bring me to...
It also doesn't work if you have no symbols for the caller. Actually, not
having source would probably be enough to make this unworkable. If debugging
the callers is an option (because you also own that code

), sure, go for
that, but it's not actually easier, just a possible workaround.
Admittedly I haven't explored advanced debugging techniques since moving
over to managed code, but given that the code that's executing is in
fact native x86, I think it's very likely there's a mode in the debugger
you can switch to that gives you access to the data at a lower level,
including register access. Maybe this is what you mean by "unmanaged"
debugging?
Actually, you *have* (read-only) register access even under managed
debugging (odd as this may seem), and EAX will actually contain the return
value when you break on the final brace of a function. However, unless your
function is returning a primitive type like int you will not be able to
inspect, let alone modify the value, because it's an address, and as far as
managed code is concerned, there's no such thing as an address to a managed
object.
You *should* be able to get it done when you turn on mixed-mode debugging
(where the debugger can debug managed and unmanaged code simultaneously).
This is what you get when you turn on the "enable unmanaged code debugging"
box in the project properties. You can now load SOS and use !DumpObject on
your address to get to your actual object (in a far less pleasing format
than what the debugger can give you, but still). Modifying it is another
chapter altogether.
However, mixed-mode debugging is slower and less flexible than pure managed
debugging (edit and continue ceases to work and there are some other more
esoteric side effects) so it should preferably be used only when you're
actually debugging an application that uses both managed and unmanaged
components. Even then I usually prefer WinDbg with SOS -- if you really need
to debug at this level, there's little point settling for less. It is not
what one would call convenient, though.
The moral of the story is that they really ought to have added a $return
pseudoregister/expression/whatever. Regular developers cannot be expected to
jump through these hoops just to have a foolproof way of accessing the
function's return value *before* you go off into some caller code. It's a
simple enough thing to want. We do get $exception for catch blocks that are
too lazy to declare a variable (and that should be *much* rarer than returns
without a variable), so why not?
In all my years of programming, I've yet to run into a situation that
required me to rewrite my code to accomodate whatever debugging I wanted
to do. Maybe those scenarios exist in the managed environment. But it
would surprise me if they do.
Oh, if you find out how to do this easily in managed mode (inspect and
modify the return value without rewriting the function or going up to the
callers), do let me know. I've not run into the issue often enough to really
try, but it's an actual issue.