Related
I need to add custom menu items as needed. I found OnInitMenuPopup (WM_INITMENUPOPUP) does what I need but I can't get an icon to show next to the text on the menu? I've tried a 16x16 png graphic using m_MyGraphic as a CPngImage, I've tried attaching it to a CBitmap, I've tried saving the graphic as a .bmp and loading as CBitmap. I've tried not setting the graphic on the load, but then trying to do it with SetMenuItemBitmaps(), I've tried a 13x13 graphic, I've tried a 15x15 graphic (which matches GetMenuCheckMarkDimensions()). Never does a graphic show next to the menu item? What am I doing wrong or missing?
TIA!
void CMainFrame::OnInitMenuPopup(CMenu* pPopupMenu, UINT nIndex, BOOL bSysMenu)
{
// add items
if (pPopupMenu && pPopupMenu->GetMenuItemCount() > 0 && pPopupMenu->GetMenuItemID(0) == ID_MY_EXPECTED_ID) {
// loop though and add menu items
for (UINT i=0; i<theApp.m_MyList.GetCount(); i++) {
CString s;
s.Format(_T("%i: %s"), i, theApp.m_MyList[i].String);
MENUITEMINFO mii={};
mii.cbSize=sizeof(mii);
mii.fMask=MIIM_ID|MIIM_STRING|MIIM_BITMAP;
mii.wID=ID_MY_RANGE_0+i;
mii.dwTypeData=s.GetBuffer();
mii.hbmpItem=(HBITMAP)m_MyBitmap.GetSafeHandle();
pPopupMenu->InsertMenuItem(i+1, &mii, TRUE);
// not working above so tried using this as well but it doesn't work either:
//pPopupMenu->SetMenuItemBitmaps(i+1, MF_BYPOSITION, &m_MyBitmap, &m_MyBitmap);
}
}
CFrameWndEx::OnInitMenuPopup(pPopupMenu, nIndex, bSysMenu);
}
I found a work around. First add CMFCToolBarMenuButton::m_bAlwaysCallOwnerDraw=TRUE; somewhere on initialization.
Then handle drawing it:
BOOL CMainFrame::OnDrawMenuImage(CDC* pDC, const CMFCToolBarMenuButton* pMenuButton, const CRect& rectImage)
{
BOOL result=FALSE;
if (pMenuButton->m_nID>=ID_MY_RANGE_0 && pMenuButton->m_nID<=ID_MY_RANGE_N) {
// size to use on menu
CSize sizemenuimage = CMFCToolBar::GetMenuImageSize();
// get size of our bitmap
BITMAP bitmap;
m_MyBitmap.GetBitmap(&bitmap);
// create dc to attach bitmap to
CDC dcmem;
if (dcmem.CreateCompatibleDC(pDC)) {
// attach bitmap to dc
CBitmap * poldbitmap=dcmem.SelectObject(&m_MyBitmap);
if (poldbitmap) {
// Draw bitmap
result=pDC->StretchBlt(rectImage.left+(rectImage.Width()-sizemenuimage.cx)/2,
rectImage.top+(rectImage.Height()-sizemenuimage.cy)/2,
sizemenuimage.cx, sizemenuimage.cy,
&dcmem, 0, 0, bitmap.bmWidth, bitmap.bmHeight, SRCCOPY);
// Select original object
dcmem.SelectObject(poldbitmap);
}
dcmem.DeleteDC();
}
}
return result;
}
Another possible solution (if you already have the bitmaps for toolbar) is:
void CMainFrame::OnInitMenuPopup(CMenu* pPopupMenu, UINT nIndex, BOOL bSysMenu)
{
CMDIFrameWnd::OnInitMenuPopup(pPopupMenu, nIndex, bSysMenu);
// TODO: Add your message handler code here
HICON hIcon = AfxGetApp()->LoadIcon(IDR_TESTMETYPE);
pPopupMenu->SetMenuItemBitmaps(ID_FILE_NEW, MF_BYCOMMAND, ConvertIconToBitmap(hIcon), NULL);
...
...
}
where SetMenuItemBitmaps is defined as:
CBitmap* CMainFrame::ConvertIconToBitmap(HICON hIcon)
{
CDC dc;
CBitmap bmp;
CClientDC ClientDC(this);
dc.CreateCompatibleDC(&ClientDC);
bmp.CreateCompatibleBitmap(&ClientDC, 13, 13);
CBitmap* pOldBmp = (CBitmap*)dc.SelectObject(&bmp);
::DrawIconEx(dc.GetSafeHdc(), 0, 0, hIcon, 13, 13, 0, (HBRUSH)RGB(255, 255, 255), DI_NORMAL);
dc.SelectObject(pOldBmp);
dc.DeleteDC();
HBITMAP hBitmap = (HBITMAP)::CopyImage((HANDLE)((HBITMAP)bmp),
IMAGE_BITMAP, 0, 0, LR_DEFAULTSIZE);
return CBitmap::FromHandle(hBitmap);
}
I found on (at least) Win10 that calling ::GetClipboardData() on a CF_DIBV5 created via Alt-PrtScrn (may be a synthesized format) causes the image to be modified (and basically corrupted).
For example, on the handler for ON_WM_CLIPBOARDUPDATE() the simple loop below will cause the corruption (note that you need to use debug mode so the ::GetClipboardData() is not optimized out).
To test, first don't run your app that processes the clipboard, use Alt-PrntScrn to capture data, then paste it to Paint. Now run the app that process the clipboard (with below sample below). Repeat Alt-PrntScrn process and you'll see it's different where the right side of the captured window ends up on the left and not centered in the area.
void CMainFrame::OnClipboardUpdate()
if (::OpenClipboard(AfxGetMainWnd()->m_hWnd)) {
UINT uformat=0;
while ((uformat=::EnumClipboardFormats(uformat))!=0) {
if (uformat==CF_DIBV5) {
// get the data - run in debug mode so not optimized out
HGLOBAL hglobal=::GetClipboardData(uformat);
}
}
// clean up
::CloseClipboard();
}
}
To enable the handler you need to call AddClipboardFormatListener(GetSafeHwnd()); in something like int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct) then RemoveClipboardFormatListener(GetSafeHwnd()); on void CMainFrame::OnDestroy()
So is this a bug in Win10 (and other Windows Versions) or should I be doing something else that the sample isn't doing? (I know other formats exist, but the CF_DBIV5 is what I wanted).
I'm on Version 1903 (OS Build 18362.838)
Note the sample pic has right side items on left and some garbage pixels in lower left. I alt-prtscrn while app running, pasted in paint.
My resolution is 2560x1600.
Here's a link to a project that will cause the problem:
Sample Project
You can find the following description in the documentation :
The red, green, and blue bitfield masks for BI_BITFIELD bitmaps
immediately follow the BITMAPINFOHEADER, BITMAPV4HEADER, and
BITMAPV5HEADER structures. The BITMAPV4HEADER and BITMAPV5HEADER
structures contain additional members for red, green, and blue masks
as follows.
When the biCompression member of BITMAPINFOHEADER is set to BI_BITFIELDS and the function receives an argument of type LPBITMAPINFO, the color masks will immediately follow the header. The color table, if present, will follow the color masks. BITMAPCOREHEADER bitmaps do not support color masks.
When you handle CF_DIBV5 correctly you will draw the image successfully. The following is an example of Win32 C++ you can refer to:
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
static UINT uFormat = (UINT) -1;
HDC hdcMem = NULL;
RECT rc = {0};
BYTE * pData = NULL;
BITMAPV5HEADER *pDibv5Info = NULL;
switch (message)
{
case WM_CLIPBOARDUPDATE:
{
if (IsClipboardFormatAvailable(CF_DIBV5))
{
uFormat = CF_DIBV5;
::CloseClipboard();
GetClientRect(hWnd, &rc);
InvalidateRect(hWnd, &rc, TRUE);
}
}
break;
case WM_PAINT:
{
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hWnd, &ps);
switch (uFormat)
{
case CF_DIBV5:
hdcMem = CreateCompatibleDC(hdc);
if (hdcMem != NULL)
{
if (::OpenClipboard(hWnd)) {
HANDLE hglobal = ::GetClipboardData(uFormat);
pData = (BYTE*)GlobalLock(hglobal);
if (pData)
{
pDibv5Info = (BITMAPV5HEADER *)pData;
int offset = pDibv5Info->bV5Size + pDibv5Info->bV5ClrUsed * sizeof(RGBQUAD);
if (pDibv5Info->bV5Compression == BI_BITFIELDS)
offset += 3 * sizeof(DWORD); //three DWORD color masks that specify the red, green, and blue components
pData += offset;
SetDIBitsToDevice(hdc, 20, 20, pDibv5Info->bV5Width, pDibv5Info->bV5Height, 0, 0, 0, pDibv5Info->bV5Height, pData, (BITMAPINFO *)pDibv5Info, 0);
}
GlobalUnlock(hglobal);
::CloseClipboard();
}
}
break;
}
EndPaint(hWnd, &ps);
}
break;
}
The correct image drawn in my application window:
I can reproduce the same issue without code:
if (pDibv5Info->bV5Compression == BI_BITFIELDS)
offset += 3 * sizeof(DWORD);
The corrupted image:
I'm updating the question to remove irrelevant details. The conclusion I've drawn is that if a valid alpha channel exists, it will honor it, but if it doesn't (say a 24-bit PNG w/o alpha channel), it uses F0F0F0 as a transparent color.
I have an image being loaded into a static "picture control" (chosen in visual studio) in a dialog. I noticed that color 0xF0F0F0 is being displayed as a "transparent" color (background of the dialog bleeds through). The bitmap is loaded via CStatic::SetBitmap.
The Picture Control transparent flag is set to false.
The image is loaded via CImage::Load.
If I wanted to mask a color out of a CStatic bitmap set via SetBitmap, how would I do it? I don't, but maybe that would help me find the cause.
Minimum example below. I created a dialog project with the VS wizard, and added a picture control to the main dialog. Then I added only the following code:
//header code added
CPngImage logoImage;
CStatic pictureCtrl;
CBrush bgBrush;
....
afx_msg HBRUSH OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor);
//cpp code added
DDX_Control(pDX, IDC_STATICIMG, pictureCtrl);
....
ON_WM_CTLCOLOR()
....
bgBrush.CreateSolidBrush(RGB(0, 255, 0));
logoImage.LoadFromFile(_T("C:\\temp\\logo.png"));
pictureCtrl.SetBitmap(logoImage);
....
HBRUSH CMFCApplication1Dlg::OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor) {
return bgBrush;
}
And here is the image file I'm testing with.
And here is what it looks like on the dialog:
// MFCApplication1Dlg.h : header file
//
#pragma once
// CMFCApplication1Dlg dialog
class CMFCApplication1Dlg : public CDialogEx
{
// Construction
public:
CMFCApplication1Dlg(CWnd* pParent = nullptr); // standard constructor
CPngImage logoImage;
CStatic pictureCtrl;
CBrush bgBrush;
// Dialog Data
#ifdef AFX_DESIGN_TIME
enum { IDD = IDD_MFCAPPLICATION1_DIALOG };
#endif
protected:
virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support
// Implementation
protected:
HICON m_hIcon;
// Generated message map functions
virtual BOOL OnInitDialog();
afx_msg void OnSysCommand(UINT nID, LPARAM lParam);
afx_msg void OnPaint();
afx_msg HCURSOR OnQueryDragIcon();
DECLARE_MESSAGE_MAP()
public:
afx_msg HBRUSH OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor);
};
// MFCApplication1Dlg.cpp : implementation file
//
#include "stdafx.h"
#include "MFCApplication1.h"
#include "MFCApplication1Dlg.h"
#include "afxdialogex.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#endif
// CAboutDlg dialog used for App About
class CAboutDlg : public CDialogEx
{
public:
CAboutDlg();
// Dialog Data
#ifdef AFX_DESIGN_TIME
enum { IDD = IDD_ABOUTBOX };
#endif
protected:
virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support
// Implementation
protected:
DECLARE_MESSAGE_MAP()
};
CAboutDlg::CAboutDlg() : CDialogEx(IDD_ABOUTBOX)
{
}
void CAboutDlg::DoDataExchange(CDataExchange* pDX)
{
CDialogEx::DoDataExchange(pDX);
}
BEGIN_MESSAGE_MAP(CAboutDlg, CDialogEx)
END_MESSAGE_MAP()
// CMFCApplication1Dlg dialog
CMFCApplication1Dlg::CMFCApplication1Dlg(CWnd* pParent /*=nullptr*/)
: CDialogEx(IDD_MFCAPPLICATION1_DIALOG, pParent)
{
m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
}
void CMFCApplication1Dlg::DoDataExchange(CDataExchange* pDX)
{
CDialogEx::DoDataExchange(pDX);
DDX_Control(pDX, IDC_STATICIMG, pictureCtrl);
}
BEGIN_MESSAGE_MAP(CMFCApplication1Dlg, CDialogEx)
ON_WM_SYSCOMMAND()
ON_WM_PAINT()
ON_WM_QUERYDRAGICON()
ON_WM_CTLCOLOR()
END_MESSAGE_MAP()
// CMFCApplication1Dlg message handlers
BOOL CMFCApplication1Dlg::OnInitDialog()
{
CDialogEx::OnInitDialog();
// Add "About..." menu item to system menu.
// IDM_ABOUTBOX must be in the system command range.
ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX);
ASSERT(IDM_ABOUTBOX < 0xF000);
CMenu* pSysMenu = GetSystemMenu(FALSE);
if (pSysMenu != nullptr)
{
BOOL bNameValid;
CString strAboutMenu;
bNameValid = strAboutMenu.LoadString(IDS_ABOUTBOX);
ASSERT(bNameValid);
if (!strAboutMenu.IsEmpty())
{
pSysMenu->AppendMenu(MF_SEPARATOR);
pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu);
}
}
// Set the icon for this dialog. The framework does this automatically
// when the application's main window is not a dialog
SetIcon(m_hIcon, TRUE); // Set big icon
SetIcon(m_hIcon, FALSE); // Set small icon
bgBrush.CreateSolidBrush(RGB(0, 255, 0));
logoImage.LoadFromFile(_T("C:\\temp\\logo.png"));
pictureCtrl.SetBitmap(logoImage);
return TRUE; // return TRUE unless you set the focus to a control
}
void CMFCApplication1Dlg::OnSysCommand(UINT nID, LPARAM lParam)
{
if ((nID & 0xFFF0) == IDM_ABOUTBOX)
{
CAboutDlg dlgAbout;
dlgAbout.DoModal();
}
else
{
CDialogEx::OnSysCommand(nID, lParam);
}
}
// If you add a minimize button to your dialog, you will need the code below
// to draw the icon. For MFC applications using the document/view model,
// this is automatically done for you by the framework.
void CMFCApplication1Dlg::OnPaint()
{
if (IsIconic())
{
CPaintDC dc(this); // device context for painting
SendMessage(WM_ICONERASEBKGND, reinterpret_cast<WPARAM>(dc.GetSafeHdc()), 0);
// Center icon in client rectangle
int cxIcon = GetSystemMetrics(SM_CXICON);
int cyIcon = GetSystemMetrics(SM_CYICON);
CRect rect;
GetClientRect(&rect);
int x = (rect.Width() - cxIcon + 1) / 2;
int y = (rect.Height() - cyIcon + 1) / 2;
// Draw the icon
dc.DrawIcon(x, y, m_hIcon);
}
else
{
CDialogEx::OnPaint();
}
}
// The system calls this function to obtain the cursor to display while the user drags
// the minimized window.
HCURSOR CMFCApplication1Dlg::OnQueryDragIcon()
{
return static_cast<HCURSOR>(m_hIcon);
}
HBRUSH CMFCApplication1Dlg::OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor) {
return bgBrush;
}
On my system (Windows 10), the color 0xF0F0F0 equals GetSysColor(COLOR_BTNFACE) which is the default dialog background color. When drawing, the static control seems to replace this color in the background image with the brush returned from OnCtlColor() handler of the parent window. This does have the taste of a feature and not a bug (though I couldn't find anything in the reference that specifies this behaviour).
Here is a code snippet to reproduce this issue even without using CPngImage or CImage, just by drawing in a memory DC with color 0xF0F0F0.
As the behaviour only appears when the source image does not contain an alpha channel, a solution would be to convert the source image to 32-bpp ARGB format. This way we don't have to override CStatic::OnPaint():
// Set the alpha channel of a 32-bpp ARGB image to the given value.
HRESULT SetAlphaChannel( CImage& image, std::uint8_t alpha )
{
if( ! image.GetBits() || image.GetBPP() != 32 )
return E_INVALIDARG;
GdiFlush(); // Make sure GDI has finished all drawing in source image.
for( int y = 0; y < image.GetHeight(); ++y )
{
DWORD* pPix = reinterpret_cast<DWORD*>( image.GetPixelAddress( 0, y ) );
for( int x = 0; x < image.GetWidth(); ++x, ++pPix )
{
*pPix = ( *pPix & 0xFFFFFF ) | ( alpha << 24 );
}
}
return S_OK;
}
// Load an image and convert to 32-bpp ARGB format, if necessary.
HRESULT LoadImageAndConvertToARGB32( CImage& image, LPCWSTR pFilePath )
{
CImage tempImage;
HRESULT hr = tempImage.Load( pFilePath );
if( FAILED( hr ) )
return hr;
if( tempImage.GetBPP() == 32 ) // Assume 32 bpp image already has an alpha channel
{
image.Attach( tempImage.Detach() );
return S_OK;
}
if( ! image.Create( tempImage.GetWidth(), tempImage.GetHeight(), 32, CImage::createAlphaChannel ) )
return E_FAIL;
HDC const imageDC = image.GetDC();
BOOL const bitBltSuccess = tempImage.BitBlt( imageDC, 0, 0, SRCCOPY );
image.ReleaseDC();
if( ! bitBltSuccess )
return E_FAIL;
SetAlphaChannel( image, 255 ); // set alpha to opaque
return S_OK;
}
Usage:
Replace call to CImage::Load() by:
LoadImageAndConvertToARGB32( m_image, filePath );
Notes:
There is another static control nastiness when you assign a 32-bpp bitmap with a non-zero alpha channel to the control¹ (as you do when following my solution). In this case, the static control will make a copy of the bitmap you passed in while you are responsible to destroy this copy!
Mandatory OldNewThing read:
"When will the static control automatically delete the image loaded into it, and when is it the responsibility of the application?"
¹) More precisely: When using version 6 of the common controls, which almost all applications do these days.
It's not a PNG problem, it's a color depth problem.
According to your code, I converted 8-bit PNG picture into 8-bit BMP picture by format conversion tool, and the picture still show the background color.
So I saved 8-bit PNG picture to 32-bit png picture, and that's all right.
Why is that?
Answer: A GIF file has two parts: a color table and the image pixel data. The color table is a list of the colors used in that image (an 8-bit GIF can have up to 2^8 = 256 colors in the color table, but a 4-bit GIF can have only 2^4 = 16 colors), and each color is assigned a number. The image pixel data are for the image itself, and each pixel is assigned a number that points to its color in the color table. For example, if color #10 in the color table is red (#FF0000), then any pixel in the image with the number 10 will be displayed as red. The colors in the color table will vary from GIF file to GIF file based on the image itself; color #10 will not always be red. The color table is the set of up to 256 colors necessary to render that image.
When we add index transparency, every color in the color table is given a transparency designation in addition to its color data (i.e., RGB values):
zero (o = False in Boolean algebra) means do not display this color, or
one (1 = True in Boolean Algebra) means display this color.
There are no intermediate opacities; the color is either displayed or it is not. The end result is that a pixel with an index transparency color will not be displayed and whatever is in the background behind that pixel will show through. For example, if color #10 is red (#FF0000) and is designated as transparent (index transparency = 0), then any pixel that is color #10 will not be displayed and the background will show through.
There can be multiple transparent colors in index transparency, because every color in the color table has a designation of either opaque (1) or transparent (0). Most graphics programs assume that the canvas color (often white, but it could be any color) is the default transparent color, but you can specify any color (or any number of colors) as transparent or not.
This type of transparency is very common in GIF and PNG8 files and is easy to identify, because there is no fading, there are no partially transparent pixels, and the edges are often described as “hard” or “pixelated.”
I'm afraid the following is the best answer I'm going to get. MFC/Win32 does something funny and it's not clear why. But the image has no problems displaying correctly if you draw it manually.
I created a custom (very rough) CStatic class and added the OnPaint below. In the screenshot, the first is using my class, and the second is using regular CStatic. Both are using an identical image (24 bit BMP).
struct AFX_CTLCOLOR {
HWND hWnd;
HDC hDC;
UINT nCtlType;
};
void CMyStatic::OnPaint() {
CPaintDC dc(this); // device context for painting
CRect r;
GetClientRect(r);
WPARAM w = (WPARAM)&dc;
AFX_CTLCOLOR ctl;
ctl.hWnd = this->GetSafeHwnd();
ctl.nCtlType = CTLCOLOR_STATIC;
ctl.hDC = dc.GetSafeHdc();
HBRUSH bg=(HBRUSH)::SendMessage(GetParent()->GetSafeHwnd(), WM_CTLCOLORSTATIC, w, (LPARAM)&ctl);
CBrush cbg;
cbg.Attach(bg);
dc.FillRect(r, &cbg);
cbg.Detach();
HBITMAP hbmp=GetBitmap();
BITMAP binfo;
CBitmap* cbmp = CBitmap::FromHandle(hbmp);
cbmp->GetBitmap(&binfo);
CDC dcMem;
dcMem.CreateCompatibleDC(&dc);
dcMem.SelectObject(cbmp);
dc.BitBlt((r.Width()-binfo.bmWidth)/2, (r.Height() - binfo.bmHeight) / 2, binfo.bmWidth, binfo.bmHeight, &dcMem, 0, 0, SRCCOPY);
}
suppose that I have COpenGLControl class downloaded here from codeguru assuming that the first event handler runned when creating the OpenGL window is OnCreate, I have tried to catch up the errors of this class. here's the code used to create the window in my dialog's .h and .cpp files:
MyOpenGLTestDlg.h
COpenGLControl m_oglWindow;
MyOpenGLTestDlg.cpp
CRect rect;
// Get size and position of the picture control
GetDlgItem(ID_OPENGL)->GetWindowRect(rect);
// Convert screen coordinates to client coordinates
ScreenToClient(rect);
up to know I think the function OnCreate has been invoked. In fact, I think the line of code COpenGLControl m_oglWindow; causes this function to be invoked! but I'm not sure so it will be appreciated if you guide me alittle about this?
anyway, I have not made a lot of change to the class:
OpenGLControl.h
#pragma once
#include "afxwin.h"
#include "WinBase.h"
#include <gl/gl.h>
#include <gl/glu.h>
class COpenGLControl : public CWnd
{
public:
/******************/
/* Public Members */
/******************/
UINT_PTR m_unpTimer;
// View information variables
float m_fLastX;
float m_fLastY;
float m_fPosX;
float m_fPosY;
float m_fZoom;
float m_fRotX;
float m_fRotY;
bool m_bIsMaximized;
private:
/*******************/
/* Private Members */
/*******************/
// Window information
CWnd *hWnd; //window handle
HDC hdc; //device context handle
HGLRC hrc; //handle to GL Rendering Context
int m_nPixelFormat;
CRect m_rect;
CRect m_oldWindow;
CRect m_originalRect;
public:
COpenGLControl(void);
virtual ~COpenGLControl(void);
void oglCreate(CRect rect, CWnd *parent);
void oglInitialize(void);
void oglDrawScene(void);
// Added message classes:
afx_msg void OnPaint();
afx_msg void OnSize(UINT nType, int cx, int cy);
afx_msg void OnDraw(CDC *pDC);
afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct);
afx_msg void OnTimer(UINT nIDEvent);
afx_msg void OnMouseMove(UINT nFlags, CPoint point);
DECLARE_MESSAGE_MAP()
};
OpenGLControl.cpp
int COpenGLControl::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
if (CWnd::OnCreate(lpCreateStruct) == -1) return -1;
oglInitialize();
return 0;
}
void COpenGLControl::oglInitialize(void)
{
// Initial Setup:
//
static PIXELFORMATDESCRIPTOR pfd =
{
sizeof(PIXELFORMATDESCRIPTOR),
1,
PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER,
PFD_TYPE_RGBA,
32, // bit depth
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
24, // z-buffer depth
8,0,PFD_MAIN_PLANE, 0, 0, 0, 0,
};
// Get device context only once.
hdc = GetDC()->m_hDC;
// Pixel format.
m_nPixelFormat = ChoosePixelFormat(hdc, &pfd);
SetPixelFormat(hdc, m_nPixelFormat, &pfd);
// Create the OpenGL Rendering Context.
hrc = wglCreateContext(hdc);
GLenum error13 = glGetError();
wglMakeCurrent(hdc, hrc);
// Basic Setup:
//
// Set color to use when clearing the background.
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
glClearDepth(1.0f);
// Turn on backface culling
glFrontFace(GL_CCW);
glCullFace(GL_BACK);
// Turn on depth testing
glEnable(GL_DEPTH_TEST);
glDepthFunc(GL_LEQUAL);
// Send draw request
OnDraw(NULL);
}
As you see I have written the code GLenum error13 = glGetError(); right after hrc = wglCreateContext(hdc); to catch any probable error that it throws and yes the value for error13 is 1282 which means INVALID_OPERATION so I think the handle for OpenGL rendering context is not created properly!
besides if you check the values for hdc and hrc, you encounter these:
hdc -> unused = ??? (Error: expression can not be evaluated)
hrc -> unused = 0
Could you help me find out why it is the case? and what's the problem?
The results are quite undefined if you call GL functions (like glGetError() itself) without having a current GL context.
wglCreateContext() is part of the Win33 API (and not the GL API) and will signal errors by returning a NULL pointer. You can call the Windows API function GetLastError() if you want details in that case, like with most other Windows API functions.
Do not call an OpenGL API function before you have a "current context" for the calling thread in Win32. The operations will be undefined.
To correct this issue, simply move the call to wglMakeCurrent (...) one line up so that it comes before the call to glGetError (...).
Note that you have to do this for every thread, and in Win32 only one thread is allowed to access an OpenGL context at any given time. If you ever want to do multi-threaded rendering in Win32, you will have to either acquire/release the context and do synchronization between threads or use a bunch of contexts that "share lists" (wglShareLists (...)).
Like derhass mentioned, if you want information about an error generated by the WGL API, use GetLastError (...). WGL is part of the window system, which is built on top of the Win32 API, so it will communicate its errors to you through the traditional Win32 channels.
By the way, try not to print GL error values in decimal form. They are always enumerated in gl.h as hexadecimal constants, so it would be easier to find the appropriate enumerant if you did this:
printf ("OpenGL Error: 0x%X\n", err);
The same goes for all enumerated constants in OpenGL. If you do not have a function that will map them to a human-readable string, you should use the hexadecimal value to look them up.
What is the best way to set up a window in win32 that has OpenGl(and glsl if the needs extra code to work) integrated into it?
I have done some research and found numerous ways of accomplishing this task i was wondering what the best way is or what way you like the most if the best way doesn't have an answer.
I have looked at nehe`s design and also the one supplied by the OpenGl super bible which both have completely different ways of accomplishing it (also the super bibles one gives me errors :().
any help would be appreciated including tutorials etc.
thanks
All your "different ways" aren't so different. You have to:
create your window (in the usual Win32 way, with RegisterClass(Ex) and CreateWindow(Ex))
create a GDI device context corresponding to your window (GetDC)
pick a pixel format which is supported by the display (optional DescribePixelFormat, then ChoosePixelFormat)
create your OpenGL context (wglCreateContext)
(optional, but required to use GLSL) link OpenGL extension functions (GLee or GLEW helpers, or glGetString(GL_EXTENSIONS) then wglGetProcAddress)
(optional) create an OpenGL 3.x context, free the compatibility context (wglCreateContextAttribs)
make the context active (wglMakeCurrent)
start using OpenGL (set up shader programs, load textures, draw stuff, etc.)
An excerpt of code showing these steps in action (not suitable for copy+paste, a bunch of RAII wrappers are used):
bool Context::attach( HWND hwnd )
{
PIXELFORMATDESCRIPTOR pfd = { sizeof(pfd), 1 };
if (!m_dc) {
scoped_window_hdc(hwnd).swap(m_dc);
pfd.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_SUPPORT_COMPOSITION | PFD_DOUBLEBUFFER;
pfd.iPixelType = PFD_TYPE_RGBA;
pfd.cColorBits = 32;
pfd.cAlphaBits = 8;
pfd.iLayerType = PFD_MAIN_PLANE;
auto format_index = ::ChoosePixelFormat(m_dc.get(), &pfd);
if (!format_index)
return false;
if (!::SetPixelFormat(m_dc.get(), format_index, &pfd))
return false;
}
auto active_format_index = ::GetPixelFormat(m_dc.get());
if (!active_format_index)
return false;
if (!::DescribePixelFormat(m_dc.get(), active_format_index, sizeof pfd, &pfd))
return false;
if ((pfd.dwFlags & PFD_SUPPORT_OPENGL) != PFD_SUPPORT_OPENGL)
return false;
m_render_thread = ::CreateThread(NULL, 0, &RenderThreadProc, this, 0, NULL);
return m_render_thread != NULL;
}
DWORD WINAPI Context::RenderThreadProc( LPVOID param )
{
Context* const ctx = static_cast<Context*>(param);
HDC dc = ctx->m_dc.get();
SIZE canvas_size;
ctx->m_dc.check_resize(&canvas_size);
scoped_hglrc glrc(wglCreateContext(dc));
if (!glrc)
return EXIT_FAILURE;
if (!glrc.make_current(dc))
return EXIT_FAILURE;
if (ctx->m_major_version > 2 && GLEE_WGL_ARB_create_context) {
int const create_attribs[] = {
WGL_CONTEXT_MAJOR_VERSION_ARB, ctx->m_major_version,
WGL_CONTEXT_MINOR_VERSION_ARB, ctx->m_minor_version,
0
};
scoped_hglrc advrc(wglCreateContextAttribsARB(dc, 0, create_attribs));
if (advrc) {
if (!advrc.make_current(dc))
return EXIT_FAILURE;
advrc.swap(glrc);
}
}
{
const char* ver = reinterpret_cast<const char*>(glGetString(GL_VERSION));
if (ver) {
OutputDebugStringA("GL_VERSION = \"");
OutputDebugStringA(ver);
OutputDebugStringA("\"\n");
}
}
glDisable(GL_DITHER);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glClearColor(0.f, 0.f, 0.f, 1.f);
if (GLEE_WGL_EXT_swap_control)
wglSwapIntervalEXT(1);
GLuint vao;
glGenVertexArrays(1, &vao);
glBindVertexArray(vao);
while (!::InterlockedExchange(&ctx->m_stop_render, 0)) {
ctx->process_queued_tasks();
if (ctx->m_dc.check_resize(&canvas_size)) {
glViewport(0, 0, canvas_size.cx, canvas_size.cy);
ctx->process_on_resize();
}
glClear(GL_COLOR_BUFFER_BIT);
glBindVertexArray(vao);
ctx->process_on_render();
BOOL swapped = ::SwapBuffers(dc);
if (!swapped)
std::cout << "::SwapBuffers failure, GetLastError() returns " << std::hex << ::GetLastError() << std::endl;
}
ctx->m_program_db.clear();
return EXIT_SUCCESS;
}
It also doesn't cover window creation, it enables OpenGL on an existing window.
This pretty much did it for me: http://nehe.gamedev.net/tutorial/creating_an_opengl_window_%28win32%29/13001/
EDIT: Related code:
/*
* This Code Was Created By Jeff Molofee 2000
* A HUGE Thanks To Fredric Echols For Cleaning Up
* And Optimizing This Code, Making It More Flexible!
* If You've Found This Code Useful, Please Let Me Know.
* Visit My Site At nehe.gamedev.net
*/
#include <windows.h> // Header File For Windows
#include <gl\gl.h> // Header File For The OpenGL32 Library
#include <gl\glu.h> // Header File For The GLu32 Library
#include <gl\glaux.h> // Header File For The Glaux Library
HDC hDC=NULL; // Private GDI Device Context
HGLRC hRC=NULL; // Permanent Rendering Context
HWND hWnd=NULL; // Holds Our Window Handle
HINSTANCE hInstance; // Holds The Instance Of The Application
bool keys[256]; // Array Used For The Keyboard Routine
bool active=TRUE; // Window Active Flag Set To TRUE By Default
bool fullscreen=TRUE; // Fullscreen Flag Set To Fullscreen Mode By Default
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); // Declaration For WndProc
GLvoid ReSizeGLScene(GLsizei width, GLsizei height) // Resize And Initialize The GL Window
{
if (height==0) // Prevent A Divide By Zero By
{
height=1; // Making Height Equal One
}
glViewport(0,0,width,height); // Reset The Current Viewport
glMatrixMode(GL_PROJECTION); // Select The Projection Matrix
glLoadIdentity(); // Reset The Projection Matrix
// Calculate The Aspect Ratio Of The Window
gluPerspective(45.0f,(GLfloat)width/(GLfloat)height,0.1f,100.0f);
glMatrixMode(GL_MODELVIEW); // Select The Modelview Matrix
glLoadIdentity(); // Reset The Modelview Matrix
}
int InitGL(GLvoid) // All Setup For OpenGL Goes Here
{
glShadeModel(GL_SMOOTH); // Enable Smooth Shading
glClearColor(0.0f, 0.0f, 0.0f, 0.5f); // Black Background
glClearDepth(1.0f); // Depth Buffer Setup
glEnable(GL_DEPTH_TEST); // Enables Depth Testing
glDepthFunc(GL_LEQUAL); // The Type Of Depth Testing To Do
glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST); // Really Nice Perspective Calculations
return TRUE; // Initialization Went OK
}
int DrawGLScene(GLvoid) // Here's Where We Do All The Drawing
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // Clear Screen And Depth Buffer
glLoadIdentity(); // Reset The Current Modelview Matrix
return TRUE; // Everything Went OK
}
GLvoid KillGLWindow(GLvoid) // Properly Kill The Window
{
if (fullscreen) // Are We In Fullscreen Mode?
{
ChangeDisplaySettings(NULL,0); // If So Switch Back To The Desktop
ShowCursor(TRUE); // Show Mouse Pointer
}
if (hRC) // Do We Have A Rendering Context?
{
if (!wglMakeCurrent(NULL,NULL)) // Are We Able To Release The DC And RC Contexts?
{
MessageBox(NULL,"Release Of DC And RC Failed.","SHUTDOWN ERROR",MB_OK | MB_ICONINFORMATION);
}
if (!wglDeleteContext(hRC)) // Are We Able To Delete The RC?
{
MessageBox(NULL,"Release Rendering Context Failed.","SHUTDOWN ERROR",MB_OK | MB_ICONINFORMATION);
}
hRC=NULL; // Set RC To NULL
}
if (hDC && !ReleaseDC(hWnd,hDC)) // Are We Able To Release The DC
{
MessageBox(NULL,"Release Device Context Failed.","SHUTDOWN ERROR",MB_OK | MB_ICONINFORMATION);
hDC=NULL; // Set DC To NULL
}
if (hWnd && !DestroyWindow(hWnd)) // Are We Able To Destroy The Window?
{
MessageBox(NULL,"Could Not Release hWnd.","SHUTDOWN ERROR",MB_OK | MB_ICONINFORMATION);
hWnd=NULL; // Set hWnd To NULL
}
if (!UnregisterClass("OpenGL",hInstance)) // Are We Able To Unregister Class
{
MessageBox(NULL,"Could Not Unregister Class.","SHUTDOWN ERROR",MB_OK | MB_ICONINFORMATION);
hInstance=NULL; // Set hInstance To NULL
}
}
/* This Code Creates Our OpenGL Window. Parameters Are: *
* title - Title To Appear At The Top Of The Window *
* width - Width Of The GL Window Or Fullscreen Mode *
* height - Height Of The GL Window Or Fullscreen Mode *
* bits - Number Of Bits To Use For Color (8/16/24/32) *
* fullscreenflag - Use Fullscreen Mode (TRUE) Or Windowed Mode (FALSE) */
BOOL CreateGLWindow(char* title, int width, int height, int bits, bool fullscreenflag)
{
GLuint PixelFormat; // Holds The Results After Searching For A Match
WNDCLASS wc; // Windows Class Structure
DWORD dwExStyle; // Window Extended Style
DWORD dwStyle; // Window Style
RECT WindowRect; // Grabs Rectangle Upper Left / Lower Right Values
WindowRect.left=(long)0; // Set Left Value To 0
WindowRect.right=(long)width; // Set Right Value To Requested Width
WindowRect.top=(long)0; // Set Top Value To 0
WindowRect.bottom=(long)height; // Set Bottom Value To Requested Height
fullscreen=fullscreenflag; // Set The Global Fullscreen Flag
hInstance = GetModuleHandle(NULL); // Grab An Instance For Our Window
wc.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC; // Redraw On Size, And Own DC For Window.
wc.lpfnWndProc = (WNDPROC) WndProc; // WndProc Handles Messages
wc.cbClsExtra = 0; // No Extra Window Data
wc.cbWndExtra = 0; // No Extra Window Data
wc.hInstance = hInstance; // Set The Instance
wc.hIcon = LoadIcon(NULL, IDI_WINLOGO); // Load The Default Icon
wc.hCursor = LoadCursor(NULL, IDC_ARROW); // Load The Arrow Pointer
wc.hbrBackground = NULL; // No Background Required For GL
wc.lpszMenuName = NULL; // We Don't Want A Menu
wc.lpszClassName = "OpenGL"; // Set The Class Name
if (!RegisterClass(&wc)) // Attempt To Register The Window Class
{
MessageBox(NULL,"Failed To Register The Window Class.","ERROR",MB_OK|MB_ICONEXCLAMATION);
return FALSE; // Return FALSE
}
if (fullscreen) // Attempt Fullscreen Mode?
{
DEVMODE dmScreenSettings; // Device Mode
memset(&dmScreenSettings,0,sizeof(dmScreenSettings)); // Makes Sure Memory's Cleared
dmScreenSettings.dmSize=sizeof(dmScreenSettings); // Size Of The Devmode Structure
dmScreenSettings.dmPelsWidth = width; // Selected Screen Width
dmScreenSettings.dmPelsHeight = height; // Selected Screen Height
dmScreenSettings.dmBitsPerPel = bits; // Selected Bits Per Pixel
dmScreenSettings.dmFields=DM_BITSPERPEL|DM_PELSWIDTH|DM_PELSHEIGHT;
// Try To Set Selected Mode And Get Results. NOTE: CDS_FULLSCREEN Gets Rid Of Start Bar.
if (ChangeDisplaySettings(&dmScreenSettings,CDS_FULLSCREEN)!=DISP_CHANGE_SUCCESSFUL)
{
// If The Mode Fails, Offer Two Options. Quit Or Use Windowed Mode.
if (MessageBox(NULL,"The Requested Fullscreen Mode Is Not Supported By\nYour Video Card. Use Windowed Mode Instead?","NeHe GL",MB_YESNO|MB_ICONEXCLAMATION)==IDYES)
{
fullscreen=FALSE; // Windowed Mode Selected. Fullscreen = FALSE
}
else
{
// Pop Up A Message Box Letting User Know The Program Is Closing.
MessageBox(NULL,"Program Will Now Close.","ERROR",MB_OK|MB_ICONSTOP);
return FALSE; // Return FALSE
}
}
}
if (fullscreen) // Are We Still In Fullscreen Mode?
{
dwExStyle=WS_EX_APPWINDOW; // Window Extended Style
dwStyle=WS_POPUP; // Windows Style
ShowCursor(FALSE); // Hide Mouse Pointer
}
else
{
dwExStyle=WS_EX_APPWINDOW | WS_EX_WINDOWEDGE; // Window Extended Style
dwStyle=WS_OVERLAPPEDWINDOW; // Windows Style
}
AdjustWindowRectEx(&WindowRect, dwStyle, FALSE, dwExStyle); // Adjust Window To True Requested Size
// Create The Window
if (!(hWnd=CreateWindowEx( dwExStyle, // Extended Style For The Window
"OpenGL", // Class Name
title, // Window Title
dwStyle | // Defined Window Style
WS_CLIPSIBLINGS | // Required Window Style
WS_CLIPCHILDREN, // Required Window Style
0, 0, // Window Position
WindowRect.right-WindowRect.left, // Calculate Window Width
WindowRect.bottom-WindowRect.top, // Calculate Window Height
NULL, // No Parent Window
NULL, // No Menu
hInstance, // Instance
NULL))) // Dont Pass Anything To WM_CREATE
{
KillGLWindow(); // Reset The Display
MessageBox(NULL,"Window Creation Error.","ERROR",MB_OK|MB_ICONEXCLAMATION);
return FALSE; // Return FALSE
}
static PIXELFORMATDESCRIPTOR pfd= // pfd Tells Windows How We Want Things To Be
{
sizeof(PIXELFORMATDESCRIPTOR), // Size Of This Pixel Format Descriptor
1, // Version Number
PFD_DRAW_TO_WINDOW | // Format Must Support Window
PFD_SUPPORT_OPENGL | // Format Must Support OpenGL
PFD_DOUBLEBUFFER, // Must Support Double Buffering
PFD_TYPE_RGBA, // Request An RGBA Format
bits, // Select Our Color Depth
0, 0, 0, 0, 0, 0, // Color Bits Ignored
0, // No Alpha Buffer
0, // Shift Bit Ignored
0, // No Accumulation Buffer
0, 0, 0, 0, // Accumulation Bits Ignored
16, // 16Bit Z-Buffer (Depth Buffer)
0, // No Stencil Buffer
0, // No Auxiliary Buffer
PFD_MAIN_PLANE, // Main Drawing Layer
0, // Reserved
0, 0, 0 // Layer Masks Ignored
};
if (!(hDC=GetDC(hWnd))) // Did We Get A Device Context?
{
KillGLWindow(); // Reset The Display
MessageBox(NULL,"Can't Create A GL Device Context.","ERROR",MB_OK|MB_ICONEXCLAMATION);
return FALSE; // Return FALSE
}
if (!(PixelFormat=ChoosePixelFormat(hDC,&pfd))) // Did Windows Find A Matching Pixel Format?
{
KillGLWindow(); // Reset The Display
MessageBox(NULL,"Can't Find A Suitable PixelFormat.","ERROR",MB_OK|MB_ICONEXCLAMATION);
return FALSE; // Return FALSE
}
if(!SetPixelFormat(hDC,PixelFormat,&pfd)) // Are We Able To Set The Pixel Format?
{
KillGLWindow(); // Reset The Display
MessageBox(NULL,"Can't Set The PixelFormat.","ERROR",MB_OK|MB_ICONEXCLAMATION);
return FALSE; // Return FALSE
}
if (!(hRC=wglCreateContext(hDC))) // Are We Able To Get A Rendering Context?
{
KillGLWindow(); // Reset The Display
MessageBox(NULL,"Can't Create A GL Rendering Context.","ERROR",MB_OK|MB_ICONEXCLAMATION);
return FALSE; // Return FALSE
}
if(!wglMakeCurrent(hDC,hRC)) // Try To Activate The Rendering Context
{
KillGLWindow(); // Reset The Display
MessageBox(NULL,"Can't Activate The GL Rendering Context.","ERROR",MB_OK|MB_ICONEXCLAMATION);
return FALSE; // Return FALSE
}
ShowWindow(hWnd,SW_SHOW); // Show The Window
SetForegroundWindow(hWnd); // Slightly Higher Priority
SetFocus(hWnd); // Sets Keyboard Focus To The Window
ReSizeGLScene(width, height); // Set Up Our Perspective GL Screen
if (!InitGL()) // Initialize Our Newly Created GL Window
{
KillGLWindow(); // Reset The Display
MessageBox(NULL,"Initialization Failed.","ERROR",MB_OK|MB_ICONEXCLAMATION);
return FALSE; // Return FALSE
}
return TRUE; // Success
}
LRESULT CALLBACK WndProc( HWND hWnd, // Handle For This Window
UINT uMsg, // Message For This Window
WPARAM wParam, // Additional Message Information
LPARAM lParam) // Additional Message Information
{
switch (uMsg) // Check For Windows Messages
{
case WM_ACTIVATE: // Watch For Window Activate Message
{
if (!HIWORD(wParam)) // Check Minimization State
{
active=TRUE; // Program Is Active
}
else
{
active=FALSE; // Program Is No Longer Active
}
return 0; // Return To The Message Loop
}
case WM_SYSCOMMAND: // Intercept System Commands
{
switch (wParam) // Check System Calls
{
case SC_SCREENSAVE: // Screensaver Trying To Start?
case SC_MONITORPOWER: // Monitor Trying To Enter Powersave?
return 0; // Prevent From Happening
}
break; // Exit
}
case WM_CLOSE: // Did We Receive A Close Message?
{
PostQuitMessage(0); // Send A Quit Message
return 0; // Jump Back
}
case WM_KEYDOWN: // Is A Key Being Held Down?
{
keys[wParam] = TRUE; // If So, Mark It As TRUE
return 0; // Jump Back
}
case WM_KEYUP: // Has A Key Been Released?
{
keys[wParam] = FALSE; // If So, Mark It As FALSE
return 0; // Jump Back
}
case WM_SIZE: // Resize The OpenGL Window
{
ReSizeGLScene(LOWORD(lParam),HIWORD(lParam)); // LoWord=Width, HiWord=Height
return 0; // Jump Back
}
}
// Pass All Unhandled Messages To DefWindowProc
return DefWindowProc(hWnd,uMsg,wParam,lParam);
}
int WINAPI WinMain( HINSTANCE hInstance, // Instance
HINSTANCE hPrevInstance, // Previous Instance
LPSTR lpCmdLine, // Command Line Parameters
int nCmdShow) // Window Show State
{
MSG msg; // Windows Message Structure
BOOL done=FALSE; // Bool Variable To Exit Loop
// Ask The User Which Screen Mode They Prefer
if (MessageBox(NULL,"Would You Like To Run In Fullscreen Mode?", "Start FullScreen?",MB_YESNO|MB_ICONQUESTION)==IDNO)
{
fullscreen=FALSE; // Windowed Mode
}
// Create Our OpenGL Window
if (!CreateGLWindow("NeHe's OpenGL Framework",640,480,16,fullscreen))
{
return 0; // Quit If Window Was Not Created
}
while(!done) // Loop That Runs While done=FALSE
{
if (PeekMessage(&msg,NULL,0,0,PM_REMOVE)) // Is There A Message Waiting?
{
if (msg.message==WM_QUIT) // Have We Received A Quit Message?
{
done=TRUE; // If So done=TRUE
}
else // If Not, Deal With Window Messages
{
TranslateMessage(&msg); // Translate The Message
DispatchMessage(&msg); // Dispatch The Message
}
}
else // If There Are No Messages
{
// Draw The Scene. Watch For ESC Key And Quit Messages From DrawGLScene()
if (active) // Program Active?
{
if (keys[VK_ESCAPE]) // Was ESC Pressed?
{
done=TRUE; // ESC Signalled A Quit
}
else // Not Time To Quit, Update Screen
{
DrawGLScene(); // Draw The Scene
SwapBuffers(hDC); // Swap Buffers (Double Buffering)
}
}
if (keys[VK_F1]) // Is F1 Being Pressed?
{
keys[VK_F1]=FALSE; // If So Make Key FALSE
KillGLWindow(); // Kill Our Current Window
fullscreen=!fullscreen; // Toggle Fullscreen / Windowed Mode
// Recreate Our OpenGL Window
if (!CreateGLWindow("NeHe's OpenGL Framework",640,480,16,fullscreen))
{
return 0; // Quit If Window Was Not Created
}
}
}
}
// Shutdown
KillGLWindow(); // Kill The Window
return (msg.wParam); // Exit The Program
}
If you really, really want to do it yourself, you can still have a look at how GLFW does it. In its source directory, you have a win32-specific directory, in which you have a windowing-specic .c file. You should find everything you need in this file.
This page of the OpenGL Wiki could help you, too.
The best way is NOT to do it yourself. OpenGL context creation is a bottomless abyss of complexity. You have lots of tools that can do the dirty work for you, and what's more, it'll work on Linux and Mac too
GLFW, FreeGlut, SDL, SFML are probably the most common.
See GLFW's user guide, for instance, to see how easy these libs make it to create a window :
glfwInit();
glfwOpenWindow( 300,300, 0,0,0,0,0,0, GLFW_WINDOW );
// bam, you're done