Search code examples
c#dllimportunsafefixed

Does "fixed" really guarantee anything when passing pointers (ie int[]) to DLLs?


I tried searching for this but haven't found anything, however when passing an int[] into a native DLL function as a pointer, isn't there still the danger that the DLL could maintain a reference to the pointer, and then try to access it again after the "fixed" block has terminated? Wouldn't this cause memory access errors if the GC has moved your array? If so, how do you get around this? Or is this an unlikely scenario?


Solution

  • Here's a class I made to fix the problem. It creates a memory space in unmanaged using AllocHGlobal, and then wraps a pointer to that space. Unfortunately making this generic doesn't seem to work, as there's no way that I can find to cast from void* to T in the this[int i] method.

    
        unsafe sealed class FixedFloatArray : IDisposable {
            readonly float* _floats;
    
            internal FixedFloatArray(int size) {
                _floats = (float*)Marshal.AllocHGlobal(sizeof(float) * size);
            }
    
            internal FixedFloatArray(float[] floats) {
                _floats = (float*)Marshal.AllocHGlobal(sizeof(float) * floats.Length);
                Marshal.Copy(floats, 0, (IntPtr)_floats, floats.Length);
            }
    
            internal float this[int i] {
                get { return _floats[i]; }
                set { _floats[i] = value; }
            }
    
            internal void CopyFrom(float[] source, int sourceIndex, int destinationIndex, int length) {
                var memoryOffset = (int)_floats + destinationIndex * sizeof(float);
                Marshal.Copy(source, sourceIndex, (IntPtr)memoryOffset, length);
            }
    
            internal void CopyTo(int sourceIndex, float[] destination, int destinationIndex, int length) {
                var memoryOffset = (int)_floats + sourceIndex * sizeof(float);
                Marshal.Copy((IntPtr)memoryOffset, destination, destinationIndex, length);
            }
    
            public static implicit operator IntPtr(FixedFloatArray ffa) {
                return (IntPtr)ffa._floats;
            }
    
            public void Dispose() {
                Marshal.FreeHGlobal((IntPtr)_floats);
            }
        }