ReactOS  0.4.15-dev-5461-g062a8f2
storages.c
Go to the documentation of this file.
1 /*
2  * Implementation of the Microsoft Installer (msi.dll)
3  *
4  * Copyright 2008 James Hawkins
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19  */
20 
21 #include <stdarg.h>
22 
23 #define COBJMACROS
24 
25 #include "windef.h"
26 #include "winbase.h"
27 #include "winuser.h"
28 #include "winerror.h"
29 #include "ole2.h"
30 #include "msi.h"
31 #include "msiquery.h"
32 #include "objbase.h"
33 #include "msipriv.h"
34 #include "query.h"
35 
36 #include "wine/debug.h"
37 
39 
40 #define NUM_STORAGES_COLS 2
41 #define MAX_STORAGES_NAME_LEN 62
42 
43 typedef struct tabSTORAGE
44 {
47 } STORAGE;
48 
49 typedef struct tagMSISTORAGESVIEW
50 {
58 
60 {
61  if (size >= sv->max_storages)
62  {
63  sv->max_storages *= 2;
64  sv->storages = msi_realloc(sv->storages, sv->max_storages * sizeof(*sv->storages));
65  if (!sv->storages)
66  return FALSE;
67  }
68 
69  return TRUE;
70 }
71 
73 {
75 
76  TRACE("(%p, %d, %d, %p)\n", view, row, col, val);
77 
78  if (col != 1)
80 
81  if (row >= sv->num_rows)
82  return ERROR_NO_MORE_ITEMS;
83 
84  *val = sv->storages[row].str_index;
85 
86  return ERROR_SUCCESS;
87 }
88 
90 {
92 
93  TRACE("(%p, %d, %d, %p)\n", view, row, col, stm);
94 
95  if (row >= sv->num_rows)
96  return ERROR_FUNCTION_FAILED;
97 
98  return ERROR_INVALID_DATA;
99 }
100 
101 static UINT STORAGES_set_string( struct tagMSIVIEW *view, UINT row, UINT col, const WCHAR *val, int len )
102 {
103  ERR("Cannot modify primary key.\n");
104  return ERROR_FUNCTION_FAILED;
105 }
106 
108 {
109  ILockBytes *lockbytes = NULL;
110  STATSTG stat;
111  LPVOID data;
112  HRESULT hr;
113  DWORD size, read;
115 
116  hr = IStream_Stat(stm, &stat, STATFLAG_NONAME);
117  if (FAILED(hr))
118  return hr;
119 
120  if (stat.cbSize.QuadPart >> 32)
121  {
122  ERR("Storage is too large\n");
123  return E_FAIL;
124  }
125 
126  size = stat.cbSize.QuadPart;
127  data = msi_alloc(size);
128  if (!data)
129  return E_OUTOFMEMORY;
130 
131  hr = IStream_Read(stm, data, size, &read);
132  if (FAILED(hr) || read != size)
133  goto done;
134 
135  hr = CreateILockBytesOnHGlobal(NULL, TRUE, &lockbytes);
136  if (FAILED(hr))
137  goto done;
138 
139  ZeroMemory(&offset, sizeof(ULARGE_INTEGER));
140  hr = ILockBytes_WriteAt(lockbytes, offset, data, size, &read);
141  if (FAILED(hr) || read != size)
142  goto done;
143 
144  hr = StgOpenStorageOnILockBytes(lockbytes, NULL,
146  NULL, 0, stg);
147  if (FAILED(hr))
148  goto done;
149 
150 done:
151  msi_free(data);
152  if (lockbytes) ILockBytes_Release(lockbytes);
153  return hr;
154 }
155 
157 {
159  IStorage *stg, *substg, *prev;
160  const WCHAR *name;
161  HRESULT hr;
162  UINT r;
163 
164  TRACE("view %p, row %u, col %u, stream %p.\n", view, row, col, stream);
165 
166  if ((r = stream_to_storage(stream, &stg)))
167  return r;
168 
170 
171  hr = IStorage_CreateStorage(sv->db->storage, name,
173  0, 0, &substg);
174  if (FAILED(hr))
175  {
176  IStorage_Release(stg);
177  return ERROR_FUNCTION_FAILED;
178  }
179 
180  hr = IStorage_CopyTo(stg, 0, NULL, NULL, substg);
181  if (FAILED(hr))
182  {
183  IStorage_Release(substg);
184  IStorage_Release(stg);
185  return ERROR_FUNCTION_FAILED;
186  }
187  IStorage_Release(substg);
188 
189  prev = sv->storages[row].storage;
190  sv->storages[row].storage = stg;
191  if (prev) IStorage_Release(prev);
192 
193  return ERROR_SUCCESS;
194 }
195 
197 {
199  IStorage *stg, *substg = NULL, *prev;
200  IStream *stm;
201  LPWSTR name = NULL;
202  HRESULT hr;
204 
205  TRACE("(%p, %p)\n", view, rec);
206 
207  if (row >= sv->num_rows)
208  return ERROR_FUNCTION_FAILED;
209 
210  r = MSI_RecordGetIStream(rec, 2, &stm);
211  if (r != ERROR_SUCCESS)
212  return r;
213 
214  r = stream_to_storage(stm, &stg);
215  if (r != ERROR_SUCCESS)
216  {
217  IStream_Release(stm);
218  return r;
219  }
220 
221  name = strdupW(MSI_RecordGetString(rec, 1));
222  if (!name)
223  {
225  goto done;
226  }
227 
228  hr = IStorage_CreateStorage(sv->db->storage, name,
230  0, 0, &substg);
231  if (FAILED(hr))
232  {
234  goto done;
235  }
236 
237  hr = IStorage_CopyTo(stg, 0, NULL, NULL, substg);
238  if (FAILED(hr))
239  {
241  goto done;
242  }
243 
244  prev = sv->storages[row].storage;
246  IStorage_AddRef(stg);
247  sv->storages[row].storage = stg;
248  if (prev) IStorage_Release(prev);
249 
250 done:
251  msi_free(name);
252 
253  if (substg) IStorage_Release(substg);
254  IStorage_Release(stg);
255  IStream_Release(stm);
256 
257  return r;
258 }
259 
260 static UINT STORAGES_insert_row(struct tagMSIVIEW *view, MSIRECORD *rec, UINT row, BOOL temporary)
261 {
263 
264  if (!storages_set_table_size(sv, ++sv->num_rows))
265  return ERROR_FUNCTION_FAILED;
266 
267  if (row == -1)
268  row = sv->num_rows - 1;
269 
270  memset(&sv->storages[row], 0, sizeof(sv->storages[row]));
271 
272  /* FIXME have to readjust rows */
273 
274  return STORAGES_set_row(view, row, rec, 0);
275 }
276 
278 {
279  FIXME("(%p %d): stub!\n", view, row);
280  return ERROR_SUCCESS;
281 }
282 
284 {
285  TRACE("(%p, %p)\n", view, record);
286  return ERROR_SUCCESS;
287 }
288 
290 {
291  TRACE("(%p)\n", view);
292  return ERROR_SUCCESS;
293 }
294 
295 static UINT STORAGES_get_dimensions(struct tagMSIVIEW *view, UINT *rows, UINT *cols)
296 {
298 
299  TRACE("(%p, %p, %p)\n", view, rows, cols);
300 
301  if (cols) *cols = NUM_STORAGES_COLS;
302  if (rows) *rows = sv->num_rows;
303 
304  return ERROR_SUCCESS;
305 }
306 
308  UINT *type, BOOL *temporary, LPCWSTR *table_name )
309 {
310  TRACE("(%p, %d, %p, %p, %p, %p)\n", view, n, name, type, temporary,
311  table_name);
312 
313  if (n == 0 || n > NUM_STORAGES_COLS)
315 
316  switch (n)
317  {
318  case 1:
319  if (name) *name = L"Name";
321  break;
322 
323  case 2:
324  if (name) *name = L"Data";
326  break;
327  }
328  if (table_name) *table_name = L"_Storages";
329  if (temporary) *temporary = FALSE;
330  return ERROR_SUCCESS;
331 }
332 
334 {
335  LPCWSTR str;
336  UINT r, i, id, data;
337 
338  str = MSI_RecordGetString(rec, 1);
339  r = msi_string2id(sv->db->strings, str, -1, &id);
340  if (r != ERROR_SUCCESS)
341  return r;
342 
343  for (i = 0; i < sv->num_rows; i++)
344  {
345  STORAGES_fetch_int(&sv->view, i, 1, &data);
346 
347  if (data == id)
348  {
349  *row = i;
350  return ERROR_SUCCESS;
351  }
352  }
353 
354  return ERROR_FUNCTION_FAILED;
355 }
356 
358 {
360  UINT r, row;
361 
362  r = storages_find_row(sv, rec, &row);
363  if (r != ERROR_SUCCESS)
364  return ERROR_FUNCTION_FAILED;
365 
366  return STORAGES_set_row(view, row, rec, 0);
367 }
368 
370 {
372  UINT r, row;
373 
374  r = storages_find_row(sv, rec, &row);
375  if (r == ERROR_SUCCESS)
376  return storages_modify_update(view, rec);
377 
378  return STORAGES_insert_row(view, rec, -1, FALSE);
379 }
380 
381 static UINT STORAGES_modify(struct tagMSIVIEW *view, MSIMODIFY eModifyMode, MSIRECORD *rec, UINT row)
382 {
383  UINT r;
384 
385  TRACE("%p %d %p\n", view, eModifyMode, rec);
386 
387  switch (eModifyMode)
388  {
389  case MSIMODIFY_ASSIGN:
390  r = storages_modify_assign(view, rec);
391  break;
392 
393  case MSIMODIFY_INSERT:
394  r = STORAGES_insert_row(view, rec, -1, FALSE);
395  break;
396 
397  case MSIMODIFY_UPDATE:
398  r = storages_modify_update(view, rec);
399  break;
400 
403  case MSIMODIFY_REFRESH:
404  case MSIMODIFY_REPLACE:
405  case MSIMODIFY_MERGE:
406  case MSIMODIFY_DELETE:
407  case MSIMODIFY_VALIDATE:
410  FIXME("%p %d %p - mode not implemented\n", view, eModifyMode, rec );
412  break;
413 
414  default:
416  }
417 
418  return r;
419 }
420 
422 {
424  UINT i;
425 
426  TRACE("(%p)\n", view);
427 
428  for (i = 0; i < sv->num_rows; i++)
429  {
430  if (sv->storages[i].storage)
431  IStorage_Release(sv->storages[i].storage);
432  }
433 
434  msi_free(sv->storages);
435  sv->storages = NULL;
436  msi_free(sv);
437 
438  return ERROR_SUCCESS;
439 }
440 
441 static const MSIVIEWOPS storages_ops =
442 {
445  NULL,
457  NULL,
458  NULL,
459  NULL,
460  NULL,
461  NULL,
462 };
463 
465 {
466  IEnumSTATSTG *stgenum = NULL;
467  STATSTG stat;
468  HRESULT hr;
469  UINT count = 0;
470  ULONG size;
471 
472  hr = IStorage_EnumElements(sv->db->storage, 0, NULL, 0, &stgenum);
473  if (FAILED(hr))
474  return -1;
475 
476  sv->max_storages = 1;
477  sv->storages = msi_alloc(sizeof(*sv->storages));
478  if (!sv->storages)
479  return -1;
480 
481  while (TRUE)
482  {
483  size = 0;
484  hr = IEnumSTATSTG_Next(stgenum, 1, &stat, &size);
485  if (FAILED(hr) || !size)
486  break;
487 
488  if (stat.type != STGTY_STORAGE)
489  {
490  CoTaskMemFree(stat.pwcsName);
491  continue;
492  }
493 
494  TRACE("enumerated storage %s\n", debugstr_w(stat.pwcsName));
495 
496  if (!storages_set_table_size(sv, ++count))
497  {
498  count = -1;
499  break;
500  }
501 
502  sv->storages[count - 1].str_index = msi_add_string(sv->db->strings, stat.pwcsName, -1, FALSE);
503  sv->storages[count - 1].storage = NULL;
504 
505  IStorage_OpenStorage(sv->db->storage, stat.pwcsName, NULL,
507  &sv->storages[count - 1].storage);
508  CoTaskMemFree(stat.pwcsName);
509  }
510 
511  IEnumSTATSTG_Release(stgenum);
512  return count;
513 }
514 
516 {
517  MSISTORAGESVIEW *sv;
518  INT rows;
519 
520  TRACE("(%p, %p)\n", db, view);
521 
522  sv = msi_alloc_zero( sizeof(MSISTORAGESVIEW) );
523  if (!sv)
524  return ERROR_FUNCTION_FAILED;
525 
526  sv->view.ops = &storages_ops;
527  sv->db = db;
528 
529  rows = add_storages_to_table(sv);
530  if (rows < 0)
531  {
532  msi_free( sv );
533  return ERROR_FUNCTION_FAILED;
534  }
535  sv->num_rows = rows;
536 
537  *view = (MSIVIEW *)sv;
538 
539  return ERROR_SUCCESS;
540 }
static UINT STORAGES_close(struct tagMSIVIEW *view)
Definition: storages.c:289
const WCHAR * msi_string_lookup(const string_table *st, UINT id, int *len) DECLSPEC_HIDDEN
Definition: string.c:343
#define ERROR_INVALID_PARAMETER
Definition: compat.h:101
#define STGM_SHARE_DENY_NONE
Definition: objbase.h:920
struct tabSTORAGE STORAGE
static UINT STORAGES_get_column_info(struct tagMSIVIEW *view, UINT n, LPCWSTR *name, UINT *type, BOOL *temporary, LPCWSTR *table_name)
Definition: storages.c:307
HRESULT WINAPI CreateILockBytesOnHGlobal(HGLOBAL global, BOOL delete_on_release, ILockBytes **ret)
Definition: memlockbytes.c:98
static UINT STORAGES_fetch_stream(struct tagMSIVIEW *view, UINT row, UINT col, IStream **stm)
Definition: storages.c:89
VOID WINAPI CoTaskMemFree(LPVOID ptr)
Definition: ifs.c:442
#define ERROR_SUCCESS
Definition: deptool.c:10
HRESULT hr
Definition: shlfolder.c:183
const WCHAR * LPCWSTR
Definition: xmlstorage.h:185
#define ERROR_NO_MORE_ITEMS
Definition: compat.h:105
#define STGM_SHARE_EXCLUSIVE
Definition: objbase.h:923
#define TRUE
Definition: types.h:120
static WCHAR * strdupW(const WCHAR *src)
Definition: main.c:92
GLdouble GLdouble GLdouble r
Definition: gl.h:2055
GLuint GLuint GLsizei count
Definition: gl.h:1545
IStorage * storage
Definition: storages.c:46
UINT MSI_RecordGetIStream(MSIRECORD *, UINT, IStream **) DECLSPEC_HIDDEN
Definition: record.c:852
GLdouble n
Definition: glext.h:7729
struct tagMSISTORAGESVIEW MSISTORAGESVIEW
#define ZeroMemory
Definition: winbase.h:1670
static void msi_free(void *mem)
Definition: msipriv.h:1159
#define MSITYPE_STRING
Definition: msipriv.h:50
#define E_FAIL
Definition: ddrawi.h:102
int32_t INT
Definition: typedefs.h:58
static void * msi_realloc(void *mem, size_t len) __WINE_ALLOC_SIZE(2)
Definition: msipriv.h:1154
static void * msi_alloc_zero(size_t len) __WINE_ALLOC_SIZE(1)
Definition: msipriv.h:1148
static UINT STORAGES_insert_row(struct tagMSIVIEW *view, MSIRECORD *rec, UINT row, BOOL temporary)
Definition: storages.c:260
static UINT storages_modify_assign(struct tagMSIVIEW *view, MSIRECORD *rec)
Definition: storages.c:369
#define L(x)
Definition: ntvdm.h:50
GLenum GLint GLuint mask
Definition: glext.h:6028
#define ERROR_FUNCTION_FAILED
Definition: winerror.h:985
#define E_OUTOFMEMORY
Definition: ddrawi.h:100
#define FALSE
Definition: types.h:117
unsigned int BOOL
Definition: ntddk_ex.h:94
string_table * strings
Definition: msipriv.h:110
static UINT STORAGES_delete(struct tagMSIVIEW *view)
Definition: storages.c:421
#define debugstr_w
Definition: kernel32.h:32
#define FIXME(fmt,...)
Definition: debug.h:111
#define NUM_STORAGES_COLS
Definition: storages.c:40
const WCHAR * str
#define STGM_WRITE
Definition: objbase.h:918
#define MSITYPE_NULLABLE
Definition: msipriv.h:51
int MSIMODIFY
Definition: winemsi.idl:33
const WCHAR * MSI_RecordGetString(const MSIRECORD *, UINT) DECLSPEC_HIDDEN
Definition: record.c:433
GLuint GLfloat * val
Definition: glext.h:7180
UINT str_index
Definition: storages.c:45
#define STGM_READ
Definition: objbase.h:917
#define TRACE(s)
Definition: solgame.cpp:4
GLsizeiptr size
Definition: glext.h:5919
__wchar_t WCHAR
Definition: xmlstorage.h:180
const MSIVIEWOPS * ops
Definition: msipriv.h:355
LONG HRESULT
Definition: typedefs.h:79
GLintptr offset
Definition: glext.h:5920
UINT msi_string2id(const string_table *st, const WCHAR *data, int len, UINT *id) DECLSPEC_HIDDEN
Definition: string.c:400
unsigned long DWORD
Definition: ntddk_ex.h:95
HRESULT WINAPI StgOpenStorageOnILockBytes(ILockBytes *plkbyt, IStorage *pstgPriority, DWORD grfMode, SNB snbExclude, DWORD reserved, IStorage **ppstgOpen)
Definition: storage32.c:8984
GLint GLenum GLsizei GLsizei GLsizei GLint GLsizei const GLvoid * data
Definition: gl.h:1950
static BOOL storages_set_table_size(MSISTORAGESVIEW *sv, UINT size)
Definition: storages.c:59
static UINT storages_find_row(MSISTORAGESVIEW *sv, MSIRECORD *rec, UINT *row)
Definition: storages.c:333
Definition: parse.h:22
static UINT storages_modify_update(struct tagMSIVIEW *view, MSIRECORD *rec)
Definition: storages.c:357
static HRESULT stream_to_storage(IStream *stm, IStorage **stg)
Definition: storages.c:107
Definition: stat.h:55
GLenum GLsizei len
Definition: glext.h:6722
static UINT STORAGES_modify(struct tagMSIVIEW *view, MSIMODIFY eModifyMode, MSIRECORD *rec, UINT row)
Definition: storages.c:381
#define STGM_READWRITE
Definition: objbase.h:919
static INT add_storages_to_table(MSISTORAGESVIEW *sv)
Definition: storages.c:464
MSIDATABASE * db
Definition: storages.c:52
STORAGE * storages
Definition: storages.c:53
#define ERROR_INVALID_DATA
Definition: winerror.h:116
#define ERR(fmt,...)
Definition: debug.h:110
static UINT STORAGES_get_dimensions(struct tagMSIVIEW *view, UINT *rows, UINT *cols)
Definition: storages.c:295
_CRTIMP int __cdecl stat(const char *_Filename, struct stat *_Stat)
Definition: stat.h:345
static UINT STORAGES_delete_row(struct tagMSIVIEW *view, UINT row)
Definition: storages.c:277
GLsizei GLenum const GLvoid GLsizei GLenum GLbyte GLbyte GLbyte GLdouble GLdouble GLdouble GLfloat GLfloat GLfloat GLint GLint GLint GLshort GLshort GLshort GLubyte GLubyte GLubyte GLuint GLuint GLuint GLushort GLushort GLushort GLbyte GLbyte GLbyte GLbyte GLdouble GLdouble GLdouble GLdouble GLfloat GLfloat GLfloat GLfloat GLint GLint GLint GLint GLshort GLshort GLshort GLshort GLubyte GLubyte GLubyte GLubyte GLuint GLuint GLuint GLuint GLushort GLushort GLushort GLushort GLboolean const GLdouble const GLfloat const GLint const GLshort const GLbyte const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLdouble const GLfloat const GLfloat const GLint const GLint const GLshort const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort GLenum GLenum GLenum GLfloat GLenum GLint GLenum GLenum GLenum GLfloat GLenum GLenum GLint GLenum GLfloat GLenum GLint GLint GLushort GLenum GLenum GLfloat GLenum GLenum GLint GLfloat const GLubyte GLenum GLenum GLenum const GLfloat GLenum GLenum const GLint GLenum GLint GLint GLsizei GLsizei GLint GLenum GLenum const GLvoid GLenum GLenum const GLfloat GLenum GLenum const GLint GLenum GLenum const GLdouble GLenum GLenum const GLfloat GLenum GLenum const GLint GLsizei GLuint GLfloat GLuint GLbitfield GLfloat GLint GLuint GLboolean GLenum GLfloat GLenum GLbitfield GLenum GLfloat GLfloat GLint GLint const GLfloat GLenum GLfloat GLfloat GLint GLint GLfloat GLfloat GLint GLint const GLfloat GLint GLfloat GLfloat GLint GLfloat GLfloat GLint GLfloat GLfloat const GLdouble const GLfloat const GLdouble const GLfloat GLint i
Definition: glfuncs.h:248
static UINT STORAGES_fetch_int(struct tagMSIVIEW *view, UINT row, UINT col, UINT *val)
Definition: storages.c:72
#define MAX_STORAGES_NAME_LEN
Definition: storages.c:41
unsigned int UINT
Definition: ndis.h:50
#define NULL
Definition: types.h:112
#define MSITYPE_VALID
Definition: msipriv.h:48
static UINT STORAGES_set_row(struct tagMSIVIEW *view, UINT row, MSIRECORD *rec, UINT mask)
Definition: storages.c:196
static UINT STORAGES_execute(struct tagMSIVIEW *view, MSIRECORD *record)
Definition: storages.c:283
Definition: name.c:38
WINE_DEFAULT_DEBUG_CHANNEL(msidb)
static UINT STORAGES_set_stream(MSIVIEW *view, UINT row, UINT col, IStream *stream)
Definition: storages.c:156
static void * msi_alloc(size_t len) __WINE_ALLOC_SIZE(1)
Definition: msipriv.h:1142
unsigned int ULONG
Definition: retypes.h:1
GLenum GLuint id
Definition: glext.h:5579
#define ERROR_CALL_NOT_IMPLEMENTED
Definition: compat.h:102
static const MSIVIEWOPS storages_ops
Definition: storages.c:441
IStorage * storage
Definition: msipriv.h:109
GLuint GLuint GLsizei GLenum type
Definition: gl.h:1545
UINT STORAGES_CreateView(MSIDATABASE *db, MSIVIEW **view)
Definition: storages.c:515
BOOL msi_add_string(string_table *st, const WCHAR *data, int len, BOOL persistent) DECLSPEC_HIDDEN
Definition: string.c:303
WCHAR * LPWSTR
Definition: xmlstorage.h:184
#define memset(x, y, z)
Definition: compat.h:39
static UINT STORAGES_set_string(struct tagMSIVIEW *view, UINT row, UINT col, const WCHAR *val, int len)
Definition: storages.c:101
_CRTIMP int __cdecl read(_In_ int _FileHandle, _Out_writes_bytes_(_MaxCharCount) void *_DstBuf, _In_ unsigned int _MaxCharCount)
#define ERROR_OUTOFMEMORY
Definition: deptool.c:13
struct png_info_def *typedef unsigned char **typedef struct png_info_def *typedef struct png_info_def *typedef struct png_info_def *typedef unsigned char ** row
Definition: typeof.h:78
GLuint const GLchar * name
Definition: glext.h:6031