[Mono-dev] GetAddrOf mono-basic vb.net

Jonathan Gilbert 2a5gjx302 at sneakemail.com
Sat May 5 14:20:00 EDT 2007


At 05:00 PM 5/4/2007 +0200, Rolf Bjarne wrote:
>
>>It is in VB6.
>>Public Declare Function GetAddrOf Lib "kernel32" Alias "MulDiv" (nNumber As
>>Any, Optional ByVal nNumerator As Long = 1, Optional ByVal nDenominator As
>>Long = 1) As Long
>
>The VB.Net version should be something like
>Public Declare Function GetAddrOf Lib "kernel32" Alias "MulDiv" (ByVal
>nNumber As Integer, Optional ByVal Numerator As Integer = 1, Optional ByVal
>Denominator As Integer = 1) As Integer
>
>At least according to the declaration of MulDiv in MSDN.
>
>Now why do you rename a function called MulDiv to GetAddrOf?

What it is is an ingenious use of an API function to achieve something the
original author thought VB didn't have built-in. (The original author
should be directed to the help file pages for VarPtr, StrPtr and ObjPtr.)

Note that in the original VB6 declaration, the nNumber parameter is not
declared correctly as a ByVal Long, but rather is left "As Any". When the
parameter is "As Any", VB6 will pass it ByRef unless you explicitly ask it
not to in the call. This means the caller receives a pointer on the stack.
Note that the MulDiv API function expects the actual value on the stack.

What then happens if you, say, call GetAddrOf(my_local_variable), is that
the Optional parameters default to one, and you end up asking the OS to
take the memory address of my_local_variable, multiply it by 1, and then
divide it by 1. Obviously this returns simply the memory address, unchanged.

(Why is there an API function just to multiply and then divide a number? So
that programming languages that cannot easily work with 64-bit values can
scale a 32-bit value by a fraction where the intermediate value, multiplied
by the numerator but not yet divided, does not fit into 32 bits.)

In VB6, as I mentioned, there is a built-in function to do this: VarPtr (or
StrPtr if you want the string data of a String (which is just a BSTR in
VB6), or ObjPtr if you want a pointer to the interface you have selected
for a given object reference).

In VB.NET, the idiom is, for the most part, completely unneeded, especially
if you are hoping to write code that runs both on Windows and on other
platforms in Mono. The only situation I can think of where you would
directly need the memory address of data is where you have an unmanaged
structure which has a member that points at the data, a structure which you
would then pass into some system-specific API function.

I'm no VB.NET expert; in C#, you would simply write an unsafe code block
and use the address-of operator '&' to get your pointer (using 'fixed' for
arrays). Perhaps there is a better way, but if you *really* need to pass a
value by its memory address in VB.NET, all that comes to my mind would be
to use Marshal.AllocHGlobal, store the resulting IntPtr in the unmanaged
structure, and then use Marshal.WriteInt32 (or one of its friends) to fill
the data at the memory address you just allocated.

Of course, if all you need is to pass a local variable directly into an API
function ByRef, then instead of declaring that function with a ByVal memory
address, declare it with a ByRef parameter of the actual data type and
.NET's P/Invoke marshalling will automatically give the API a pointer.

I'd be academically interested to know if there's a better way in VB.NET,
but that just about sums up what this GetAddrOf is all about.

Jonathan Gilbert




More information about the Mono-devel-list mailing list