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
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.:And last, if you have access to debugger, why not attach at least stacktrace?