Last active
August 29, 2015 14:13
-
-
Save trevorlinton/293f8d3de742ed7054e4 to your computer and use it in GitHub Desktop.
Rendering Content over WPF WebControl
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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