AccessViolationException with GLFW in C#
Asked Answered
T

1

7

I have a problem with the glfwSetCharCallback function. Whenever I call it, the glfwPollEvents throws an AccessViolationException saying: "Attempted to read or write protected memory. This is often an indication that other memory is corrupt."

I have created a very simple wrapper just to demonstrate the problem:

using System;
using System.Runtime.InteropServices;
using System.Security;

namespace Test
{
    public delegate void GlfwCharCallback(GlfwWindow window, Char character);

    [StructLayout(LayoutKind.Explicit)]
    public struct GlfwMonitor
    {
        private GlfwMonitor(IntPtr ptr)
        {
            _nativePtr = ptr;
        }

        [FieldOffset(0)] private readonly IntPtr _nativePtr;

        public static readonly GlfwMonitor Null = new GlfwMonitor(IntPtr.Zero);
    }

    [StructLayout(LayoutKind.Explicit)]
    public struct GlfwWindow
    {
        private GlfwWindow(IntPtr ptr)
        {
            _nativePtr = ptr;
        }

        [FieldOffset(0)] private readonly IntPtr _nativePtr;

        public static GlfwWindow Null = new GlfwWindow(IntPtr.Zero);
    }

    public class Wrap
    {
        [DllImport("GLFW3", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
        private static extern Int32 glfwInit();

        [DllImport("GLFW3", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
        internal static extern GlfwWindow glfwCreateWindow(Int32 width, Int32 height,
                                                           [MarshalAs(UnmanagedType.LPStr)] String title,
                                                           GlfwMonitor monitor, GlfwWindow share);

        [DllImport("GLFW3", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
        internal static extern void glfwSetCharCallback(GlfwWindow window, GlfwCharCallback callback);

        [DllImport("GLFW3", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
        internal static extern void glfwPollEvents();

        public static Boolean Init()
        {
            return glfwInit() == 1;
        }

        public static GlfwWindow CreateWindow(Int32 width, Int32 height, String title, GlfwMonitor monitor,
                                              GlfwWindow share)
        {
            return glfwCreateWindow(width, height, title, monitor, share);
        }

        public static void SetCharCallback(GlfwWindow window, GlfwCharCallback callback)
        {
            glfwSetCharCallback(window, callback);
        }

        public static void PollEvents()
        {
            glfwPollEvents();
        }
    }
}

And I call it like I would GLFW:

using System;

namespace Test
{
    class Program
    {
        static void Main()
        {
            Wrap.Init();
            var window = Wrap.CreateWindow(800, 600, "None", GlfwMonitor.Null, GlfwWindow.Null);
            Wrap.SetCharCallback(window, (glfwWindow, character) => Console.WriteLine(character));

            while(true)
            {
                Wrap.PollEvents();
            }
        }
    }
}

The character is printed into the console and I get the AccessViolationException.

What am I doing incorrectly? All the DllImports specify the CDecl calling convention (I've tried the others on both PollEvents and SetCharCallback), I've tried all CharSets on the SetCharCallback function and nothing worked.

Could someone please help me?

Edit:

Here is the GLFW3 Dll that I'm using: http://www.mediafire.com/?n4uc2bdiwdzddda

Edit 2:

Using the newest "lib-msvc110" DLL made glfwSetCharCallback work. glfwSetKeyCallback and glfwSetCursorPosCallback do not work and continue to throw AccessViolationException. I will attempt to figure out what makes CharCallback so special, but honestly, I've gone through the GLFW source code through and through and all the functions seem to do their thing in the same way. Maybe there's something I'm overlooking.

Edit 3:

I've tried everything, cdecl, stdcall, all the charsets, all versions of the dll, all combinations of arguments, etc. I've made sure that the callbacks are not disposed by storing a reference to them. I've also tried to purposefully crash the application by not reserving any space of arguments of glfwSetCharCallback (which works as of now) and I haven't managed to do it. This leads me to believe that the arguments themselves have no bearing on the application.

The thing that really makes me think the fault is with GLFW itself is the fact that if I compile against the x86-64 dll, everything works perfectly. It is a little bit strange to use cdecl on x86-64, because MSDN specifically states that the only calling convention is fastcall.

Terrence answered 31/7, 2013 at 12:6 Comment(3)
I've uploaded the Dll that I'm using, just in case someone needs to try compiling the code to see what's wrong.Terrence
For what it's worth I ran your example code and it worked fine for me. Does it work for you? I don't know if it's related but something you could look into is making sure any unmanaged resources aren't being prematurely cleaned up by the GC.Quibbling
No, it doesn't work for me. It crashed. Also, I wrote "I've made sure that the callbacks are not disposed by storing a reference to them". Anyway, I think I'm on the right track to solving this, but first I need to test all the methods to be sure.Terrence
T
3

It took me a while, but I solved it. All the delegates are describing C# functions, which cannot be called from C. The solution is to make the delegates describe C-like functions, which can be achieved via the UnmanagedFunctionPointer attribute.

Adding the attribute to all delegates (as shown below) solved the problem.

[UnmanagedFunctionPointer(CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
public delegate void GlfwCharCallback(GlfwWindow window, Char character);
Terrence answered 12/8, 2013 at 8:26 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.