7

我对 glfwSetCharCallback 函数有疑问。每当我调用它时,glfwPollEvents 都会抛出一个 AccessViolationException 说:“试图读取或写入受保护的内存。这通常表明其他内存已损坏。”

我创建了一个非常简单的包装器来演示这个问题:

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();
        }
    }
}

我把它称为 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();
            }
        }
    }
}

该字符被打印到控制台中,我得到了 AccessViolationException。

我做错了什么?所有的 DllImports 都指定了 CDecl 调用约定(我在 PollEvents 和 SetCharCallback 上都尝试过其他的),我在 SetCharCallback 函数上尝试了所有的 CharSet,但没有任何效果。

有人可以帮我吗?

编辑:

这是我正在使用的 GLFW3 Dll: http ://www.mediafire.com/?n4uc2bdiwdzddda

编辑2:

使用最新的“lib-msvc110”DLL 使 glfwSetCharCallback 工作。glfwSetKeyCallback 和 glfwSetCursorPosCallback 不起作用并继续抛出 AccessViolationException。我将尝试弄清楚是什么让 CharCallback 如此特别,但老实说,我已经一遍又一遍地浏览了 GLFW 源代码,并且所有函数似乎都以相同的方式完成它们的工作。也许我忽略了一些东西。

编辑3:

我已经尝试了所有内容,cdecl、stdcall、所有字符集、所有版本的 dll、所有参数组合等。我确保不会通过存储对回调的引用来处理回调。我还尝试通过不保留 glfwSetCharCallback 的任何参数空间(目前有效)来故意使应用程序崩溃,但我没有设法做到这一点。这使我相信这些论点本身与申请无关。

真正让我认为问题出在 GLFW 本身的事实是,如果我针对 x86-64 dll 进行编译,一切都会完美运行。在 x86-64 上使用 cdecl 有点奇怪,因为 MSDN 明确指出唯一的调用约定是 fastcall。

4

1 回答 1

3

我花了一段时间,但我解决了它。所有的delegate都在描述C#函数,不能从C中调用。解决的办法是让delegate描述类C的函数,可以通过UnmanagedFunctionPointerattribute来实现。

将属性添加到所有代表(如下所示)解决了这个问题。

[UnmanagedFunctionPointer(CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
public delegate void GlfwCharCallback(GlfwWindow window, Char character);
于 2013-08-12T08:26:56.963 回答