前言
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
并添加代码:
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内核的,有如下三种方式可以获取:
- 安装开发版的Edge (Chromium),稳定版的Edge目前不支持WebView控件,不知道后续会不会开放。
- 安装独立的WebView2 Runtime,它可以独立下载和升级。
- 程序内嵌入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内核)