2008-10-13 15 views
5

Próbuję użyć opengl w C#. Mam następujący kod, który nie powiedzie się z powodu błędu 2000 ERROR_INVALID_PIXEL_FORMAT
Pierwsze definicje:wglCreateContext w C#, ale nie w zarządzanym C++

[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); 

a teraz kod, który kończy się niepowodzeniem:

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 

ten sam kod w zarządzanym C++ pracuje

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

Próbowałem już na 64-bitowym komputerze VIsta z kartą graficzną ATI i 32-bitowym Windows XP z Nvidią z tym samym wynikiem w obu przypadkach.
Chciałbym także wspomnieć, że nie chcę używać do tego żadnych napisanych już ram.

Czy ktoś może mi pokazać, gdzie jest błąd w kodzie C#, który powoduje wyjątek?

Odpowiedz

14

znaleziono rozwiązanie.
Problem jest bardzo dziwny i bardzo trudny do znalezienia. Gdzieś w Internecie odkryłem, że kiedy łączysz opengl32.lib podczas kompilowania aplikacji C++, musisz go umieścić przed gdi32.lib. Powodem tego jest to, że (podobno) opengl32.dll nadpisuje funkcje ChoosePixelFormat i SetPixelFormat (i prawdopodobnie więcej :-). Jak znalazłem w mojej wersji C++, przypadkowo tak było.
Heh, ale jak to zrobić w C#
Po kilku dniach poszukiwań znalazłem, że w tao framework one rozwiązać go za pomocą KERNEL32.DLL LoadLibrary() i ładowanie opengl32.dll przed wywołaniem SetPixelFormat

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

Tak wiemy, że plik opengl32.dll musi być załadowany przed gdi32.dll, czy istnieje inny sposób robienia tego. Po chwili myślałem, że możemy wywołać funkcję NOP z pliku opengl32.dll, aby go załadować. Na przykład:

[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 
} 

i na początku stosowania, przed każdym wywołaniu Gdi32.dll Używam tego:

GL.glGetString(0); 

Oba sposoby rozwiązuje problem.

0

Nie mogę tego teraz przetestować, ale moim pierwszym podejrzeniem byłaby struktura opakowania. Czy próbowałeś ustawić pakowanie na 1 w atrybucie StructLayout? Na przykład:

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

Cheers, Brian

0

Dwukrotnie pomaga również wywoływanie wglCreateContext.

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()); 
} 
Powiązane problemy