简体   繁体   English

使用C#获取OpenGL版本的最简单方法

[英]Easiest way to get the OpenGL Version with C#

I use an external C++ DLL which useses OpenGL. 我使用外部C ++ DLL使用OpenGL。 But the external DLL chrashes when there is no OpenGL on the running system. 但是当正在运行的系统上没有OpenGL时,外部DLL会崩溃。 I tried to catch the exception from the dll in my C# code, but it seems like I can not catch it, because the exception is raised in the c++ dll. 我试图在我的C#代码中从dll中捕获异常,但似乎我无法捕获它,因为在c ++ dll中引发了异常。

So my next idea is it to implement a simple function that checks, which version of OpenGL is installed. 所以我的下一个想法是实现一个简单的函数来检查,安装了哪个版本的OpenGL。

I have absolute no idea of OpenGL and how it works, but someone told me I need to create a context to get the version of it. 我绝对不知道OpenGL及其工作原理,但有人告诉我,我需要创建一个上下文来获取它的版本。 Is there a simple way to implement a function that does that in C#? 有没有一种简单的方法来实现在C#中执行此操作的函数? I do not want to import a heavy dll like OpenTK just to get the version. 我不想导入像OpenTK这样的重型dll来获取版本。

Can I directly call the opengl32.dll and create a context to get the version? 我可以直接调用opengl32.dll并创建一个上下文来获取版本吗?

eg 例如

[DllImport("opengl32.dll")]
public static extern string glGetString(string glVersion);

I know that this snippet won't work, but what does it need to make it work? 我知道这个片段不起作用,但它需要什么才能使它工作? How can I create such a context, because if that won't work I might already know that there is no OpenGL and my problem would be solved if I can catch that. 我怎样才能创建这样的上下文,因为如果那不起作用,我可能已经知道没有OpenGL,如果我能抓住它,我的问题就会解决。

As you mentioned: 如你所说:

To be specific the c++ dll is actually a C# wrapped version of a C++ dll. 具体来说,c ++ dll实际上是C ++ dll的C#包装版本。 So I reference it as it would be a normal C# dll. 所以我引用它,因为它将是一个普通的C#dll。

You're in luck that referenced assemblies are not loaded until they are called upon for the first time (because otherwise the complexity of the answer would greatly increase in my knowledge). 你很幸运,在第一次调用引用的程序集之前不会加载它们(因为否则,我知道答案的复杂性会大大增加)。 This way you can check for the OpenGL version before that wrapper is actually loaded (and possibly crash, due to no OpenGL present). 这样,您可以在实际加载该包装之前检查OpenGL版本(并且可能由于没有OpenGL存在而崩溃)。

My suggestion is you use the information described in this question to determine where OpenGL is installed. 我的建议是您使用此问题中描述的信息来确定OpenGL的安装位置。

Then you can refer this question on how to get the version. 然后你可以参考这个问题来获取版本。

That C++ code roughly translates to the following C# code: 该C ++代码大致转换为以下C#代码:

internal static class Imports
{
    [DllImport("kernel32.dll", CallingConvention = CallingConvention.Winapi, SetLastError = true, CharSet = CharSet.Ansi)]
    public static extern IntPtr LoadLibrary([MarshalAs(UnmanagedType.LPStr)]string lpFileName);

    [DllImport("kernel32.dll", CallingConvention = CallingConvention.Winapi, SetLastError = true)]
    [return: MarshalAs(UnmanagedType.Bool)]
    public static extern bool FreeLibrary(IntPtr hModule);

    [DllImport("user32.dll", CallingConvention = CallingConvention.Winapi)]
    public static extern IntPtr GetDC(IntPtr hWnd);

    [DllImport("kernel32.dll", CallingConvention = CallingConvention.Winapi, CharSet = CharSet.Ansi, ExactSpelling = true, SetLastError = true)]
    public static extern IntPtr GetProcAddress(IntPtr hModule, [MarshalAs(UnmanagedType.LPStr)]string procName);
}

internal sealed class UnmanagedLibrary : IDisposable
{
    private bool disposed = false;

    public UnmanagedLibrary(string path)
    {
        Handle = Imports.LoadLibrary(path);
        if (Handle == IntPtr.Zero)
        {
            throw new Exception($"Failed to load library \"{path}\" ({Marshal.GetLastWin32Error()}).");
        }
    }

    ~UnmanagedLibrary()
    {
        Dispose(false);
    }

    public void Dispose()
    { 
        Dispose(true);

        GC.SuppressFinalize(this);           
    }

    private void Dispose(bool disposing)
    {
        if (!disposed)
        {
            Imports.FreeLibrary(Handle);

            disposed = true;
        }
    }

    public IntPtr Handle
    {
        get;
        private set;
    }
}

internal static class OpenGLHelper
{
    [StructLayout(LayoutKind.Explicit)]
    private struct PIXELFORMATDESCRIPTOR 
    {
        [FieldOffset(0)]
        public UInt16 nSize;
        [FieldOffset(2)]
        public UInt16 nVersion;
        [FieldOffset(4)]
        public UInt32 dwFlags;
        [FieldOffset(8)]
        public Byte iPixelType;
        [FieldOffset(9)]
        public Byte cColorBits;
        [FieldOffset(10)]
        public Byte cRedBits;
        [FieldOffset(11)]
        public Byte cRedShift;
        [FieldOffset(12)]
        public Byte cGreenBits;
        [FieldOffset(13)]
        public Byte cGreenShift;
        [FieldOffset(14)]
        public Byte cBlueBits;
        [FieldOffset(15)]
        public Byte cBlueShift;
        [FieldOffset(16)]
        public Byte cAlphaBits;
        [FieldOffset(17)]
        public Byte cAlphaShift;
        [FieldOffset(18)]
        public Byte cAccumBits;
        [FieldOffset(19)]
        public Byte cAccumRedBits;
        [FieldOffset(20)]
        public Byte cAccumGreenBits;
        [FieldOffset(21)]
        public Byte cAccumBlueBits;
        [FieldOffset(22)]
        public Byte cAccumAlphaBits;
        [FieldOffset(23)]
        public Byte cDepthBits;
        [FieldOffset(24)]
        public Byte cStencilBits;
        [FieldOffset(25)]
        public Byte cAuxBuffers;
        [FieldOffset(26)]
        public SByte iLayerType;
        [FieldOffset(27)]
        public Byte bReserved;
        [FieldOffset(28)]
        public UInt32 dwLayerMask;
        [FieldOffset(32)]
        public UInt32 dwVisibleMask;
        [FieldOffset(36)]
        public UInt32 dwDamageMask;
    }

    private const byte PFD_TYPE_RGBA = 0;

    private const sbyte PFD_MAIN_PLANE = 0;

    private const uint PFD_DOUBLEBUFFER = 1;
    private const uint PFD_DRAW_TO_WINDOW = 4;
    private const uint PFD_SUPPORT_OPENGL = 32;

    private const int GL_VERSION = 0x1F02;

    [UnmanagedFunctionPointer(CallingConvention.Winapi)]
    private delegate int ChoosePixelFormatDelegate(IntPtr hdc, IntPtr ppfd);

    [UnmanagedFunctionPointer(CallingConvention.Winapi)]
    private delegate int SetPixelFormatDelegate(IntPtr hdc, int format, IntPtr ppfd);

    [UnmanagedFunctionPointer(CallingConvention.Winapi)]
    private delegate IntPtr wglCreateContextDelegate(IntPtr arg1);

    [UnmanagedFunctionPointer(CallingConvention.Winapi)]
    private delegate int wglDeleteContextDelegate(IntPtr arg1);

    [UnmanagedFunctionPointer(CallingConvention.Winapi)]
    private delegate int wglMakeCurrentDelegate(IntPtr arg1, IntPtr arg2);

    [UnmanagedFunctionPointer(CallingConvention.Winapi)]
    private delegate IntPtr glGetStringDelegate(int name);

    public static string GetVersion()
    {
        using (UnmanagedLibrary openGLLib = new UnmanagedLibrary("opengl32.dll"))
        using (UnmanagedLibrary gdi32Lib = new UnmanagedLibrary("Gdi32.dll"))
        {
            IntPtr deviceContextHandle = Imports.GetDC(Process.GetCurrentProcess().MainWindowHandle);
            if (deviceContextHandle == IntPtr.Zero)
            {
                throw new Exception("Failed to get device context from the main window.");
            }

            IntPtr choosePixelFormatAddress = Imports.GetProcAddress(gdi32Lib.Handle, "ChoosePixelFormat");
            if (choosePixelFormatAddress == IntPtr.Zero)
            {
                throw new Exception($"Failed to get ChoosePixelFormat address ({Marshal.GetLastWin32Error()}).");
            }

            ChoosePixelFormatDelegate choosePixelFormat = Marshal.GetDelegateForFunctionPointer<ChoosePixelFormatDelegate>(choosePixelFormatAddress);

            PIXELFORMATDESCRIPTOR pfd = new PIXELFORMATDESCRIPTOR
            {
                nSize = (UInt16)Marshal.SizeOf(typeof(PIXELFORMATDESCRIPTOR)),
                nVersion = 1,
                dwFlags = (PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER),
                iPixelType = PFD_TYPE_RGBA,
                cColorBits = 32,
                cRedBits = 0,
                cRedShift = 0,
                cGreenBits = 0,
                cGreenShift = 0,
                cBlueBits = 0,
                cBlueShift = 0,
                cAlphaBits = 0,
                cAlphaShift = 0,
                cAccumBits = 0,
                cAccumRedBits = 0,
                cAccumGreenBits = 0,
                cAccumBlueBits = 0,
                cAccumAlphaBits = 0,
                cDepthBits = 24,
                cStencilBits = 8,
                cAuxBuffers = 0,
                iLayerType = PFD_MAIN_PLANE,
                bReserved = 0,
                dwLayerMask = 0,
                dwVisibleMask = 0,
                dwDamageMask = 0
            };

            IntPtr pfdPtr = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(PIXELFORMATDESCRIPTOR)));
            try
            {
                Marshal.StructureToPtr(pfd, pfdPtr, false);

                int pixelFormat = choosePixelFormat(deviceContextHandle, pfdPtr);
                if (pixelFormat == 0)
                {
                    throw new Exception($"Failed to choose pixel format ({Marshal.GetLastWin32Error()}).");
                }

                IntPtr setPixelFormatAddress = Imports.GetProcAddress(gdi32Lib.Handle, "SetPixelFormat");
                if (setPixelFormatAddress == IntPtr.Zero)
                {
                    throw new Exception($"Failed to get SetPixelFormat address ({Marshal.GetLastWin32Error()}).");
                }

                SetPixelFormatDelegate setPixelFormat = Marshal.GetDelegateForFunctionPointer<SetPixelFormatDelegate>(setPixelFormatAddress);
                if (setPixelFormat(deviceContextHandle, pixelFormat, pfdPtr) <= 0)
                {
                    throw new Exception($"Failed to set pixel format ({Marshal.GetLastWin32Error()}).");
                }

                IntPtr wglCreateContextAddress = Imports.GetProcAddress(openGLLib.Handle, "wglCreateContext");
                if (wglCreateContextAddress == IntPtr.Zero)
                {
                    throw new Exception($"Failed to get wglCreateContext address ({Marshal.GetLastWin32Error()}).");
                }

                wglCreateContextDelegate wglCreateContext = Marshal.GetDelegateForFunctionPointer<wglCreateContextDelegate>(wglCreateContextAddress);

                IntPtr wglDeleteContextAddress = Imports.GetProcAddress(openGLLib.Handle, "wglDeleteContext");
                if (wglDeleteContextAddress == IntPtr.Zero)
                {
                    throw new Exception($"Failed to get wglDeleteContext address ({Marshal.GetLastWin32Error()}).");
                }

                wglDeleteContextDelegate wglDeleteContext = Marshal.GetDelegateForFunctionPointer<wglDeleteContextDelegate>(wglDeleteContextAddress);

                IntPtr openGLRenderingContext = wglCreateContext(deviceContextHandle);
                if (openGLRenderingContext == IntPtr.Zero)
                {
                    throw new Exception($"Failed to create OpenGL rendering context ({Marshal.GetLastWin32Error()}).");
                }

                try
                {
                    IntPtr wglMakeCurrentAddress = Imports.GetProcAddress(openGLLib.Handle, "wglMakeCurrent");
                    if (wglMakeCurrentAddress == IntPtr.Zero)
                    {
                        throw new Exception($"Failed to get wglMakeCurrent address ({Marshal.GetLastWin32Error()}).");
                    }

                    wglMakeCurrentDelegate wglMakeCurrent = Marshal.GetDelegateForFunctionPointer<wglMakeCurrentDelegate>(wglMakeCurrentAddress);
                    if (wglMakeCurrent(deviceContextHandle, openGLRenderingContext) <= 0)
                    {
                        throw new Exception($"Failed to make current device context ({Marshal.GetLastWin32Error()}).");
                    }

                    IntPtr glGetStringAddress = Imports.GetProcAddress(openGLLib.Handle, "glGetString");
                    if (glGetStringAddress == IntPtr.Zero)
                    {
                        throw new Exception($"Failed to get glGetString address ({Marshal.GetLastWin32Error()}).");
                    }

                    glGetStringDelegate glGetString = Marshal.GetDelegateForFunctionPointer<glGetStringDelegate>(glGetStringAddress);
                    IntPtr versionStrPtr = glGetString(GL_VERSION);
                    if (versionStrPtr == IntPtr.Zero)
                    {
                        // I don't think this ever goes wrong, in the context of OP's question and considering the current code.
                        throw new Exception("Failed to get OpenGL version string.");
                    }

                    return Marshal.PtrToStringAnsi(versionStrPtr);
                }
                finally
                {
                    wglDeleteContext(openGLRenderingContext);
                }
            }
            finally
            {
                Marshal.FreeHGlobal(pfdPtr);
            }
        }
    }
}

You can then get the version string via: 然后您可以通过以下方式获取版本字符串

OpenGLHelper.GetVersion()

Whichs gives me the following output on my machine: 哪个在我的机器上给我以下输出:

4.5.0 - Build 22.20.16.4836

From MSDN : 来自MSDN

The GL_VERSION string begins with a version number. GL_VERSION字符串以版本号开头。 The version number uses one of these forms: 版本号使用以下形式之一:

major_number.minor_number major_number.minor_number

major_number.minor_number.release_number major_number.minor_number.release_number

The important part is, is that you do this check before you actually call anything from the wrapper DLL. 重要的是,在从包装器DLL实际调用任何内容之前,请执行此检查。

The way this code works is that it dynamically retrieves the function addresses in opengl32.dll and Gdi32.dll . 此代码的工作方式是它动态检索opengl32.dllGdi32.dll的函数地址。 It dynamically loads the two mentioned libraries and retrieves the function addresses we need to call. 它动态加载两个提到的库并检索我们需要调用的函数地址。 This differs from DllImport as it imports from an already loaded library. 这与DllImport不同,因为它从已加载的库中导入。 So in a sense we're doing exactly the same as DllImport , except for manually loading/unloading the unmanaged libraries. 所以从某种意义上说,除了手动加载/卸载非托管库之外,我们与DllImport完全相同。 I suggest you search for the function names on MSDN, which clearly explains what each function does and returns. 我建议你在MSDN上搜索函数名,这清楚地解释了每个函数的作用和返回。

CAVEAT: This code assumes your application has a window associated with it. CAVEAT:此代码假定您的应用程序有一个与之关联的窗口。 Although GetDC is valid with a null pointer (which should return the device context for the entire screen according to MSDN ), I don't know if that will always work. 虽然GetDCnull指针有效(根据MSDN应返回整个屏幕的设备上下文),但我不知道这是否总是有效。

NOTE: You should also implement your own Exception instead of throwing the base one. 注意:您还应该实现自己的Exception而不是抛出基础Exception

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM