Skip to content

Instantly share code, notes, and snippets.

@trevorlinton
Last active August 29, 2015 14:13
Show Gist options
  • Save trevorlinton/293f8d3de742ed7054e4 to your computer and use it in GitHub Desktop.
Save trevorlinton/293f8d3de742ed7054e4 to your computer and use it in GitHub Desktop.
Rendering Content over WPF WebControl
#include <stdint.h>
#include <stdio.h>
#include <string>
#include <vcclr.h>
#include <windows.h>
#include <mmsystem.h>
#include <msclr/marshal.h>
#include <msclr/marshal_cppstd.h>
#include <d3d9.h>
#using <System.dll>
#using <System.Core.dll>
#using <PresentationCore.dll>
#using <PresentationFramework.dll>
#using <WindowsBase.dll>
using namespace std;
using namespace Microsoft::Win32;
using namespace System;
using namespace System::Windows;
using namespace System::Windows::Controls;
using namespace System::Windows::Interop;
using namespace System::Windows::Media;
using namespace System::Windows::Media::Imaging;
#define TIMER_MILLISECONDS 17
#define FRAME_RATE_ENABLED
struct D3DData {
LPDIRECT3DVERTEXBUFFER9 vertex;
IDirect3DDevice9Ex* d3ddev;
IDirect3D9Ex* d3d9;
IDirect3DSurface9* dest;
};
public ref class WebBrowserEx {
public:
WebBrowserEx() {
SetBrowserFeatureControl();
this->app = gcnew Application();
this->mWnd = gcnew Window();
this->brwsr = gcnew WebBrowser();
this->img = gcnew Image();
Grid^ grid = gcnew Grid();
RowDefinition^ rd = gcnew RowDefinition();
rd->Height = GridLength(1, GridUnitType::Star);
RowDefinition^ rd2 = gcnew RowDefinition();
rd2->Height = GridLength(1, GridUnitType::Star);
grid->RowDefinitions->Add(rd);
grid->RowDefinitions->Add(rd2);
grid->Children->Add(this->brwsr);
grid->SetRow(this->brwsr, 0);
// start delete here
Canvas^ canvas = gcnew Canvas();
canvas->Children->Add(this->img);
img->SetValue(Canvas::LeftProperty,(double)1);
img->SetValue(Canvas::TopProperty,(double)1);
img->Width = 500;
img->Height = 200;
grid->Children->Add(canvas);
grid->SetRow(canvas, 1);
Button^ button = gcnew Button();
button->Width = 100;
button->Height = 30;
button->Content = "HEllo";
grid->Children->Add(button);
grid->SetRow(button, 1);
//end delete here
//grid->Children->Add(this->img);
//grid->SetRow(this->img, 1);
this->mWnd->Content = grid;
this->img->Stretch = Stretch::None;
}
void Run(bool useDirectX) {
renderModeDirectX = useDirectX;
this->mWnd->SizeChanged += gcnew SizeChangedEventHandler(this, &WebBrowserEx::SizeChanged);
this->brwsr->Loaded += gcnew RoutedEventHandler(this, &WebBrowserEx::Loaded);
this->brwsr->Navigate("http://ie.microsoft.com/testdrive/Performance/FishIETank/");
this->app->Run(this->mWnd);
}
private:
void SetBrowserFeatureControlKey(String^ feature, String^ appName, int value) {
String^ featuresPath = "HKEY_CURRENT_USER\\Software\\Microsoft\\Internet Explorer\\Main\\FeatureControl\\";
String^ path = featuresPath + feature;
Microsoft::Win32::Registry::SetValue(path, appName, value, Microsoft::Win32::RegistryValueKind::DWord);
}
void SetBrowserFeatureControl() {
System::Diagnostics::Process^ process = System::Diagnostics::Process::GetCurrentProcess();
String^ pName = process->Modules[0]->FileName;
array<String^>^ path = pName->Split('\\');
String^ fileName = path[path->Length-1];
SetBrowserFeatureControlKey("FEATURE_96DPI_PIXEL", fileName, 1); // enable high-dpi support.
SetBrowserFeatureControlKey("FEATURE_BROWSER_EMULATION", fileName, 00000); // turn off compatibility mode.
SetBrowserFeatureControlKey("FEATURE_AJAX_CONNECTIONEVENTS", fileName, 1);
SetBrowserFeatureControlKey("FEATURE_ENABLE_CLIPCHILDREN_OPTIMIZATION", fileName, 1);
SetBrowserFeatureControlKey("FEATURE_GPU_RENDERING", fileName, 1); // use GPU rendering
SetBrowserFeatureControlKey("FEATURE_IVIEWOBJECTDRAW_DMLT9_WITH_GDI ", fileName, 0); // force directX
SetBrowserFeatureControlKey("FEATURE_NINPUT_LEGACYMODE", fileName, 0);
SetBrowserFeatureControlKey("FEATURE_DISABLE_NAVIGATION_SOUNDS", fileName, 1);
SetBrowserFeatureControlKey("FEATURE_SCRIPTURL_MITIGATION", fileName, 1);
SetBrowserFeatureControlKey("FEATURE_SPELLCHECKING", fileName, 0);
SetBrowserFeatureControlKey("FEATURE_STATUS_BAR_THROTTLING", fileName, 1);
SetBrowserFeatureControlKey("FEATURE_VALIDATE_NAVIGATE_URL", fileName, 1);
SetBrowserFeatureControlKey("FEATURE_WEBOC_DOCUMENT_ZOOM", fileName, 1); // allow zoom.
SetBrowserFeatureControlKey("FEATURE_WEBOC_POPUPMANAGEMENT", fileName, 0); // disallow auto-popups
SetBrowserFeatureControlKey("FEATURE_ADDON_MANAGEMENT", fileName, 0); // disallow auto-addons/plugins
SetBrowserFeatureControlKey("FEATURE_WEBSOCKET", fileName, 1);
SetBrowserFeatureControlKey("FEATURE_WINDOW_RESTRICTIONS", fileName, 0); // disallow popups
SetBrowserFeatureControlKey("FEATURE_SECURITYBAND", fileName, 0); // disallow security band (still retains security)
SetBrowserFeatureControlKey("FEATURE_LOCALMACHINE_LOCKDOWN", fileName, 1); // allow file's to integrate with IWebBrowser JS execute.
SetBrowserFeatureControlKey("FEATURE_BLOCK_LMZ_SCRIPT", fileName, 0); // disable activeX security band warnings on local scripts.
SetBrowserFeatureControlKey("FEATURE_BLOCK_LMZ_OBJECT", fileName, 0); // disable activeX security.
SetBrowserFeatureControlKey("FEATURE_RESTRICT_ACTIVEXINSTALL", fileName, 0);
SetBrowserFeatureControlKey("FEATURE_PROTOCOL_LOCKDOWN", fileName, 0);
SetBrowserFeatureControlKey("FEATURE_ZONE_ELEVATION", fileName, 0);
SetBrowserFeatureControlKey("FEATURE_SCRIPTURL_MITIGATION", fileName, 0);
}
void SizeChanged(System::Object ^sender, SizeChangedEventArgs ^args) {
Width = (int)this->brwsr->ActualWidth;
Height = (int)this->brwsr->ActualHeight;
DpiWidth = (int)this->brwsr->ActualWidth*dpiX;
DpiHeight = (int)this->brwsr->ActualHeight*dpiY;
if(!renderModeDirectX) {
if(hBitmap != NULL) {
DeleteObject(hBitmap);
}
hBitmap = CreateCompatibleBitmap(hdcFrom, DpiWidth, DpiHeight);
}
}
#ifdef FRAME_RATE_ENABLED
void PrintFrameRate(Object^ sender, EventArgs^ e) {
Console::WriteLine("FPS: "+frameRate);
frameRate = 0;
}
#endif
void Loaded(System::Object ^sender, RoutedEventArgs ^args) {
brwsrHwnd = (HWND)this->brwsr->Handle.ToPointer();
hdcFrom = GetDC(brwsrHwnd);
PresentationSource^ presSource = PresentationSource::FromVisual(this->mWnd);
dpiY = dpiX = (presSource == nullptr) ? 1.0 : presSource->CompositionTarget->TransformToDevice.M11;
DpiWidth = (int)this->brwsr->ActualWidth*dpiX;
DpiHeight = (int)this->brwsr->ActualHeight*dpiY;
Width = (int)this->brwsr->ActualWidth;
Height = (int)this->brwsr->ActualHeight;
#ifdef FRAME_RATE_ENABLED
frameRate = 0;
frameRateTimer = gcnew System::Windows::Threading::DispatcherTimer(System::Windows::Threading::DispatcherPriority::Normal);
frameRateTimer->Tick += gcnew EventHandler(this, &WebBrowserEx::PrintFrameRate);
frameRateTimer->Interval = TimeSpan(0,0,0,1,0);
frameRateTimer->Start();
#endif
if(renderModeDirectX) {
// render direct3D.
this->d3dimg = gcnew D3DImage(dpiX * 96.0, dpiY * 96.0);
this->d3dimg->IsFrontBufferAvailableChanged += gcnew DependencyPropertyChangedEventHandler(this, &WebBrowserEx::OnIsFrontBufferAvailableChanged);
InitializeD3D();
this->img->Source = this->d3dimg;
} else {
// GDI bitblt redirect, so slow...
hdcTo = CreateCompatibleDC(hdcFrom);
hBitmap = CreateCompatibleBitmap(hdcFrom, DpiWidth, DpiHeight);
SelectObject(hdcTo, hBitmap);
timer = gcnew System::Windows::Threading::DispatcherTimer(System::Windows::Threading::DispatcherPriority::Render);
timer->Tick += gcnew EventHandler(this, &WebBrowserEx::RedirectWindowWithGDI);
timer->Interval = TimeSpan(0,0,0,0,TIMER_MILLISECONDS);
timer->Start();
}
}
// 1. See what SelectObject does, if it can be removed -- done, sort of yes
// we need to reassign on each blit, but not back.
// THIS MAY BE THE SOURCE OF THE MEMORY LEAK. Somehow were leaking...
// 2. Assign hBitmap to existing back buffer?
void RedirectWindowWithGDI(Object^ sender, EventArgs^ e) {
if (hBitmap != NULL)
{
HGDIOBJ hLocal = SelectObject(hdcTo, hBitmap);
StretchBlt(hdcTo, 0, 0, DpiWidth, DpiHeight, hdcFrom, 0, 0, DpiWidth, DpiHeight, SRCCOPY);
SelectObject(hdcTo, hLocal);
//BitBlt(hdcTo, 0, 0, DpiWidth, DpiHeight, hdcFrom, 0, 0, SRCCOPY);
// HUGE "Temporary" MEMORY LEAK HERE, HILARIOUSLY HUGE. The GC doesn't
// recollect the old frame very quickly, unfortunately theres no way to force it too.
source = gcnew WriteableBitmap(System::Windows::Interop::Imaging::CreateBitmapSourceFromHBitmap(IntPtr(hBitmap),
IntPtr::Zero,
System::Windows::Int32Rect::Empty,
BitmapSizeOptions::FromEmptyOptions()));
this->img->Source = source;
#ifdef FRAME_RATE_ENABLED
frameRate++;
#endif
}
}
void OnIsFrontBufferAvailableChanged(Object^ sender, DependencyPropertyChangedEventArgs e)
{
if (this->d3dimg->IsFrontBufferAvailable) {
InitializeD3D();
} else {
DeinitializeD3D();
}
}
void OnD3DRendering(Object^ sender, EventArgs^ e)
{
if (this->d3dimg->IsFrontBufferAvailable)
{
// Try lock will begin to fail persistently, setting the timespan higher helps,
// but once it locks it seems unable to re-lock, levels tried were 2, and 22.
//this->d3dimg->TryLock(Duration(TimeSpan(22)))
this->d3dimg->Lock();
this->RenderD3D();
this->d3dimg->AddDirtyRect(Int32Rect(0, 0, this->d3dimg->PixelWidth, this->d3dimg->PixelHeight));
this->d3dimg->Unlock();
}
}
void RenderD3D() {
LPDIRECT3DDEVICE9 g_pd3dDevice = this->d3ddata->d3ddev;
IDirect3DSurface9* surface = this->d3ddata->dest;
//g_pd3dDevice->Clear(0, NULL, D3DCLEAR_TARGET, D3DCOLOR_ARGB(0, 0, 0, 0), 1.0f, 0);
if (SUCCEEDED(g_pd3dDevice->BeginScene()))
{
HDC surfaceDC;
surface->GetDC(&surfaceDC);
StretchBlt(surfaceDC, 0, 0, DpiWidth, DpiHeight, hdcFrom, 0, 0, DpiWidth, DpiHeight, SRCCOPY);
//BitBlt(surfaceDC, 0, 0, DpiWidth, DpiHeight, hdcFrom, 0, 0, SRCCOPY);
surface->ReleaseDC(surfaceDC);
g_pd3dDevice->EndScene();
#ifdef FRAME_RATE_ENABLED
frameRate++;
#endif
}
}
void DeinitializeD3D() {
timer->Stop();
if (this->d3ddata->d3ddev != NULL) {
this->d3ddata->d3ddev->Release();
}
if (this->d3ddata->d3d9 != NULL) {
this->d3ddata->d3d9->Release();
}
if (this->d3ddata->dest != NULL) {
this->d3ddata->dest->Release();
}
this->d3ddata = NULL;
}
void InitializeD3D() {
if(this->d3dimg->IsFrontBufferAvailable) {
this->d3ddata = new D3DData();
HWND hWnd = (HWND)(gcnew WindowInteropHelper(this->mWnd))->Handle.ToPointer();
if (FAILED(Direct3DCreate9Ex(D3D_SDK_VERSION, ((IDirect3D9Ex**)&(this->d3ddata->d3d9))))) {
Console::WriteLine("Direct3DCreate9Ex failed.");
abort();
}
if(FAILED(this->d3ddata->d3d9->QueryInterface(__uuidof(IDirect3D9), reinterpret_cast<void **>(&(this->d3ddata->d3d9))))) {
Console::WriteLine("D3D9->QueryInterface failed.");
abort();
}
D3DPRESENT_PARAMETERS d3dpp;
ZeroMemory(&d3dpp, sizeof(d3dpp));
d3dpp.Windowed = TRUE;
d3dpp.BackBufferHeight = 1;
d3dpp.BackBufferWidth = 1;
d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
d3dpp.BackBufferFormat = D3DFMT_A8R8G8B8;
if(FAILED(this->d3ddata->d3d9->CreateDeviceEx(D3DADAPTER_DEFAULT,
D3DDEVTYPE_HAL,
hWnd,
D3DCREATE_HARDWARE_VERTEXPROCESSING,
&d3dpp,
NULL,
&(this->d3ddata->d3ddev))))
{
Console::WriteLine("CreateDeviceEX failed.");
abort();
}
if(FAILED(this->d3ddata->d3ddev->QueryInterface(__uuidof(IDirect3DDevice9), reinterpret_cast<void **>(&(this->d3ddata->d3ddev)))))
{
Console::WriteLine("Device->QueryInterface failed.");
abort();
}
if(FAILED(this->d3ddata->d3ddev->CreateRenderTarget(1920*2,
1200*2,
D3DFMT_A8R8G8B8,
D3DMULTISAMPLE_NONE,
0,
true,
&(this->d3ddata->dest),
NULL)))
{
Console::WriteLine("Device->CreateRenderTarget failed.");
abort();
}
this->d3ddata->d3ddev->SetRenderTarget(0, this->d3ddata->dest);
this->d3ddata->d3ddev->SetRenderState(D3DRS_CULLMODE, D3DCULL_NONE);
this->d3ddata->d3ddev->SetRenderState(D3DRS_LIGHTING, FALSE);
this->d3dimg->Lock();
this->d3dimg->SetBackBuffer(System::Windows::Interop::D3DResourceType::IDirect3DSurface9, IntPtr(this->d3ddata->dest));
this->d3dimg->Unlock();
timer = gcnew System::Windows::Threading::DispatcherTimer(System::Windows::Threading::DispatcherPriority::Render);
timer->Tick += gcnew EventHandler(this, &WebBrowserEx::OnD3DRendering);
timer->Interval = TimeSpan(0,0,0,0,TIMER_MILLISECONDS);
timer->Start();
}
}
D3DData *d3ddata;
Image^ img;
Window^ mWnd;
D3DImage^ d3dimg;
Application^ app;
WebBrowser^ brwsr;
HWND brwsrHwnd;
HDC hdcFrom;
HDC hdcTo;
HBITMAP hBitmap;
double dpiX;
double dpiY;
int Width;
int Height;
int DpiWidth;
int DpiHeight;
int CurrentFrameDpiWidth;
int CurrentFrameDpiHeight;
bool renderModeDirectX;
WriteableBitmap^ source;
System::Windows::Threading::DispatcherTimer^ timer;
#ifdef FRAME_RATE_ENABLED
System::Windows::Threading::DispatcherTimer^ frameRateTimer;
int frameRate;
#endif
};
[STAThread]
#pragma comment(linker, "/SUBSYSTEM:CONSOLE")
// if pragma /subsystem:windows
int WinMain (HINSTANCE inst, HINSTANCE pinst, LPSTR cmdlne, int cmdshow) {
WebBrowserEx ^app = gcnew WebBrowserEx();
app->Run(/* useDirectX */ true);
}
// if pragma /subsystem = console
void main (int argc, char **argv) {
WebBrowserEx ^app = gcnew WebBrowserEx();
app->Run(/* useDirectX */ true);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment