2008-10-13 15 views
5

Estoy tratando de usar OpenGL en C#. He siguiente código que produce el error 2000 ERROR_INVALID_PIXEL_FORMAT
primeras definiciones:wglCreateContext en C# falla pero no en C++ administrado

[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true, ExactSpelling = true)] 
public static extern IntPtr GetDC(IntPtr hWnd); 

[StructLayout(LayoutKind.Sequential)] 
    public struct PIXELFORMATDESCRIPTOR 
    { 
     public void Init() 
     { 
      nSize = (ushort) Marshal.SizeOf(typeof (PIXELFORMATDESCRIPTOR)); 
      nVersion = 1; 
      dwFlags = PFD_FLAGS.PFD_DRAW_TO_WINDOW | PFD_FLAGS.PFD_SUPPORT_OPENGL | PFD_FLAGS.PFD_DOUBLEBUFFER | PFD_FLAGS.PFD_SUPPORT_COMPOSITION; 
      iPixelType = PFD_PIXEL_TYPE.PFD_TYPE_RGBA; 
      cColorBits = 24; 
      cRedBits = cRedShift = cGreenBits = cGreenShift = cBlueBits = cBlueShift = 0; 
      cAlphaBits = cAlphaShift = 0; 
      cAccumBits = cAccumRedBits = cAccumGreenBits = cAccumBlueBits = cAccumAlphaBits = 0; 
      cDepthBits = 32; 
      cStencilBits = cAuxBuffers = 0; 
      iLayerType = PFD_LAYER_TYPES.PFD_MAIN_PLANE; 
      bReserved = 0; 
      dwLayerMask = dwVisibleMask = dwDamageMask = 0; 
     } 
     ushort nSize; 
     ushort nVersion; 
     PFD_FLAGS dwFlags; 
     PFD_PIXEL_TYPE iPixelType; 
     byte cColorBits; 
     byte cRedBits; 
     byte cRedShift; 
     byte cGreenBits; 
     byte cGreenShift; 
     byte cBlueBits; 
     byte cBlueShift; 
     byte cAlphaBits; 
     byte cAlphaShift; 
     byte cAccumBits; 
     byte cAccumRedBits; 
     byte cAccumGreenBits; 
     byte cAccumBlueBits; 
     byte cAccumAlphaBits; 
     byte cDepthBits; 
     byte cStencilBits; 
     byte cAuxBuffers; 
     PFD_LAYER_TYPES iLayerType; 
     byte bReserved; 
     uint dwLayerMask; 
     uint dwVisibleMask; 
     uint dwDamageMask; 
    } 

    [Flags] 
    public enum PFD_FLAGS : uint 
    { 
     PFD_DOUBLEBUFFER = 0x00000001, 
     PFD_STEREO = 0x00000002, 
     PFD_DRAW_TO_WINDOW = 0x00000004, 
     PFD_DRAW_TO_BITMAP = 0x00000008, 
     PFD_SUPPORT_GDI = 0x00000010, 
     PFD_SUPPORT_OPENGL = 0x00000020, 
     PFD_GENERIC_FORMAT = 0x00000040, 
     PFD_NEED_PALETTE = 0x00000080, 
     PFD_NEED_SYSTEM_PALETTE = 0x00000100, 
     PFD_SWAP_EXCHANGE = 0x00000200, 
     PFD_SWAP_COPY = 0x00000400, 
     PFD_SWAP_LAYER_BUFFERS = 0x00000800, 
     PFD_GENERIC_ACCELERATED = 0x00001000, 
     PFD_SUPPORT_DIRECTDRAW = 0x00002000, 
     PFD_DIRECT3D_ACCELERATED = 0x00004000, 
     PFD_SUPPORT_COMPOSITION = 0x00008000, 
     PFD_DEPTH_DONTCARE = 0x20000000, 
     PFD_DOUBLEBUFFER_DONTCARE = 0x40000000, 
     PFD_STEREO_DONTCARE = 0x80000000 
    } 

    public enum PFD_LAYER_TYPES : byte 
    { 
     PFD_MAIN_PLANE = 0, 
     PFD_OVERLAY_PLANE = 1, 
     PFD_UNDERLAY_PLANE = 255 
    } 

    public enum PFD_PIXEL_TYPE : byte 
    { 
     PFD_TYPE_RGBA = 0, 
     PFD_TYPE_COLORINDEX = 1 
    } 

    [DllImport("gdi32.dll", CharSet = CharSet.Auto, SetLastError = true, ExactSpelling = true)] 
    public static extern int ChoosePixelFormat(IntPtr hdc, [In] ref PIXELFORMATDESCRIPTOR ppfd); 

    [DllImport("gdi32.dll", CharSet = CharSet.Auto, SetLastError = true, ExactSpelling = true)] 
    public static extern bool SetPixelFormat(IntPtr hdc, int iPixelFormat, ref PIXELFORMATDESCRIPTOR ppfd); 
[DllImport("opengl32.dll", CharSet = CharSet.Auto, SetLastError = true, ExactSpelling = true)] 
    public static extern IntPtr wglCreateContext(IntPtr hDC); 

y ahora el código que falla:

IntPtr dc = Win.GetDC(hwnd); 

var pixelformatdescriptor = new GL.PIXELFORMATDESCRIPTOR(); 
pixelformatdescriptor.Init(); 

var pixelFormat = GL.ChoosePixelFormat(dc, ref pixelformatdescriptor); 
if(!GL.SetPixelFormat(dc, pixelFormat, ref pixelformatdescriptor)) 
    throw new Win32Exception(Marshal.GetLastWin32Error()); 
IntPtr hglrc; 
if((hglrc = GL.wglCreateContext(dc)) == IntPtr.Zero) 
    throw new Win32Exception(Marshal.GetLastWin32Error()); //<----- here I have exception 

el mismo código en C++ administrada está trabajando

HDC dc = GetDC(hWnd); 

PIXELFORMATDESCRIPTOR pf; 
pf.nSize = sizeof(PIXELFORMATDESCRIPTOR); 
pf.nVersion = 1; 
pf.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER | PFD_SUPPORT_COMPOSITION; 
pf.cColorBits = 24; 
pf.cRedBits = pf.cRedShift = pf.cGreenBits = pf.cGreenShift = pf.cBlueBits = pf.cBlueShift = 0; 
pf.cAlphaBits = pf.cAlphaShift = 0; 
pf.cAccumBits = pf.cAccumRedBits = pf.cAccumGreenBits = pf.cAccumBlueBits = pf.cAccumAlphaBits = 0; 
pf.cDepthBits = 32; 
pf.cStencilBits = pf.cAuxBuffers = 0; 
pf.iLayerType = PFD_MAIN_PLANE; 
pf.bReserved = 0; 
pf.dwLayerMask = pf.dwVisibleMask = pf.dwDamageMask = 0; 

int ipf = ChoosePixelFormat(dc, &pf); 
SetPixelFormat(dc, ipf, &pf); 

HGLRC hglrc = wglCreateContext(dc); 

Lo he probado en VIsta de 64 bits con tarjeta gráfica ATI y en Windows XP de 32 bits con Nvidia con el mismo resultado en ambos casos.
También quiero mencionar que no quiero usar ningún marco ya escrito para ello.

¿Alguien puede mostrarme dónde está el error en el código C# que está causando la excepción?

Respuesta

14

Encontrado.
El problema es muy extraño feo y realmente difícil de encontrar. En algún lugar de Internet descubrí que cuando se está enlazando OpenGL32.lib mientras se compila la aplicación C++, se debe colocar antes de gdi32.lib. La razón para esto es que (supuestamente) opengl32.dll sobrescribe las funciones ChoosePixelFormat y SetPixelFormat (y probablemente más :-). Como encontré en mi versión de C++, accidentalmente fue el caso.
Je, pero cómo hacerlo en C#
Después de unos días de búsqueda me encontré con que en tao framework lo resolvieron mediante LoadLibrary kernel32.dll() y Opengl32.dll carga antes de llamar SetPixelFormat

public static bool SetPixelFormat(IntPtr deviceContext, int pixelFormat, ref PIXELFORMATDESCRIPTOR pixelFormatDescriptor) { 
     Kernel.LoadLibrary("opengl32.dll"); 
     return _SetPixelFormat(deviceContext, pixelFormat, ref pixelFormatDescriptor); 
    } 

Así sabemos que opengl32.dll se debe cargar antes de gdi32.dll, ¿hay alguna otra forma de hacerlo? Después de un tiempo, pensé que podíamos llamar a alguna función NOP de opengl32.dll para cargarla. Por ejemplo:

[DllImport("opengl32.dll", EntryPoint = "glGetString", CharSet = CharSet.Auto, SetLastError = true, ExactSpelling = true)] 
static extern IntPtr _glGetString(StringName name); 
public static string glGetString(StringName name) 
{ 
    return Marshal.PtrToStringAnsi(_glGetString(name)); 
} 
public enum StringName : uint 
{ 
    GL_VENDOR = 0x1F00, 
    GL_RENDERER = 0x1F01, 
    GL_VERSION = 0x1F02, 
    GL_EXTENSIONS = 0x1F03 
} 

y en el inicio de la aplicación, antes de cualquier llamada a Gdi32.dll utilizo este:

GL.glGetString(0); 

Ambas formas resuelve el problema.

0

No puedo probar esto ahora, pero mi primera sospecha sería la estructura del embalaje. ¿Has intentado configurar el empaque en 1 en el atributo StructLayout? Por ejemplo:

[StructLayout(LayoutKind.Sequential, Pack=1)] 

Cheers, solución Brian

0

Llamar a wglCreateContext dos veces ayuda también.

if (SetPixelFormat(DC, iPixelformat, ref pfd) == false) 
    throw new Win32Exception(Marshal.GetLastWin32Error()); 

RC = wglCreateContext(DC); 
if (RC == HGLRC.Zero) 
{ 
    if (SetPixelFormat(DC, iPixelformat, ref pfd) == false) 
     throw new Win32Exception(Marshal.GetLastWin32Error()); 
    RC = wglCreateContext(DC); 
    if (RC == HGLRC.Zero) 
     throw new Win32Exception(Marshal.GetLastWin32Error()); 
} 
Cuestiones relacionadas