Microsoft's documentation on .NET native interoperability gives the most common example (even calls it that, actually) of a call from C# to unmanaged code:
using System.Runtime.InteropServices;
public class Program
{
// Import user32.dll (containing the function we need) and define
// the method corresponding to the native function.
[DllImport("user32.dll")]
public static extern int MessageBox(IntPtr hWnd, String text, String caption, int options);
public static void Main(string[] args)
{
// Invoke the function as a regular managed method.
MessageBox(IntPtr.Zero, "Command-line message box", "Attention!", 0);
}
}
That seems pretty straightforward and does work fine. But I'm puzzled about the second and third parameters of the MessageBox
call. If MessageBox
were a managed method, those would be string
reference types, with their actual data residing on the heap. The garbage collector might rearrange the heap, but (being a managed function) that wouldn't have any effect that MessageBox
could see, as the reference would always be to the current location, no matter where that was. But the unmanaged version expects a pointer containing the actual memory address itself of the string.
Now, maybe my question reflects my experience with Java (and relative lack of experience with C#), but I'm wondering why the above call to the unmanaged function MessageBox
isn't at risk of the garbage collector moving the actual data in the string from one location on the heap to another, after MessageBox
is called, and before MessageBox
dereferences the pointer.
In other words, how can we be sure the address passed to MessageBox
continues to point to the actual string data until after it returns?