visual - webbrowser.navigate c# example
¿Cómo arreglar un error de opacidad con DrawToBitmap en WebBrowser Control? (1)
Estoy buscando una solución integrada para tomar una captura de pantalla de la URL publicada en la pregunta sin HIDDEN TEXT. En otras palabras, una solución para respetar la opacidad.
El siguiente código hace precisamente eso: respeta la opacidad de CSS . Entre otras cosas, utiliza un objeto Metafile
y la API OleDraw
para representar la imagen de la página web.
La prueba HTML:
<!DOCTYPE html>
<body style=''background-color: grey''>
<div style=''background-color: blue; opacity: 0.2; color: yellow''>This is a text</div>
</body>
La salida:
El código (aplicación de consola):
using Microsoft.Win32;
using System;
using System.ComponentModel;
using System.Drawing;
using System.Drawing.Imaging;
using System.Runtime.InteropServices;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace Console_21697048
{
// http://stackoverflow.com/q/21697048/1768303
class Program
{
const string HTML = "<!DOCTYPE html><body style=''background-color: grey''><div style=''background-color: blue; opacity: 0.2; color: yellow''>This is a text</div></body>";
const string FILE_NAME = "webpage.png";
readonly static Size IMAGE_SIZE = new Size(320, 200);
// Main
static void Main(string[] args)
{
try
{
// enable HTML5 etc (assuming we''re running IE9+)
SetFeatureBrowserFeature("FEATURE_BROWSER_EMULATION", 9000);
// force software rendering
SetFeatureBrowserFeature("FEATURE_IVIEWOBJECTDRAW_DMLT9_WITH_GDI", 1);
SetFeatureBrowserFeature("FEATURE_GPU_RENDERING", 0);
using (var apartment = new MessageLoopApartment())
{
// create WebBrowser on a seprate thread with its own message loop
var webBrowser = apartment.Invoke(() => new WebBrowser());
// navigate and wait for the result
apartment.Invoke(() =>
{
var pageLoadedTcs = new TaskCompletionSource<bool>();
webBrowser.DocumentCompleted += (s, e) =>
pageLoadedTcs.TrySetResult(true);
webBrowser.DocumentText = HTML;
return pageLoadedTcs.Task;
}).Wait();
// save the picture
apartment.Invoke(() =>
{
webBrowser.Size = IMAGE_SIZE;
var rectangle = new Rectangle(0, 0, webBrowser.Width, webBrowser.Height);
// get reference DC
using (var screenGraphics = webBrowser.CreateGraphics())
{
var screenHdc = screenGraphics.GetHdc();
// create a metafile
using (var metafile = new Metafile(screenHdc, rectangle, MetafileFrameUnit.Pixel))
{
using (var graphics = Graphics.FromImage(metafile))
{
var hdc = graphics.GetHdc();
var rect = new Rectangle(0, 0, 320, 50);
OleDraw(webBrowser.ActiveXInstance, DVASPECT_CONTENT, hdc, ref rectangle);
graphics.ReleaseHdc(hdc);
}
// save the metafile as bitmap
metafile.Save(FILE_NAME, ImageFormat.Png);
}
screenGraphics.ReleaseHdc(screenHdc);
}
});
// dispose of webBrowser
apartment.Invoke(() => webBrowser.Dispose());
webBrowser = null;
}
}
catch (Exception ex)
{
Console.WriteLine(ex.ToString());
}
}
// interop
const uint DVASPECT_CONTENT = 1;
[DllImport("ole32.dll", PreserveSig = false)]
static extern void OleDraw(
[MarshalAs(UnmanagedType.IUnknown)] object pUnk,
uint dwAspect,
IntPtr hdcDraw,
[In] ref System.Drawing.Rectangle lprcBounds);
// WebBrowser Feature Control
// http://msdn.microsoft.com/en-us/library/ie/ee330733(v=vs.85).aspx
static void SetFeatureBrowserFeature(string feature, uint value)
{
if (LicenseManager.UsageMode != LicenseUsageMode.Runtime)
return;
var appName = System.IO.Path.GetFileName(System.Diagnostics.Process.GetCurrentProcess().MainModule.FileName);
Registry.SetValue(@"HKEY_CURRENT_USER/Software/Microsoft/Internet Explorer/Main/FeatureControl/" + feature,
appName, value, RegistryValueKind.DWord);
}
}
// MessageLoopApartment
// more info: http://stackoverflow.com/a/21808747/1768303
public class MessageLoopApartment : IDisposable
{
Thread _thread; // the STA thread
TaskScheduler _taskScheduler; // the STA thread''s task scheduler
public TaskScheduler TaskScheduler { get { return _taskScheduler; } }
/// <summary>MessageLoopApartment constructor</summary>
public MessageLoopApartment()
{
var tcs = new TaskCompletionSource<TaskScheduler>();
// start an STA thread and gets a task scheduler
_thread = new Thread(startArg =>
{
EventHandler idleHandler = null;
idleHandler = (s, e) =>
{
// handle Application.Idle just once
Application.Idle -= idleHandler;
// return the task scheduler
tcs.SetResult(TaskScheduler.FromCurrentSynchronizationContext());
};
// handle Application.Idle just once
// to make sure we''re inside the message loop
// and SynchronizationContext has been correctly installed
Application.Idle += idleHandler;
Application.Run();
});
_thread.SetApartmentState(ApartmentState.STA);
_thread.IsBackground = true;
_thread.Start();
_taskScheduler = tcs.Task.Result;
}
/// <summary>shutdown the STA thread</summary>
public void Dispose()
{
if (_taskScheduler != null)
{
var taskScheduler = _taskScheduler;
_taskScheduler = null;
// execute Application.ExitThread() on the STA thread
Task.Factory.StartNew(
() => Application.ExitThread(),
CancellationToken.None,
TaskCreationOptions.None,
taskScheduler).Wait();
_thread.Join();
_thread = null;
}
}
/// <summary>Task.Factory.StartNew wrappers</summary>
public void Invoke(Action action)
{
Task.Factory.StartNew(action,
CancellationToken.None, TaskCreationOptions.None, _taskScheduler).Wait();
}
public TResult Invoke<TResult>(Func<TResult> action)
{
return Task.Factory.StartNew(action,
CancellationToken.None, TaskCreationOptions.None, _taskScheduler).Result;
}
}
}
De acuerdo con el siguiente enlace y mi aplicación de consola, el método DrawToBitmap
no respeta la opacidad.
Enlace de prueba: http://social.msdn.microsoft.com/Forums/vstudio/en-US/e9704309-0c52-442d-80e0-2f8393dcd313/webbrowser-opacity-problem-
Mi código HTML: http://fiddle.jshell.net/L37TC/
<div id="fader" style="background-color: #ff0000">ffff</div>
<div style="background-color: blue; opacity:0;filter:alpha(opacity=0);">HIDDEN TEXT!</div>
SomeText
Mi código de consola C #:
var bmp = new Bitmap(640,480, PixelFormat::Format32bppArgb)
var web = (System.Windows.Forms.Control)sender;
web.DrawToBitmap(bmp, Rectangle(0, 0, 640,480));
Así que estoy buscando una solución alternativa .NET ( sin CEF, Awesomium o cualquier extensión ) solo una función incorporada de .NET para solucionar el error o una solución alternativa para tomar una captura de pantalla de una URL web en mi consola solicitud.
Si hago que la ventana de WebBrowser
esté visible para mi cliente y uso CopyFromScreen
la opacidad se respeta y HIDDEN TEXT
no se muestra, sin embargo, no quiero que la ventana de WebBrowser
esté visible en la pantalla del escritorio.
Estoy buscando una solución integrada para tomar una captura de pantalla de la URL publicada en la pregunta sin HIDDEN TEXT
. En otras palabras, una solución para respetar la opacity
.
EDIT1: Todos los píxeles en mi class
Bitmap
( class
.NET no formato BMP) tienen un valor alfa de 255. Por lo tanto, el problema NO es con el formato de archivo. He intentado con PNG y cualquier otro formato compatible con .NET.
Código fuente completo (Plantilla de consola, Necesidad de agregar referencias a System.Drawing
y System.Windows.Forms
class Program
{
static System.Windows.Forms.WebBrowser w = new System.Windows.Forms.WebBrowser();
[STAThread]
static void Main(string[] args)
{
w.Navigate("http://fiddle.jshell.net/L37TC/show/");
w.DocumentCompleted += w_DocumentCompleted;
System.Windows.Forms.Application.Run();
while (true) Console.Read();
}
static void w_DocumentCompleted(object sender, System.Windows.Forms.WebBrowserDocumentCompletedEventArgs e)
{
var bmp = new System.Drawing.Bitmap(w.Width, w.Height, System.Drawing.Imaging.PixelFormat.Format32bppArgb);
((System.Windows.Forms.Control)w).DrawToBitmap(bmp, new System.Drawing.Rectangle(0, 0, w.Width, w.Height));
for (int i = 0; i < w.Width; i++) for (int j = 0; j < w.Height; j++) if (bmp.GetPixel(i, j).A != 255)
{
Console.WriteLine("Alpha != 255");
return;
}
Console.WriteLine("All pixels have alpha value of 255");
bmp.Save(@"d:/ss.png", System.Drawing.Imaging.ImageFormat.Png);
// HIDDEN TEXT has opcity of 0 but it''s appearing in image
}
}