Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Int-Pointer to unmanaged code

I'm pretty new to C# and have a simple(?) question. From unmanaged code I receive an int-pointer:

public foo(ref IntPtr state)
{
   _myState = state;
}

_myState is a IntPtr member of the class. Now I want to exchange states via _myState with the unmanaged C++ code. Everything works if I write this:

public foo(ref IntPtr state)
{
   _myState = state;
   ....do some stuff
   state = 7;
}

In the unmanaged application I can see the new value 7. But if I write this:

public foo(ref IntPtr state)
{
   _myState = state;
   ...do some stuff
   _myState = 7;
}

then nothing happens. The initial value of state is 0, and when changing myState to 7 it is not updated in the unmanaged application. How can I assign a member variable like _myState to the state parameter as a "pointer", so when state is updated, _myState is also updated? In C++ this would be no problem with a pointer...

Ok, here is the real code:

[DllExport("QFX_InitializeInterfaceObject", CallingConvention = CallingConvention.StdCall)]
    public static void QFX_InitializeInterfaceObject(
        long windowHandle,
        ref IntPtr processState)
    {
        ChartWindowHandle = (IntPtr)windowHandle;
        OrderCommandProcessState = processState;
    }

All I want is that OrderCommandProcessState gets the same reference as processState to its value.

like image 455
Juergen Avatar asked Dec 16 '22 09:12

Juergen


1 Answers

First off, I want to make sure this point is clear: an IntPtr is just an integer that happens to be the same size as a native pointer on that machine architecture -- it's a 64 bit integer on x64 systems, for example. It does not necessarily have the semantics of a pointer, though of course it is common for interop code to stuff pointers into IntPtrs as a way of marshalling them around safely.

Moving on to your specific question, let's ignore the fact that it's an IntPtr. Pretend it's just an int, because that's basically what it is:

public void Foo(ref int x) // x is an alias to another variable of type int.
{
    int y = x; // y is a copy of the contents of x
    y = 7; // The contents of y are no longer a copy of the contents of x
}

Changing y does not in any way change x; x is an alias to a different variable, and y briefly has a copy of the contents of that variable. It is not in any way an alias to that same variable.

How can I make one variable into an alias to another variable, so when the state of the one variable is updated, the linked variable is also updated? In C++ this would be no problem with a pointer.

Today in the safe subset you can only do so via "ref" and "out" parameters to methods. The "ref" parameter becomes an alias to the given variable. That is the only safe way that you can directly make one variable into an alias for another.

The CLR supports ref locals as well. We could implement such a feature, and in fact I have prototyped it in C#. In my prototype you could say:

public void Foo(ref int x) // x is an alias to another variable of type int.
{
    ref int y = ref x; // y is now an alias to the same variable that x aliases!
    y = 7; // changing y now changes x, and whatever x 
           // aliases, because those are all the same variable
}

But we have not added this feature to C# and have no plans to do so any time soon. If you have a compelling usage case for it, I'd love to hear it. (UPDATE: The feature was added to C# 7.)

(The CLR also permits "ref" return types. However, the CLR does NOT permit making an alias to a variable and then storing that alias in a field! The field might be of longer lifetime than the linked variable, and the CLR designers wish to avoid that whole class of bugs that plagues C and C++.)

If you know that the variable is pinned to a particular location in memory then you can turn off the safety system and make a pointer to that variable; you then have a perfectly ordinary pointer that you can use as you would in C++. (That is, if a pointer ptr refers to a variable then *ptr is an alias to that variable.)

unsafe public void Foo(int* x) // *x is an alias to a variable of type int.
{
    int* y = x; // *y is now an alias to the same variable that *x aliases
    *y = 7; // changing *y now changes *x, and whatever *x 
            // aliases, because those are all the same variable
}

The CLR puts no restrictions on how pointers can be used; you are free to store them in fields if you want. However, if you turn the safety system off then you are responsible for ensuring that the garbage collector (or whatever memory manager owns that storage -- it might not be managed memory) is not going to change the location of the aliased variable for the lifetime of the pointer. Do not turn off that safety system unless you really know what you are doing; that safety system is there to protect you.

like image 80
Eric Lippert Avatar answered Jan 02 '23 22:01

Eric Lippert