-1
$\begingroup$

I am following this tutorial on how to host Win32 OpenGL windows in WPF. There is also a Microsoft walkthrough on how to Host a Win32 Control in WPF that is useful aswell. The original project is from 2009 and uses managed C++ but doesn't actually render. I have rebuilt the program using the same source code (a few modifications due to debugging) using a C++ CLR Class Library. I use the compiled DLL in C# WPF however, when I run the program I get a blank window as opposed to the triangle in the tutorial.

I was wondering if someone could take a look at my program and see why nothing is rendering.

What the original program renders:

enter image description here

What I am getting:

enter image description here

Here is a link to the source on GitHub.

OpenGLCpp.h

#pragma once

// Exclude rarely used parts of the windows headers
#define WIN32_LEAN_AND_MEAN

#include <Windows.h>

#include "Helper.h"
#include "OpenGL.h"

// To use these, we must add some references...
//  o PresentationFramework (for HwndHost)
//      * PresentationCore
//      * WindowsBase
using namespace System;
using namespace System::Windows;
using namespace System::Windows::Interop;
using namespace System::Windows::Input;
using namespace System::Windows::Media;
using namespace System::Runtime::InteropServices;

namespace OpenGLCpp 
{
    LRESULT WINAPI MyMsgProc(HWND _hWnd, UINT _msg, WPARAM _wParam, LPARAM _lParam)
    {
        switch (_msg)
        {
            // Make sure the window gets focus when it has to!
        case WM_IME_SETCONTEXT:
            // LOWORD(wParam) = 0 stands for deactivation, so don't set
            // focus then (results in a rather, err... 'greedy' window...)
            if (LOWORD(_wParam) > 0)
                SetFocus(_hWnd);

            return 0;

        default:
            return DefWindowProc(_hWnd, _msg, _wParam, _lParam);
        }
    }

    //
    // This class implements HwndHost
    //
    // We have to overwrite BuildWindowCore and DestroyWindowCore
    //
    // A very simple example which creates a Win32 ListBox can be found in the MSDN:
    // http://msdn2.microsoft.com/en-us/library/aa970061.aspx
    //
    public ref class OpenGLHwnd : public HwndHost
    {
    public:
        OpenGLHwnd() : m_hRC(NULL),
            m_hDC(NULL),
            m_hWnd(NULL),
            m_hInstance(NULL),
            m_sWindowName(NULL),
            m_sClassName(NULL),
            m_fRotationAngle(0.0f)
        {
        }

    private:
        HGLRC                   m_hRC;
        HDC                     m_hDC;
        HWND                    m_hWnd;
        HINSTANCE               m_hInstance;
        LPCWSTR                 m_sWindowName;
        LPCWSTR                 m_sClassName;

        float                   m_fRotationAngle;

        // DPI Scaling
        double                  m_dScaleX;
        double                  m_dScaleY;

    protected:
        /*virtual IntPtr WndProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, bool% handled) override
        {
            switch( msg )
            {
                case WM_IME_SETCONTEXT:
                    if(LOWORD(wParam.ToInt32()) > 0)
                        SetFocus(m_hWnd);

                    handled = true;
                    return System::IntPtr::Zero;
            }

            handled = false;
            return System::IntPtr::Zero;
        }*/
    public:
        virtual void OnRenderSizeChanged(SizeChangedInfo^ sizeInfo) override
        {
            if (m_hDC == NULL || m_hRC == NULL)
                return;

            // Apply DPI correction
            // NOTE: sizeInfo->NewSize contains doubles, so we do the multiplication before
            // converting to int.
            int iHeight = (int)(sizeInfo->NewSize.Height * m_dScaleY);
            int iWidth = (int)(sizeInfo->NewSize.Width * m_dScaleX);

            if (iWidth == 0 || iHeight == 0)
                return;

            wglMakeCurrent(m_hDC, m_hRC);
            glViewport(0, 0, iWidth, iHeight);

            glMatrixMode(GL_PROJECTION);
            glLoadIdentity();
            glOrtho(-1.0, 1.0, -1.0, 1.0, 1.0, 100.0);
            // gluPerspective( 67.5, ((double)(iWidth) / (double)(iHeight)), 1.0, 500.0);
            glMatrixMode(GL_MODELVIEW);
            glLoadIdentity();
        }

        //
        // Communicating with this method will be considerably more complicated in a real-
        // world application...
        //
        virtual void OnRender(System::Windows::Media::DrawingContext^ drawingContext) override
        {
            UNREF(drawingContext);

            if (m_hDC == NULL || m_hRC == NULL)
                return;

            wglMakeCurrent(m_hDC, m_hRC);

            glClearColor(0.0f, 0.0f, 0.0f, 0.0f);

            glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
            glLoadIdentity();

            glTranslatef(0.0f, 0.0f, -2.6f);
            glRotatef(m_fRotationAngle, 0.0f, 1.0f, 0.0f);

            glBegin(GL_TRIANGLES);
            glColor3f(1.0f, 0.0f, 0.0f);    glVertex3f(-1.0f, -1.0f, 0.0f);
            glColor3f(0.0f, 1.0f, 0.0f);    glVertex3f(0.0f, 1.0f, 0.0f);
            glColor3f(0.0f, 0.0f, 1.0f);    glVertex3f(1.0f, -1.0f, 0.0f);
            glEnd();

            glFlush();
            SwapBuffers(m_hDC); // NOTE: This is no longer wglSwapBuffers

            // For constant rotation speed, and correct rotation speeds on different machines,
            // we'd need a timer here. However, I don't want to bloat the example code.
            m_fRotationAngle += 1.0;
        }

        virtual void DestroyWindowCore(HandleRef hwnd) override
        {
            if (NULL != m_hRC)
            {
                wglDeleteContext(m_hRC);
                m_hRC = NULL;
            }

            if (NULL != m_hWnd && NULL != m_hDC)
            {
                ReleaseDC(m_hWnd, m_hDC);
                m_hDC = NULL;
            }

            if (NULL != m_hWnd && m_hWnd == (HWND)hwnd.Handle.ToPointer())
            {
                ::DestroyWindow(m_hWnd);
                m_hWnd = NULL;
            }

            UnregisterClass(m_sClassName, m_hInstance);
        }

        bool RegisterWindowClass()
        {
            //
            // Create custom WNDCLASS
            //
            WNDCLASS wndClass;

            if (GetClassInfo(m_hInstance, m_sClassName, &wndClass))
            {
                // Class is already registered!
                return true;
            }

            wndClass.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC;

            // Not providing a WNDPROC here results in a crash on my system:
            wndClass.lpfnWndProc = (WNDPROC)MyMsgProc;
            wndClass.cbClsExtra = 0;
            wndClass.cbWndExtra = 0;
            wndClass.hInstance = m_hInstance;
            wndClass.hIcon = LoadIcon(NULL, IDI_WINLOGO);
            wndClass.hCursor = LoadCursor(0, IDC_ARROW);
            wndClass.hbrBackground = 0;
            wndClass.lpszMenuName = 0; // No menu
            wndClass.lpszClassName = m_sClassName;

            // ... and register it
            if (!RegisterClass(&wndClass))
            {
                Helper::ErrorExit(L"RegisterWindowClass");
                return false;
            }

            return true;
        }

        //
        // This is the key method to override
        //
        virtual HandleRef BuildWindowCore(HandleRef hwndParent) override
        {
            // Get HINSTANCE
            m_hInstance = (HINSTANCE)GetModuleHandle(NULL);
            m_sWindowName = L"OpenGL in HwndHost";
            m_sClassName = L"OGLClassHwnd";

            if (RegisterWindowClass())
            {
                // some default size
                int iWidth = 2;
                int iHeight = 2;

                DWORD dwStyle = WS_CHILD | WS_VISIBLE;

                // Get the parent (WPF) Hwnd. This is important: Windows won't let you create
                // the Hwnd otherwise.
                HWND parentHwnd = (HWND)hwndParent.Handle.ToPointer();

                m_hWnd = CreateWindowEx(0,
                    m_sClassName,
                    m_sWindowName,
                    dwStyle,
                    0,  // X Pos
                    0,  // Y Pos
                    iWidth,
                    iHeight,
                    parentHwnd, // Parent
                    0,  // Menu
                    m_hInstance,
                    0); // Param

                if (!m_hWnd)
                {
                    Helper::ErrorExit(L"BuildWindowCore");
                }

                m_hDC = GetDC(m_hWnd);
                if (!m_hDC)
                {
                    Helper::ErrorExit(L"BuildWindowCore");
                }

                //
                // Create PixelFormatDescriptor and choose pixel format
                //
                uint PixelFormat;

                BYTE iAlphaBits = 0;
                BYTE iColorBits = 32;
                BYTE iDepthBits = 16;
                BYTE iAccumBits = 0;
                BYTE iStencilBits = 0;

                static PIXELFORMATDESCRIPTOR pfd =
                {
                    sizeof(PIXELFORMATDESCRIPTOR),  //size
                    1,                              //version
                    PFD_DRAW_TO_WINDOW |                //flags
                    PFD_SUPPORT_OPENGL |
                    PFD_DOUBLEBUFFER,
                    PFD_TYPE_RGBA,                  //pixeltype
                    iColorBits,
                    0, 0, 0, 0, 0, 0,               //color bits ignored
                    iAlphaBits,
                    0,                              //alpha shift ignored
                    iAccumBits,                     //accum. buffer
                    0, 0, 0, 0,                     //accum bits ignored
                    iDepthBits,                     //depth buffer
                    iStencilBits,                   //stencil buffer
                    0,                              //aux buffer
                    PFD_MAIN_PLANE,                 //layer type
                    0,                              //reserved
                    0, 0, 0                         //masks ignored
                };

                PixelFormat = ChoosePixelFormat(m_hDC, &pfd);
                if (!PixelFormat)
                {
                    Helper::ErrorExit(L"BuildWindowCore");
                }

                if (!SetPixelFormat(m_hDC, PixelFormat, &pfd))
                {
                    Helper::ErrorExit(L"BuildWindowCore");
                }

                m_hRC = wglCreateContext(m_hDC);
                if (!m_hRC)
                {
                    Helper::ErrorExit(L"BuildWindowCore");
                }

                if (!wglMakeCurrent(m_hDC, m_hRC))
                {
                    Helper::ErrorExit(L"BuildWindowCore");
                }

                //
                // Get DPI information and store scaling relative to 96 DPI.
                // See http://msdn2.microsoft.com/en-us/library/ms969894.aspx
                //
                m_dScaleX = GetDeviceCaps(m_hDC, LOGPIXELSX) / 96.0;
                m_dScaleY = GetDeviceCaps(m_hDC, LOGPIXELSY) / 96.0;

                glEnable(GL_DEPTH_TEST);
                glDisable(GL_TEXTURE_2D);

                return HandleRef(this, IntPtr(m_hWnd));
            }

            return HandleRef(nullptr, System::IntPtr::Zero);
        }
    };
}

OpenGLHWND.xaml.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Shapes;

using System.Windows.Interop; //HwndHost
using OpenGLCpp;

namespace OpenGLinWPF
{
    /// <summary>
    /// Interaction logic for OpenGLHWND.xaml
    /// </summary>
    public partial class OpenGLHWND : Window
    {
        public OpenGLHWND()
        {
            InitializeComponent();
        }

        private System.Windows.Threading.DispatcherTimer updateTimer = new System.Windows.Threading.DispatcherTimer();

        public override void BeginInit()
        {
            updateTimer.Interval = new TimeSpan(160000);
            updateTimer.Tick += new EventHandler(updateTimer_Tick);
            updateTimer.Start();
            base.BeginInit();
        }

        protected override void OnClosing(System.ComponentModel.CancelEventArgs e)
        {
            base.OnClosing(e);

            if (!e.Cancel)
            {
                if (null != updateTimer)
                {
                    updateTimer.Stop();
                    updateTimer = null;
                }
            }
        }

        private void updateTimer_Tick(object sender, EventArgs e)
        {
            if (null != hwndPlaceholder &&
                null != hwndPlaceholder.Child)
            {
                hwndPlaceholder.Child.InvalidateVisual();
            }
        }

        private void Window_Loaded(object sender, RoutedEventArgs e)
        {
            // Create our OpenGL Hwnd 'control'...
            HwndHost host = new OpenGLCpp.OpenGLHwnd(); 

            // ... and attach it to the placeholder control:
            hwndPlaceholder.Child = host;
        }

    }
}
$\endgroup$

1 Answer 1

0
$\begingroup$

I did not set Loaded="Window_Loaded" in

<Window x:Class="OpenGLinWPF.OpenGLHWND"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:OpenGLinWPF"
        mc:Ignorable="d"
        Title="OpenGLHWND" Height="450" Width="800"
        Loaded="Window_Loaded">
    <Grid>
        <Border Name="hwndPlaceholder" />
    </Grid>
</Window>

The description of the Window.Loaded event here says

Occurs when the element is laid out, rendered, and ready for interaction.

After setting it I get:

enter image description here

$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.