我正在学习如何在WPF中托管Win32 OpenGL窗口的tutorial。还有一个关于如何在WPF中托管Win32 OpenGL窗口的微软演练,也很有用。原始项目来自2009年,使用托管C++,但实际上并不渲染。我已经使用相同的源代码重建了程序(由于调试而做了一些修改)使用C++ CLR类库。我使用C# WPF中编译的DLL然而,当我运行程序时,我得到一个空白窗口,而不是教程中的三角形。
我想知道是否有人可以看看我的程序,看看为什么没有渲染。
原始程序呈现的内容:
我得到的是:
这是一个link到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;
}
}
}
2条答案
按热度按时间apeeds0o1#
我最近做过类似的事情(用DX11)。帮助我的是把代码分成更小的块,看看遗漏了什么(我猜是HWND错误地传递/返回)。我建议使用这种方法:
1.使C作为独立程序工作并具有正确呈现
1.正确获取C# -〉C链接(从C#到C代码调用简单返回函数就足够了)
1.从C#中将步骤#1中的独立窗口(带有渲染)作为独立窗口打开
1.将C窗口输入到C#控件。
至于HWND -在C#中,您只需使用
而在C中,您只需从创建的C窗口返回HWND -即m_hWnd。
另外还要检查你是否真的开始了渲染循环:)
xxslljrj2#
我没有设置
Loaded="Window_Loaded"
Window.Loaded事件的描述为
在元素已布局、呈现并准备好进行交互时发生。
设置后,我得到: