如何在WebBrowser控件上使用DrawToBitmap修复不透明度错误?

时间:2014-02-11 08:51:12

标签: c# .net vb.net webbrowser-control

根据以下链接和我的控制台应用程序,方法DrawToBitmap不尊重不透明度。

证明链接: http://social.msdn.microsoft.com/Forums/vstudio/en-US/e9704309-0c52-442d-80e0-2f8393dcd313/webbrowser-opacity-problem-

我的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 

我的C#控制台代码:

var bmp = new  Bitmap(640,480, PixelFormat::Format32bppArgb)
var web = (System.Windows.Forms.Control)sender;
web.DrawToBitmap(bmp, Rectangle(0, 0, 640,480));

所以我正在寻找替代的.NET内置解决方案(没有CEF,Awesomium或任何扩展 )只是.NET的内置功能修复错误或替代解决方案,以在我的控制台应用程序中截取Web URL的屏幕截图。

如果我向我的客户显示WebBrowser窗口并使用CopyFromScreen,则不尊重不透明度并且HIDDEN TEXT未显示,我怎么也不想{{1}窗口可见桌面屏幕。

我正在寻找一个内置的解决方案,可以在没有WebBrowser的问题中从已发布的网址截取屏幕截图。换句话说,尊重HIDDEN TEXT 的解决方案。

EDIT1:我的opacity Bitmap (.NET类不是BMP格式)中的所有像素的alpha值都是255.所以问题不在于文件格式。我尝试过PNG和任何其他.NET支持的格式。

完整的源代码(控制台模板,需要添加对class的引用和System.Drawing

System.Windows.Forms

1 个答案:

答案 0 :(得分:5)

  

我正在寻找一个内置的解决方案来截取发布的截图   没有HIDDEN TEXT的问题中的URL。换句话说,解决方案   尊重不透明。

以下代码就是这样做的:尊重CSS不透明度。除此之外,它还使用Metafile对象和OleDraw API来呈现网页的图片。

测试HTML:

<!DOCTYPE html>
<body style='background-color: grey'>
    <div style='background-color: blue; opacity: 0.2; color: yellow'>This is a text</div>
</body>

输出:

opacity

代码(控制台应用):

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;
        }
    }
}