在WPF程序中打開網頁:使用代理服務器並可進行JS交互


    本項目環境:使用VS2010(C#)編寫的WPF程序,通過CefSharp在程序的窗體中打開網頁。需要能夠實現網頁后台JS代碼中調用的方法,從網頁接收數據,並能返回數據給網頁。運行程序的電腦不允許上網,要求通過局域網內一個指定的代理服務器聯網,並且只有該程序能通過代理服務器打開網頁,直接用瀏覽器或其他應用程序仍然不允許上網(因此不能直接更改本機的LAN設置)。

    首先介紹一下CefSharp,它是基於Google瀏覽器的一個組件,是可以用在WPF/WinForm客戶端軟件中的嵌入式瀏覽器。

    如果你只需要通過客戶端軟件的一個窗體打開現成的網頁,那么方法還是比較簡單的,你可以直接參考http://www.w2bc.com/Article/54798這篇文章。

 

1、配置環境,加載CefSharp動態庫

  首先需要下載NuGet插件,它是免費、開源的包管理開發工具,專注於在 .NET 應用開發過程中,簡單地合並第三方的組件庫。使用該工具可以將CefSharp的完整程序包加載到工程中。下載地址:http://xz.cr173.com/soft2/nuget.tools.zip

  本項目是用VS2010編寫的,因此運行NuGet.Tools  for vs2010,安裝后,在VS的“工具”菜單下會多出一項“NuGet程序包管理器”,見下圖所示:

  新建工程文件EmbeddedWebBrowser。點擊“管理解決方案的NuGet程序包”,打開如下圖所示的窗體:

  在右上角聯機搜索“CefSharp”,下載CefSharp.Wpf和CefSharp.Common。如果你用的不是WPF,而是WinForm,則需要下載CefSharp.WinForms和CefSharp.Common。下載的過程比較慢,網速不佳的情況下大概要一個小時左右。安裝完成后,在工程文件所在目錄下會多出“packages”目錄。

 

2、MainWindow窗體:用於輸入網頁地址和初始化Cef

  在工程文件中的MainWindow窗體中,添加一個用於輸入網頁地址的編輯框,和一個用於打開網頁的按鈕。MainWindow.xaml的代碼如下所示:

<Window x:Class="EmbeddedWebBrowser.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="350" Width="800" WindowStartupLocation="CenterScreen" Loaded="Window_Loaded">
    <Grid>
        <DockPanel>
            <StackPanel DockPanel.Dock="Top" Orientation="Horizontal">
                <TextBlock Text="網頁地址:" Margin="5"/>
                <TextBox x:Name="txtAddress" Width="350" Margin="5"/>
                <Button Content="Go" Margin="5" Click="OnGoClick" IsDefault="True"/>
            </StackPanel>

            <Grid x:Name="MainGrid">

            </Grid>
        </DockPanel>
    </Grid>
</Window>

  該頁面的后台代碼MainWindow.xaml.cs如下所示:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using CefSharp;

namespace EmbeddedWebBrowser { /// <summary> /// MainWindow.xaml 的交互邏輯 /// </summary> public partial class MainWindow : Window { public MainWindow() { InitializeComponent(); } /// <summary> /// 窗體加載事件 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void Window_Loaded(object sender, RoutedEventArgs e) {
      
//默認打開的頁面 txtAddress.Text = "http://www.baidu.com/"; //txtAddress.Text = System.Diagnostics.Process.GetCurrentProcess().MainModule.FileName.Replace("EmbeddedWebBrowser.vshost.exe", "index.html");
       //設置代理服務器
var setting = new CefSharp.CefSettings(); setting.CefCommandLineArgs.Add("--proxy-server", "http://192.168.0.105:3128");
CefSharp.Cef.Initialize(setting,
true, false); } /// <summary> /// “Go”按鈕單擊事件 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void OnGoClick(object sender, RoutedEventArgs e) { //獲取要打開的頁面地址 string url = txtAddress.Text; if (!string.IsNullOrWhiteSpace(url)) { //打開網頁 WebPageViewer viewer = new WebPageViewer(url); MainGrid.Children.Insert(0, viewer); } } } }

  這里最關鍵的就是:setting.CefCommandLineArgs.Add("--proxy-server", "http://192.168.0.105:3128");這是用來設置代理服務器的IP和端口的。

     CefSharp.Cef.Initialize(setting, true, false); 用來初始化CEF,即嵌入式瀏覽器。

2016-04-18補充:CefSharp.Cef.Initialize方法在程序里只能使用一次,如果把它寫在一個會多次打開的子頁面,就會報錯:“只能初始化一次”。因此需要將這個方法寫在主程序App.cs里。並且在程序退出前使用:CefSharp.Cef.Shutdown();否則程序關閉之后,可能會由於CefSharp.Cef未關閉,而使得該程序仍然存在后台進程中(可在任務管理器中查看),影響再次打開程序使用。

 

3、WebPageViewer.xaml:用於打開和顯示網頁

  創建一個用戶控件WebPageViewer.xaml,用於打開和顯示網頁,代碼如下所示:

<UserControl x:Class="EmbeddedWebBrowser.WebPageViewer"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             xmlns:local="clr-namespace:EmbeddedWebBrowser"
             xmlns:uc="clr-namespace:EmbeddedWebBrowser"
             mc:Ignorable="d" 
             d:DesignHeight="300" d:DesignWidth="300">
    <Grid x:Name="MainGrid">
        <uc:MaskLoading x:Name="maskLoading"/>
    </Grid>
</UserControl>

     這里的“MaskLoading ”是用來顯示網頁加載時的等待畫面的,下面會進行介紹。

     后台代碼WebPageViewer.xaml.cs如下所示:

using CefSharp;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.Net;
using CefSharp.Wpf;

namespace EmbeddedWebBrowser
{
    /// <summary>
    /// 用於顯示網頁的控件
    /// </summary>
    public partial class WebPageViewer : UserControl, IRequestHandler
    {
        /// <summary>
        /// 用於顯示網頁的自定義控件,構造函數
        /// </summary>
        /// <param name="url">網頁地址</param>
        public WebPageViewer(String url)
        {
            InitializeComponent();

            var webView = new CefSharp.Wpf.ChromiumWebBrowser();

            //注冊一個JS對象
            webView.RegisterJsObject("hy", new CallbackObjectForJs());

            //注冊網頁加載事件:在頂級導航完成且內容加載到 WebView 控件中時發生
            webView.Loaded += new RoutedEventHandler(webView_Loaded);

            MainGrid.Children.Insert(0, webView);
            webView.Address = url;
        }

        /// <summary>
        /// 網頁加載完畢的事件
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        void webView_Loaded(object sender, RoutedEventArgs e)
        {
            maskLoading.Visibility = Visibility.Collapsed;   //隱藏等待畫面(正在加載...)
        }

        public bool GetAuthCredentials(IWebBrowser browserControl, IBrowser browser, IFrame frame, bool isProxy, string host, int port, string realm, string scheme, IAuthCallback callback)
        {
            throw new NotImplementedException();
        }

        public bool OnBeforeBrowse(IWebBrowser browserControl, IBrowser browser, IFrame frame, IRequest request, bool isRedirect)
        {
            throw new NotImplementedException();
        }

        public CefReturnValue OnBeforeResourceLoad(IWebBrowser browserControl, IBrowser browser, IFrame frame, IRequest request, IRequestCallback callback)
        {
            throw new NotImplementedException();
        }

        public bool OnCertificateError(IWebBrowser browserControl, IBrowser browser, CefErrorCode errorCode, string requestUrl, ISslInfo sslInfo, IRequestCallback callback)
        {
            throw new NotImplementedException();
        }

        public bool OnOpenUrlFromTab(IWebBrowser browserControl, IBrowser browser, IFrame frame, string targetUrl, WindowOpenDisposition targetDisposition, bool userGesture)
        {
            throw new NotImplementedException();
        }

        public void OnPluginCrashed(IWebBrowser browserControl, IBrowser browser, string pluginPath)
        {
            throw new NotImplementedException();
        }

        public bool OnProtocolExecution(IWebBrowser browserControl, IBrowser browser, string url)
        {
            throw new NotImplementedException();
        }

        public bool OnQuotaRequest(IWebBrowser browserControl, IBrowser browser, string originUrl, long newSize, IRequestCallback callback)
        {
            throw new NotImplementedException();
        }

        public void OnRenderProcessTerminated(IWebBrowser browserControl, IBrowser browser, CefTerminationStatus status)
        {
            throw new NotImplementedException();
        }

        public void OnRenderViewReady(IWebBrowser browserControl, IBrowser browser)
        {
            throw new NotImplementedException();
        }

        public void OnResourceLoadComplete(IWebBrowser browserControl, IBrowser browser, IFrame frame, IRequest request, IResponse response, UrlRequestStatus status, long receivedContentLength)
        {
            throw new NotImplementedException();
        }

        public void OnResourceRedirect(IWebBrowser browserControl, IBrowser browser, IFrame frame, IRequest request, ref string newUrl)
        {
            throw new NotImplementedException();
        }

        public bool OnResourceResponse(IWebBrowser browserControl, IBrowser browser, IFrame frame, IRequest request, IResponse response)
        {
            throw new NotImplementedException();
        }
    }

    /// <summary>
    /// 網頁JS中調用的方法
    /// </summary>
    public class CallbackObjectForJs
    {
        public string action(string message)
        {
            //MessageBox.Show("測試");
            return "收到網頁消息:" + message;
        }
    }
}

  該類實現IRequestHandler接口,是為了能夠和JS網頁進行交互,從GetAuthCredentials到OnResourceResponse的那幾個空方法都是為了實現這個接口。當然這些在打開百度時是用不到的,接下來會用一個自己編寫的JS網頁做講解。

 

4、與JS網頁之間進行交互(如果只是打開百度等現成的網頁,可以忽略這一項)

  先編寫一個網頁index.html,如下所示:

<html>
<head>
<title>Test Page</title>
</head>
<body>
<p style="color: red">Hello, World!</p>
<button onclick = helloWebkit()>test</button>
<div><p> :) </p></div>
<script type="text/javascript">
    function helloWebkit() { 
        alert("input:123");
        var x = hy.action("123");
        alert(x);
    }
</script>
</body>
</html>

  在該網頁上有一個按鈕test,點擊后會彈出提示“input:123”,並調用hy類中的action方法,傳入參數"123"。

  將該網頁放到DEBUG目錄下,並將第2步MainWindow.xaml.cs中的網頁地址改為該網頁的本地地址:

txtAddress.Text = System.Diagnostics.Process.GetCurrentProcess().MainModule.FileName.Replace("EmbeddedWebBrowser.vshost.exe", "index.html");

  其中EmbeddedWebBrowser為工程文件名稱。

      在第3步的WebPageViewer.xaml.cs中,注冊了一個JS對象"hy": webView.RegisterJsObject("hy", new CallbackObjectForJs());在 CallbackObjectForJs 類中實現了action方法(該方法名全為小寫字母),接收網頁上傳來的參數,並將處理后的數據返回給網頁。

 

5、在網頁加載時顯示等待畫面

    第三步的代碼中有用到“MaskLoading ”,MaskLoading.xaml的代碼如下所示:

<UserControl x:Class="EmbeddedWebBrowser.MaskLoading"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             xmlns:local="clr-namespace:EmbeddedWebBrowser"
             mc:Ignorable="d" 
             d:DesignHeight="300" d:DesignWidth="300">
    <Grid Opacity=".85">
        <TextBlock Text="頁面加載中..." VerticalAlignment="Center" HorizontalAlignment="Center"/>
    </Grid>
</UserControl>

  該頁面沒有任何邏輯,只是為了在網頁未加載完成時顯示“頁面加載中...”的提示信息。后台代碼MaskLoading.xaml.cs如下:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

namespace EmbeddedWebBrowser
{
    /// <summary>
    /// Interaction logic for MaskLoading.xaml
    /// </summary>
    public partial class MaskLoading : UserControl
    {
        /// <summary>
        /// 顯示等待畫面“頁面加載中...”
        /// </summary>
        public MaskLoading()
        {
            InitializeComponent();
        }
    }
}

 

6、其他注意事項

    如果編譯的時候報錯,提示:“無法加載CefSharp.Core.dll組件”,則需要確保電腦上有VC++2013版本的運行庫(2010版本是不行的)。運行庫的下載地址:https://www.microsoft.com/en-us/download/details.aspx?id=40784。點擊紅色的DownLoad按鈕,選擇vcredist_x86.exe。

    如果仍然報錯,檢查代碼的生成屬性:在工程文件上右擊,選擇屬性——生成,打開如下圖所示的界面:

  目標平台需要選擇“x86”

  2016-04-18補充:若平台下拉框中找不到“x86”,需要按下列步驟進行添加。

 

  若選擇了平台x86,運行時仍然報錯,則需要將原有的x86平台刪除(按下面的步驟打開配置管理器,在活動解決方案平台中選“編輯”,移除x86),再按下面的步驟重新添加。

 

      右擊解決方案--屬性--配置屬性--配置管理器,打開如下圖所示的窗體:

                                                  

  新建x86活動平台后,編譯程序時,可能目標文件會輸出到bin\x86\Debug下。此時需要在工程的屬性里,將輸出路徑改為bin\Debug。

 

      如果是64位機,上述所有的x86都需要改為x64,運行庫也需要下載vcredist_x64.exe才行。

      Winform應用程序的寫法可能會和WPF的有所不同,這里不做討論。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM