WinForm中WebBrowser加载Flash库

2022-03-24 08:05:45 浏览数 (1)

前言

Webview2官方文档:

https://docs.microsoft.com/zh-cn/microsoft-edge/webview2/get-started/wpf

WPF加载网页的三种方式

  • WebBrowser IE内核
  • Webview2 Chrome内核(官方库)
  • CEFSharp Chrome内核(三方库)

CEFSharp:

https://cloud.tencent.com/developer/article/1895623

Webview2 :

WebView2未来应该是要替代WebBrowser的,虽然需要依赖本机自带的Edge chromium内核,但相信未来的windows 更新肯定会以Edge chromium内核代替IE内核,并且不需要重新下载Chrome浏览器,使用Edge也会有一样的浏览体验。而作为桌面开发人员来说,使用WebView2直接加载网页会比使用CefSharp来得更舒服,不再需要引入一堆的CEF类库,同时安装文件也会缩小很多。

Flash支持测试页面

https://sc.chinaz.com/donghua/220315391630.htm

FlashPlayer

https://www.flash.cn/download-wins

其实我们不同的浏览器使用到的DLL是不同的

Windows

Chrome

FireFox

所以我们可以根据自己的需求去安装,不用全部安装到PC上。

Flash的现状

  • IE安装插件还可以使用
  • 360极速及QQ浏览器等,这些浏览器一般都是双内核,其中的Chromium内核版本一般都比较低,所以还可以继续加载Flash Player来使用,啥时候这些国产浏览器升级内核到88及以上版本,就彻底不支持了

WebBrowser

WPF下的WebBrowser存在内存泄漏的问题,所以这里用WinForm实现。

添加Falsh插件

有些老的系统必须用IE执行的,就只能用WebBrowser了。

保证IE上能运行Flash只需要安装flashplayerax_install_cn.exe就可以了。

首先我们要判断Flash有没有安装

代码语言:javascript复制
private bool checkFlashInstall()
{
    Type type = Type.GetTypeFromProgID("ShockwaveFlash.ShockwaveFlash");
    if (type == null)
    {
        return false;
    }

    object flashObject = Activator.CreateInstance(type);
    string versionString = flashObject
        .GetType()
        .InvokeMember("GetVariable", BindingFlags.InvokeMethod, null, flashObject, new object[] { "$version" }) as string;

    Console.WriteLine("Flash版本:"   versionString);
    Marshal.ReleaseComObject(flashObject);
    return true;
}

安装EXE程序

代码语言:javascript复制
private void installEXE(string fileName, string arguments)
{
    string result = "";
    try
    {
        using (Process process = new Process())
        {
            process.StartInfo.FileName = fileName;
            process.StartInfo.Arguments = arguments;
            process.StartInfo.CreateNoWindow = true;
            process.StartInfo.UseShellExecute = false; // 要获取输出,此值必须为 false。
            process.StartInfo.RedirectStandardOutput = true;
            process.StartInfo.RedirectStandardError = true;

            process.Start();
            process.WaitForExit(); // 等待执行完毕
            result = process.StandardError.ReadToEnd(); // 获取输出值
            process.Close();
        }
    }
    catch (Exception ex)
    {
        result = ex.Message;
    }

    Console.WriteLine("result:"   result);
}

注意

安装成功或退出,result返回的字符串都是空字符串。

判断并安装

代码语言:javascript复制
private void Form1_Load(object sender, EventArgs e)
{
    bool isInstallFlash = checkFlashInstall();
    if (!isInstallFlash)
    {
        installEXE("./Libs/flashplayerax_install_cn.exe", "");
    }

    extendedWebBrowser1.Url = new Uri(url);
}

获取系统IE版本

获取浏览器内核版本

https://ie.icoa.cn/

网上有这么个说法

WebBrowser使用的浏览器版本是IE7,这个说法其实是不太准确的。

应该这样说

WebBrowser使用的是系统版本的IE对应的兼容模式。 比如Win10的是IE11,对应的兼容模式是IE7。 所以我在Win10上用下面两种方式获取到的IE版本都是11。

方式1

代码语言:javascript复制
/// <summary>
/// 获取IE版本号
/// </summary>
/// <returns>
/// </returns>
public static int GetMajorVersion()
{
  //通过WebBrowser方案获取版本号
  return new WebBrowser().Version.Major;
}

方式2

代码语言:javascript复制
/// <summary>
/// IE浏览器版本号帮助类
/// </summary>
public static class IEVersionHelper
{
  /// <summary>
  /// 获取IE主版本号
  /// </summary>
  /// <returns>
  /// </returns>
  public static string GetMajorVersion()
  {
    var majorVersion = string.Empty;

    var detailVersion = GetDetailVersion();
    if (!string.IsNullOrWhiteSpace(detailVersion))
    {
      if (detailVersion.IndexOf(".", StringComparison.Ordinal) is int connectedCharFirstIndex && connectedCharFirstIndex > -1)
      {
        majorVersion = detailVersion.Substring(0, connectedCharFirstIndex);
      }
      else
      {
        majorVersion = detailVersion;
      }
    }

    return majorVersion;
  }

  /// <summary>
  /// 获取IE详细版本号
  /// </summary>
  /// <returns>
  /// </returns>
  public static string GetDetailVersion()
  {
    string text = @"SOFTWAREMicrosoftInternet Explorer";
    //通过注册表获取用户IE版本号
    RegistryKey mainKey = Registry.LocalMachine;
    RegistryKey subKey = mainKey.OpenSubKey(text);

    var versionNumber = subKey?.GetValue("svcVersion")?.ToString() ?? string.Empty;
    if (string.IsNullOrEmpty(versionNumber))
    {
      versionNumber = subKey?.GetValue("svcUpdateVersion")?.ToString() ?? string.Empty;
      if (string.IsNullOrEmpty(versionNumber))
      {
        versionNumber = subKey?.GetValue("Version")?.ToString() ?? string.Empty;
      }
    }
    return versionNumber;
  }
}

设置IE版本

记录WebBrowser控件使用IE渲染版本的路径:

32位 HKEY_LOCAL_MACHINESOFTWAREMicrosoftInternet ExplorerMAINFeatureControlFEATURE_BROWSER_EMULATION 64位 HKEY_LOCAL_MACHINESOFTWAREWow6432NodeMicrosoftInternet ExplorerMAINFeatureControlFEATURE_BROWSER_EMULATION

或者

32位 HKEY_CURRENT_USERSOFTWAREMicrosoftInternet ExplorerMAINFeatureControlFEATURE_BROWSER_EMULATION 64位 HKEY_CURRENT_USERSOFTWAREWow6432NodeMicrosoftInternet ExplorerMAINFeatureControlFEATURE_BROWSER_EMULATION

工具类

代码语言:javascript复制
public class IEHelper
{
  /// <summary>
  /// 定义IE版本的枚举
  /// </summary>
  public enum IeVersion
  {
    ie11强制,//11001 (0x2AF9) IE11
    ie11,//11000 (0x2AF8) IE11
    ie10强制,//10001 (0x2711) IE10
    ie10,//10000 (0x02710) IE10
    ie9强制,//9999 (0x270F) IE9
    ie9,//9000 (0x2328) IE9.
    ie8强制,//8888 (0x22B8) IE8. 强制IE8标准模式显示,忽略!DOCTYPE指令
    ie8,//8000 (0x1F40) IE8. 默认设置,在IE8标准模式中按照网页上!DOCTYPE指令展示网页
    ie7//7000 (0x1B58) 使用WebBrowser Control控件的应用程序所使用的默认值,在IE7标准模式中按照网页上!DOCTYPE指令来展示网页
  }

  /// <summary>
  /// 设置WebBrowser的默认版本
  /// </summary>
  /// <param name="ver">
  /// IE版本
  /// </param>
  public static void SetIE(IeVersion ver)
  {
    //获取程序名称
    //string productName = AppDomain.CurrentDomain.SetupInformation.ApplicationName;
    string productName = System.IO.Path.GetFileName(System.Diagnostics.Process.GetCurrentProcess().MainModule.FileName);
    object version;
    switch (ver)
    {
      case IeVersion.ie7:
        version = 0x1B58;
        break;

      case IeVersion.ie8:
        version = 0x1F40;
        break;

      case IeVersion.ie8强制:
        version = 0x22B8;
        break;

      case IeVersion.ie9:
        version = 0x2328;
        break;

      case IeVersion.ie9强制:
        version = 0x270F;
        break;

      case IeVersion.ie10:
        version = 0x2710;
        break;

      case IeVersion.ie10强制:
        version = 0x2711;
        break;

      case IeVersion.ie11:
        version = 0x2AF8;
        break;

      case IeVersion.ie11强制:
        version = 0x2AF9;
        break;

      default:
        version = 0x1F40;
        break;
    }
    string fcRegKey = @"HKEY_LOCAL_MACHINESoftwareMicrosoftInternet ExplorerMainFeatureControl";
    Registry.SetValue(fcRegKey   "FEATURE_BEHAVIORS", productName, 1, RegistryValueKind.DWord);
    Registry.SetValue(fcRegKey   "FEATURE_BROWSER_EMULATION", productName, version, RegistryValueKind.DWord);
    if (Environment.Is64BitOperatingSystem)
    {
      string fcRegKey64 = @"HKEY_LOCAL_MACHINESOFTWAREWow6432NodeMicrosoftInternet ExplorerMAINFeatureControl";
      Registry.SetValue(fcRegKey64   "FEATURE_BEHAVIORS", productName, 1, RegistryValueKind.DWord);
      Registry.SetValue(fcRegKey64   "FEATURE_BROWSER_EMULATION", productName, version, RegistryValueKind.DWord);
    }
  }
}

调用

代码语言:javascript复制
int version = GetMajorVersion();
Console.WriteLine("version:"   version);

if (version >= 9)
{
  IEHelper.SetIE(IEHelper.IeVersion.ie9);
}

注意

网上有这么个说法,把版本设置为非IE的版本号,会使用Edge,这个说法是错误的。 虽然更改后加载检测浏览器内核的网页上显示是Edge,但是其实上并不是使用的Edge。

窗口弹出

默认的WebBrowser新打开的窗口会用系统默认的IE打开,并且我们也没法获取新窗口的地址自行处理。

所以我们自定义WebBrowser保证能获取新窗口的地址。

自定义ExtendedWebBrowser

代码语言:javascript复制
using System;

namespace WinFormWebFlash.Views
{
    public class ExtendedWebBrowser : System.Windows.Forms.WebBrowser
    {
        private System.Windows.Forms.AxHost.ConnectionPointCookie cookie;
        private WebBrowserExtendedEvents events;

        public event EventHandler BeforeNavigate;

        public event EventHandler BeforeNewWindow;

        [System.Runtime.InteropServices.ComImport(), System.Runtime.InteropServices.Guid("34A715A0-6587-11D0-924A-0020AFC7AC4D"),
                System.Runtime.InteropServices.InterfaceTypeAttribute(System.Runtime.InteropServices.ComInterfaceType.InterfaceIsIDispatch),
                System.Runtime.InteropServices.TypeLibType(System.Runtime.InteropServices.TypeLibTypeFlags.FHidden)]
        public interface DWebBrowserEvents2
        {
            [System.Runtime.InteropServices.DispId(250)]
            void BeforeNavigate2(
                [System.Runtime.InteropServices.In,
                System.Runtime.InteropServices.MarshalAs(System.Runtime.InteropServices.UnmanagedType.IDispatch)] object pDisp,
                [System.Runtime.InteropServices.In] ref object URL,
                [System.Runtime.InteropServices.In] ref object flags,
                [System.Runtime.InteropServices.In] ref object targetFrameName, [System.Runtime.InteropServices.In] ref object postData,
                [System.Runtime.InteropServices.In] ref object headers,
                [System.Runtime.InteropServices.In,
                System.Runtime.InteropServices.Out] ref bool cancel);

            [System.Runtime.InteropServices.DispId(273)]
            void NewWindow3(
                [System.Runtime.InteropServices.In,
                System.Runtime.InteropServices.MarshalAs(System.Runtime.InteropServices.UnmanagedType.IDispatch)] object pDisp,
                [System.Runtime.InteropServices.In, System.Runtime.InteropServices.Out] ref bool cancel,
                [System.Runtime.InteropServices.In] ref object flags,
                [System.Runtime.InteropServices.In] ref object URLContext,
                [System.Runtime.InteropServices.In] ref object URL);
        }

        //This method will be called to give you a chance to create your own event sink
        protected override void CreateSink()
        {
            //MAKE SURE TO CALL THE BASE or the normal events won't fire
            base.CreateSink();
            events = new WebBrowserExtendedEvents(this);
            cookie = new System.Windows.Forms.AxHost.ConnectionPointCookie(this.ActiveXInstance, events, typeof(DWebBrowserEvents2));
        }

        protected override void DetachSink()
        {
            if (null != cookie)
            {
                cookie.Disconnect();
                cookie = null;
            }
            base.DetachSink();
        }

        protected void OnBeforeNavigate(string url, string frame, out bool cancel)
        {
            EventHandler h = BeforeNavigate;
            WebBrowserExtendedNavigatingEventArgs args = new WebBrowserExtendedNavigatingEventArgs(url, frame);
            if (null != h)
            {
                h(this, args);
            }
            //Pass the cancellation chosen back out to the events
            cancel = args.Cancel;
        }

        protected void OnBeforeNewWindow(string url, out bool cancel)
        {
            EventHandler h = BeforeNewWindow;
            WebBrowserExtendedNavigatingEventArgs args = new WebBrowserExtendedNavigatingEventArgs(url, null);
            if (null != h)
            {
                h(this, args);
            }
            cancel = args.Cancel;
        }

        //This class will capture events from the WebBrowser
        private class WebBrowserExtendedEvents : System.Runtime.InteropServices.StandardOleMarshalObject, DWebBrowserEvents2
        {
            private ExtendedWebBrowser _Browser;

            public WebBrowserExtendedEvents(ExtendedWebBrowser browser)
            { _Browser = browser; }

            //Implement whichever events you wish
            public void BeforeNavigate2(object pDisp, ref object URL, ref object flags, ref object targetFrameName, ref object postData, ref object headers, ref bool cancel)
            {
                _Browser.OnBeforeNavigate((string)URL, (string)targetFrameName, out cancel);
            }

            public void NewWindow3(object pDisp, ref bool cancel, ref object flags, ref object URLContext, ref object URL)
            {
                _Browser.OnBeforeNewWindow((string)URL, out cancel);
            }
        }
    }

    public class WebBrowserExtendedNavigatingEventArgs : System.ComponentModel.CancelEventArgs
    {
        private string _Frame;
        private string _Url;

        public WebBrowserExtendedNavigatingEventArgs(string url, string frame)
            : base()
        {
            _Url = url;
            _Frame = frame;
        }

        public string Frame
        {
            get { return _Frame; }
        }

        public string Url
        {
            get { return _Url; }
        }
    }
}

回到Form1.cs[设计]窗口,在菜单生成中,点生成解决方案

一会之后在工具箱的最上方就会出现一个新的组件ExtendedWebBrowser,把ExtendedWebBrowser拖进来。

ExtendedWebBrowser的事件里双击BeforeNewWindow并添加代码:

代码语言:javascript复制
private void extendedWebBrowser2_BeforeNewWindow(object sender, EventArgs e)
{
    WebBrowserExtendedNavigatingEventArgs eventArgs = e as WebBrowserExtendedNavigatingEventArgs;
    eventArgs.Cancel = true;
    ((ExtendedWebBrowser)sender).Navigate(eventArgs.Url);
}

WebView2

安装WebView2 Runtime

WebView2 实在诱人,最新的 Edge(Chromium) 性能强悍,而且所有使用 WebView2 的应用可以共用一个运行时(说人话就是一个安装了应用时,其他应用就不用装了)。

Windows 11 已经自带 WebView2 ,就连 Office 也会自动部署 WebView2 ,目前 WebView2 已经被部署到 2亿台电脑,并且还在继续增加 …… 未来是属于 WebView2 的。

重要的是 WebView2 仍然支持老旧的、即将被淘汰的 Windows 7 —— 拥有良好的兼容性。

WebView2是依赖于Edge chromium内核的,有如下三种方式可以获取:

  1. 安装开发版的Edge (Chromium),稳定版的Edge目前不支持WebView控件,不知道后续会不会开放。
  2. 安装独立的WebView2 Runtime,它可以独立下载和升级。
  3. 程序内嵌入Edge chromium内核

这三种方式运行效果基本一致,主要特点是:

  • 前两种方式和以前使用IE的浏览器控件非常类似,浏览器内核和程序是分离的,程序可以保持非常小的体积,浏览器内核可以单独升级。
  • 第一种方式目前还不支持Edge的稳定版,无法使用于生产环境
  • 第三种方式和以前的CEF比较类似,将chromium嵌入了程序,可以控制chromium的版本,减少依赖性,同时可以控制浏览器的版本,避免升级导致的不稳定。但是相应的程序包会特别大,配置也相对更麻烦。

所以这里我推荐第二种方式。

下载地址:

https://developer.microsoft.com/zh-cn/microsoft-edge/webview2/#download-section

安装WebView2

安装Microsoft.Web.WebView2程序包

代码语言:javascript复制
Install-Package Microsoft.Web.WebView2

添加名字空间

代码语言:javascript复制
xmlns:wv2="clr-namespace:Microsoft.Web.WebView2.Wpf;assembly=Microsoft.Web.WebView2.Wpf"

添加控件

代码语言:javascript复制
<wv2:WebView2 Name="webView" Source="https://www.psvmc.cn"/>

Flash支持

很遗憾,现在还没找到WebView2支持Flash的方式。

目前要想支持Flash只有两种选择:

  • 使用Electron加载Flash插件 (Chrome内核)
  • 使用WebBrowser,系统安装Flash插件(IE内核)

0 人点赞