首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >在OpenGL WPF中托管C++窗口

在OpenGL WPF中托管C++窗口
EN

Stack Overflow用户
提问于 2022-06-19 18:03:20
回答 2查看 752关注 0票数 1

我正在跟踪这个关于如何在WPF中托管Win32 OpenGL窗口的Win32。还有一个关于如何使用在WPF中托管Win32控件的Microsoft演练,它也很有用。最初的项目是从2009年开始的,使用托管C++,但实际上没有呈现。我使用相同的源代码(调试后的一些修改)使用C++ CLR类库重新构建了程序。但是,我在C# WPF中使用编译后的DLL,但是当我运行程序时,我会得到一个空白窗口,而不是教程中的三角形。

我想知道是否有人能看看我的程序,看看为什么什么都没有呈现。

原始程序呈现的内容:

我得到的是:

下面是一个链接,指向GitHub上的源代码。

OpenGLCpp.h

代码语言:javascript
运行
复制
#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

代码语言:javascript
运行
复制
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;
        }

    }
}
EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2022-06-20 17:01:02

我没有设置Loaded="Window_Loaded"

代码语言:javascript
运行
复制
<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>

这里对Window.Loaded事件的描述是

当元素被布局、呈现并准备好进行交互时发生。

在设置之后,我得到:

票数 1
EN

Stack Overflow用户

发布于 2022-06-20 16:07:41

我最近做过类似的事情(用DX11)。帮助我的是将代码分割成较小的块,并看到缺少什么(我的猜测是HWND不正确地传递/返回)。不过,我建议这样做:

  1. 使C++以独立程序的形式工作,并具有正确的呈现
  2. 正确获取C# -> C++链接(从C#到C++代码调用的简单返回函数就足够了)
  3. 将步骤1中的独立窗口(带有呈现)打开为C#中的独立窗口
  4. 将C++窗口输入到C#控件。

至于HWND -在C#中,您可以简单地使用

代码语言:javascript
运行
复制
    protected override HandleRef BuildWindowCore(HandleRef hwndParent)
    {
        _renderWindowHandle = EngineDLLAPI.BuildWindowCore(hwndParent.Handle,  hostWidth, hostHeight, hostIndex);
        
        if(_renderWindowHandle != IntPtr.Zero)
        {
            return new HandleRef(this, _renderWindowHandle);
        }
        else
        {
            throw new NotImplementedException();
        }
    }

在C++中,您可以简单地返回HWND -即从您创建的C++窗口返回的m_hWnd。

附注:还请检查是否实际启动了呈现循环:)

票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/72679201

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档