0

The below code creates a window, loads a few OpenGL procs with wglGetProcAddress, and then draws a red square in the center of a green background. On my machine the program works fine, but on a friend's laptop it crashed in the call to xglDrawArrays() with a complaint that the function tried to dereference 0x00000000. Note that the xglDrawArrays pointer was itself non-NULL on my friend's machine, as I verified in a debugger. Let me stress again that the error wasn't due to xglDrawArrays being NULL, but rather due to xglDrawArrays accessing NULL data internally.

Now, the oddest thing is that when I changed "xglDrawArrays" to "glDrawArrays" on his machine the code suddenly worked. This makes me suspect that the error was due to some misuse of WGL.

The relevant vertex and fragment shaders are very simple:

/* Vertex Shader */
attribute vec2 pos;
void main() {
    gl_Position = vec4(pos.x, pos.y, 0, 1)
}

/* Fragment Shader */
void main() {
    gl_FragColor = vec4(1, 0, 0, 1)
}

The relevant code is here:

#include <windows.h>
#include <stdlib.h>
#include <conio.h>
#include <gl/gl.h>
#include "glext.h"

typedef void (__stdcall *PFNGLDRAWARRAYSPROC)(GLenum mode, GLint first, GLsizei count);

PFNGLCREATESHADERPROC            xglCreateShader;
PFNGLSHADERSOURCEPROC            xglShaderSource;
PFNGLCOMPILESHADERPROC           xglCompileShader;
PFNGLGETSHADERIVPROC             xglGetShaderiv;
PFNGLGETSHADERINFOLOGPROC        xglGetShaderInfoLog;
PFNGLCREATEPROGRAMPROC           xglCreateProgram;
PFNGLATTACHSHADERPROC            xglAttachShader;
PFNGLLINKPROGRAMPROC             xglLinkProgram;
PFNGLGETPROGRAMIVPROC            xglGetProgramiv;
PFNGLUSEPROGRAMPROC              xglUseProgram;
PFNGLGENBUFFERSPROC              xglGenBuffers;
PFNGLBUFFERDATAPROC              xglBufferData;
PFNGLVERTEXATTRIBPOINTERPROC     xglVertexAttribPointer;
PFNGLGETATTRIBLOCATIONPROC       xglGetAttribLocation;
PFNGLENABLEVERTEXATTRIBARRAYPROC xglEnableVertexAttribArray;
PFNGLDRAWARRAYSPROC              xglDrawArrays;
PFNGLBINDBUFFERPROC              xglBindBuffer;

void
LoadOpenGLProcs(void)
{
    xglCreateShader            = (void*)wglGetProcAddress("glCreateShader");
    xglShaderSource            = (void*)wglGetProcAddress("glShaderSource");
    xglCompileShader           = (void*)wglGetProcAddress("glCompileShader");
    xglGetShaderiv             = (void*)wglGetProcAddress("glGetShaderiv");
    xglGetShaderInfoLog        = (void*)wglGetProcAddress("glGetShaderInfoLog");
    xglCreateProgram           = (void*)wglGetProcAddress("glCreateProgram");
    xglAttachShader            = (void*)wglGetProcAddress("glAttachShader");
    xglLinkProgram             = (void*)wglGetProcAddress("glLinkProgram");
    xglGetProgramiv            = (void*)wglGetProcAddress("glGetProgramiv");
    xglUseProgram              = (void*)wglGetProcAddress("glUseProgram");
    xglGenBuffers              = (void*)wglGetProcAddress("glGenBuffers");
    xglBufferData              = (void*)wglGetProcAddress("glBufferData");
    xglVertexAttribPointer     = (void*)wglGetProcAddress("glVertexAttribPointer");
    xglGetAttribLocation       = (void*)wglGetProcAddress("glGetAttribLocation");
    xglEnableVertexAttribArray = (void*)wglGetProcAddress("glEnableVertexAttribArray");
    xglDrawArrays              = (void*)wglGetProcAddress("glDrawArrays");
    xglBindBuffer              = (void*)wglGetProcAddress("glBindBuffer");
}

HGLRC openglContext;
GLuint programID;
GLint posID;
GLuint bufferID;

char*
ReadEntireFile(const wchar_t* path)
{
    char* buff = malloc(1);
    size_t buffSize = 1;

    HANDLE fileHandle = CreateFile(
        path, GENERIC_READ, 0, NULL, OPEN_EXISTING, 0, NULL);

    if( fileHandle == INVALID_HANDLE_VALUE )
    {
        MessageBox(NULL, L"CreateFile failed", L"ERROR", MB_OK);
        exit(0);
    }

    while(1)
    {
        char ch;
        BOOL success;
        DWORD numBytesRead;

        success = ReadFile(fileHandle, &ch, 1, &numBytesRead, NULL);

        if( ! success ) {
            MessageBox(NULL, L"ReadFile failed", L"ERROR", MB_OK);
            exit(0);
        }

        if( numBytesRead == 0 )
            break;

        buff[buffSize-1] = ch;
        buffSize++;
        buff = realloc(buff, buffSize);
    }

    buff[buffSize-1] = 0;
    return buff;
}

void
DieUponGLError(void)
{
    if( glGetError() != GL_NO_ERROR )
    {
        MessageBox(NULL, L"glGetError returned an error", L"ERROR", MB_OK);
        exit(0);
    }
}

void
LoadShaders(void)
{
    const char* fileData;
    GLint compileStatus;
    GLint linkStatus;
    char errorString[1024];

    GLuint vertShaderID = xglCreateShader(GL_VERTEX_SHADER);
    GLuint fragShaderID = xglCreateShader(GL_FRAGMENT_SHADER);
    _cprintf("vert shader id: %d\n", vertShaderID);
    _cprintf("frag shader id: %d\n", fragShaderID);

    fileData = ReadEntireFile(
        L"C:\\Users\\myname\\Desktop\\simple.vs");
    xglShaderSource(vertShaderID, 1, &fileData, NULL);
    DieUponGLError();
    free((void*)fileData);

    fileData = ReadEntireFile(
        L"C:\\Users\\myname\\Desktop\\simple.fs");
    xglShaderSource(fragShaderID, 1, &fileData, NULL);
    DieUponGLError();
    free((void*)fileData);

    xglCompileShader(vertShaderID);
    xglGetShaderiv(vertShaderID, GL_COMPILE_STATUS, &compileStatus);
    if( compileStatus != GL_TRUE )
    {
        xglGetShaderInfoLog(vertShaderID, 1024, NULL, errorString);
        _cprintf("[SHADER COMPILE ERROR]\n");
        _cprintf("%s\n", errorString);
        MessageBox(
            NULL, L"glCompileShader(vertShaderID) failed", L"ERROR", MB_OK);
        exit(0);
    }

    xglCompileShader(fragShaderID);
    xglGetShaderiv(fragShaderID, GL_COMPILE_STATUS, &compileStatus);
    if( compileStatus != GL_TRUE )
    {
        MessageBox(
            NULL, L"glCompileShader(fragShaderID) failed", L"ERROR", MB_OK);
        exit(0);
    }

    _cprintf("vert shader compile status: %d\n", compileStatus);
    _cprintf("frag shader compile status: %d\n", compileStatus);

    programID = xglCreateProgram();
    xglAttachShader(programID, vertShaderID);
    xglAttachShader(programID, fragShaderID);

    xglLinkProgram(programID);
    xglGetProgramiv(programID, GL_LINK_STATUS, &linkStatus);
    if( linkStatus != GL_TRUE )
    {
        MessageBox(
            NULL, L"glLinkShader(programID) failed", L"ERROR", MB_OK);
        exit(0);
    }

    xglUseProgram(programID);
    DieUponGLError();
    _cprintf("Shader program successfully linked and installed\n");

    posID = xglGetAttribLocation(programID, "pos");
    if( posID == -1 )
    {
        MessageBox(
            NULL, L"glGetAttribLocation(\"pos\") failed", L"ERROR", MB_OK);
        exit(0);
    }
}

void
LoadGeometry(void)
{

    GLfloat squarePoints[] =
    {
        -0.5, +0.5,
        -0.5, -0.5,
        +0.5, +0.5,
        +0.5, -0.5
    };

    xglGenBuffers(1, &bufferID);

    xglBindBuffer(GL_ARRAY_BUFFER, bufferID);

    xglBufferData(
        GL_ARRAY_BUFFER, sizeof(squarePoints), squarePoints, GL_STATIC_DRAW);
}

void
DrawGeometry(void)
{
    xglBindBuffer(GL_ARRAY_BUFFER, bufferID);
    xglVertexAttribPointer(posID, 2, GL_FLOAT, GL_FALSE, 0, 0);
    xglEnableVertexAttribArray(posID);
    xglDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
}

double
TimeSec(void)
{
    LARGE_INTEGER value;

    static LARGE_INTEGER freq;
    static BOOL firstRun = TRUE;
    if( firstRun )
    {
        firstRun = FALSE;
        QueryPerformanceFrequency(&freq);
    }

    QueryPerformanceCounter(&value);
    return (double)value.QuadPart / (double)freq.QuadPart;
}

LRESULT CALLBACK MainWindowProc(
    HWND hwnd,
    UINT msg,
    WPARAM wparam,
    LPARAM lparam)
{
    if( msg == WM_DESTROY )
    {
        PostQuitMessage(0);
        return 0;
    }

    return DefWindowProc(hwnd, msg, wparam, lparam);
}

int
WINAPI
WinMain(
    HINSTANCE appInstance,
    HINSTANCE prevAppInstance,
    LPSTR lpCmdLine,
    int showCmd)
{
    MSG msg;
    HWND win;
    WNDCLASSEX cls;
    HDC dc;
    GLuint pixelFormatID;
    PIXELFORMATDESCRIPTOR pixelFormatDescriptor =
    {
        sizeof(PIXELFORMATDESCRIPTOR),
        1,
        PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER,
        PFD_TYPE_RGBA,
        32, // cColorBits
        0,  // 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
        24, // Size of the depth buffer (in bits)
        0,  // Size of stencil buffer (in bits)
        0,  // Number of aux buffers
        0,  // iLayerType (ignored)
        0,  // bReserved
        0,  // dwLayerMask (ignored)
        0,  // dwVisibleMask (0 means black)
        0   // dwDamageMask (ignored)
    };

    cls.cbSize        = sizeof(cls);
    cls.style         = CS_HREDRAW | CS_VREDRAW | CS_OWNDC;
    cls.lpfnWndProc   = MainWindowProc;
    cls.cbClsExtra    = 0;
    cls.cbWndExtra    = 0;
    cls.hInstance     = appInstance;
    cls.hIcon         = NULL;
    cls.hCursor       = LoadCursor(NULL, IDC_ARROW);
    cls.hbrBackground = GetStockObject(WHITE_BRUSH);
    cls.lpszMenuName  = L"MainMenu";
    cls.lpszClassName = L"MainWindowClass";
    cls.hIconSm       = NULL;

    if( RegisterClassEx(&cls) == 0 )
    {
        MessageBox(NULL, L"RegisterClassEx failed", L"ERROR", MB_OK);
        return 0;
    }

    win = CreateWindowEx(
        0,
        L"MainWindowClass",
        L"Main Window",
        WS_OVERLAPPEDWINDOW,
        CW_USEDEFAULT,
        CW_USEDEFAULT,
        500,
        500,
        NULL,
        NULL,
        appInstance,
        NULL);

    if( win == NULL )
    {
        MessageBox(NULL, L"CreateWindowEx failed", L"ERROR", MB_OK);
        return 0;
    }

    dc = GetDC(win);
    if( dc == NULL )
    {
        MessageBox(NULL, L"GetDC() failed", L"ERROR", MB_OK);
        return 0;
    }

    pixelFormatID = ChoosePixelFormat(dc, &pixelFormatDescriptor);

    if (pixelFormatID == 0 )
    {
        MessageBox(NULL, L"ChoosePixelFormat() failed", L"ERROR", MB_OK);
        return 0;
    }

    if( ! SetPixelFormat(dc, pixelFormatID, &pixelFormatDescriptor) )
    {
        MessageBox(NULL, L"SetPixelFormat", L"ERROR", MB_OK);
        return 0;
    }

    openglContext = wglCreateContext(dc);
    if( openglContext == NULL )
    {
        MessageBox(NULL, L"wglCreateContext() failed", L"ERROR", MB_OK);
        return 0;
    }

    if( ! wglMakeCurrent(dc, openglContext) )
    {
        MessageBox(NULL, L"wglMakeCurrent() failed", L"ERROR", MB_OK);
        return 0;
    }

    ShowWindow(win, showCmd);

    AllocConsole();
    _cprintf("GL_VERSION: %s\n", glGetString(GL_VERSION));

    LoadOpenGLProcs();
    LoadShaders();
    LoadGeometry();
    DieUponGLError();

    while(1)
    {
        while( PeekMessage(&msg, NULL, 0, 0, PM_REMOVE) )
        {
            TranslateMessage(&msg); 
            DispatchMessage(&msg); 
        }

        glClearColor(0, 1, 0, 1);
        glClear(GL_COLOR_BUFFER_BIT);
        DrawGeometry();
        SwapBuffers(dc);
    }

    return 0;
}

Lastly, the glext.h file that I used was this one here: glxext.h

fieldtensor
  • 3,804
  • 2
  • 24
  • 38

1 Answers1

0

First of all, wglGetProcAddress may return NULL if you requesting function that isn't 'extension' in terms of microsoft (which is - everything above GL 1.1; DrawElements isn't above).

Second, you need to define your function type as APIENTRY, e.g.:

typedef void (APIENTRY *PFNGLDRAWARRAYSPROC)(GLenum mode, GLint first, GLsizei count);

And last, if you have access to debugger, why not attach at least stacktrace?

keltar
  • 15,618
  • 2
  • 31
  • 40
  • APIENTRY is the same as __stdcall. My calls to wglGetProcAddress didn't ever return NULL, but assuming that I believe you about the fact that they might return NULL for glDrawArrays, then what's the proper way to get access to glDrawArrays? And I couldn't attach a stacktrace because I didn't have access to my friend's machine when I wrote this post (nor do I have aceess now). – fieldtensor Jul 30 '14 at 17:16
  • No need to 'believe me', and especially assume that, when the problem is [well known](https://www.opengl.org/discussion_boards/showthread.php/168281-Can-t-get-wglGetProcAddress-to-work-in-opengl-3-1). It have a solution, too. – keltar Jul 30 '14 at 17:58
  • Or don't get proc address at all - GL 1.1 functions must be present in opengl32.dll, there could be very few reasons to acquire them manually. Just link with them as with any other external function. – keltar Jul 30 '14 at 18:01