ユーザ用ツール

サイト用ツール


it技術:dotnet:cefsharp

CefSharp

概要

Chromiumをアプリケーションに組み込むためのフレームワークで、C#実装がCefSharpとなります。

Cef

CefはChromium Embedded Frameworkの略で、Chromiumをアプリケーションに組み込むためのフレームワークです。
https://bitbucket.org/chromiumembedded/cef

Cefには各言語向けのラッパーがあり、その中のC#実装がCefSharpです。
https://github.com/cefsharp/CefSharp

インストール

CefSharpはNuGetでインストールできるようになっている。
WPF対応版とWinForms対応のコンポーネントに分かれている。
.NET Framework 4.5.2以上が必要となる。

Any CPUには対応していないが、プラットフォームをx86かx64を自動で判別する方法がある。

  1. NuGetでCefSharp.WinFormsをインストールする
  2. Visual Studioを終了する。
  3. 対象プロジェクトのcsprojファイルの最初のProperyGroupに末尾に<CefSharpAnyCpuSupport>true</CefSharpAnyCpuSupport>を追加する
  4. Visual Studioを起動してプロジェクトを読み込む。
  5. Debug および Relese フォルダ内に x86 と x64 フォルダが作成される。
  6. 下記サイトで指定されたプログラムを組み込む。
    CefSharpでAnyCPU対応に苦慮した話2

Any CPUで32bit優先にした場合(32bit優先とはARMのWindows環境でも動作可能となる)
How to use CefSharp (chromium embedded framework c#) in a Winforms application

ブラウザ言語を変更する

ひらがなと漢字が混在していた場合、ひらがなのみ太いなどのフォント表示(中国語っぽい)がされてしまう。
原因はロケールが違うため、言語を日本語に変更する。
CefSharp.Wpfのlocaleを変更する - stackoverflow

CefSettings settings = new CefSettings();
settings.Locale = "ja"
settings.AcceptLanguageList = "ja-JP"
Cef.Initialize(settings);

または、OSのカルチャーを取得してセットする。

CefSettings settings = new CefSettings();
settings.Locale = System.Globalization.CultureInfo.CurrentCulture.Parent.ToString();
settings.AcceptLanguageList = System.Globalization.CultureInfo.CurrentCulture.Name;
Cef.Initialize(settings);

debug.logを出力しない

LogSeverity プロパティを無効にする。

CefSettings settings = new CefSettings();
settings.LogSeverity = LogSeverity.Disable;
Cef.Initialize(settings);

GPUCacheやblob_storageがデスクトップに作成される

CefSharpを使用したアプリケーションをデスクトップのショートカットから実行した場合
ショートカットの作業フォルダが空だった場合、GPUCacheがblob_storageフォルダがデスクトップに作成されてしまう。
対応として作業フォルダを指定することで、作業フォルダ側に作成される。

ショートカットの作業フォルダ対応ではなくプログラムで対応する場合
カレントフォルダを設定しただけでは、GPUCacheフォルダはカレントフォルダに作成されるが、blob_storageフォルダはデスクトップに作成されてしまう。
対応として、CefSettings.UserDataPath にカレントフォルダを指定する必要がある。

// カレントディレクトリをアプリケーション起動パスに設定する
string appPath = Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location);
Directory.SetCurrentDirectory(appPath);
CefSettings settings = new CefSettings();
settings.UserDataPath = appPath;
Cef.Initialize(settings);

Cef.Initializeの設定は1回のみ

Cef.Initializeの設定は1回のみで、new ChromiumWebBrowser する前に設定する。
Cef.Initialize したかどうかは、Cef.IsInitialized で判断(false:未設定、true:設定済)する。
C# (CSharp) CefSettings Code Examples

パネル配下にブラウザをセット

フォームにToolboxを配置したあったので、パネルを用意して配下にブラウザをセットした。

browser = new ChromiumWebBrowser("https://www.google.co.jp/");
this.pnlWebBrowser.Controls.Add(browser);
browser.Dock = DockStyle.Fill;

URL(アドレス)の取得

WinFormsの場合

Addressプロパティが見当たらないため、アドレス変更イベントで変数にセットする。

this.browser= new ChromiumWebBrowser();
this.browser.AddressChanged += browser_AddressChanged;
 
private void browser_AddressChanged(object sender, AddressChangedEventArgs e)
{
    this.CurrentAddress = e.Address;
}

WPFの場合

Addressプロパティで取得できる。

var browser = new CefSharp.Wpf.ChromiumWebBrowser();
this.CurrentAddress = browser.Address

URL(アドレス)の変更

WinFormsの場合

Addressプロパティが見当たらないため、Loadメソッドで変更する。
How to change the URL using CefSharp WinForms - stackoverflow

this.Browser = new ChromiumWebBrowser();
this.Browser.Load("https://www.google.co.jp/")

WPFの場合

Addressプロパティ または、Loadメソッドで変更する。
WPFでCefSharp(Chromiumの.NET向け実装)を使う - 1

var browser = new ChromiumWebBrowser();
browser.Address = "https://www.google.co.jp/";

AddressプロパティとLoadメソッドでは、基本的な挙動に変わりはありませんが、ブラウザが初期化されていないときに実行すると挙動が変わる。

Addressを書き換えた場合

ブラウザの初期化後にそのURLに遷移する

Loadを呼んだ場合

初期化されていないので何も起こらない

ブラウザ読込完了時イベント

IEのWebブラウザコンポーネントにあった DocumentCompleted イベントの代わり。
CefSharp documentcompleted - stackoverflow

var browser = new ChromiumWebBrowser();
browser.LoadingStateChanged += OnLoadingStateChanged;
 
private void OnLoadingStateChanged(object sender, LoadingStateChangedEventArgs e)
{
    if (!e.IsLoading)
    {
        // 読込完了処理を記述
    }
}

HTML要素のボタンをClickする

Javascriptで操作させてボタンをClickさせる。
ExecuteScriptAsyncメソッドで、Javascriptを実行する。

var browser = new ChromiumWebBrowser();
 
// Enterボタンクリック用スクリプト
string clickScript = "var inputs = document.getElementsByTagName('input');" +
                     "for(var i = 0; i < inputs.length; i++){ " +
                     "  if(inputs[i].getAttribute('value') == 'Enter') inputs[i].click(); " +
                     "}";
string jsScript = string.Format("document.getElementById('c03').value = '{0}';", txtInput.Text);
browser.ExecuteScriptAsync(jsScript + clickScript);                     

HTML要素の値を取得する

Javascriptで操作させて取得する。
ExecuteScriptAsyncメソッドで、Javascriptを実行する。
CefSharp - HTML要素の値を取得する

var browser = new ChromiumWebBrowser();
 
browser.EvaluateScriptAsync(jsScript).ContinueWith(x =>
{
    var response = x.Result;
 
    if (response.Success && response.Result != null)
    {
        userCodeData = response.Result.ToString();
    }
});

キー押下の処理

IEのWebブラウザコンポーネントにあった previewkeydown イベントの代わり

KeyboardHandleクラスを定義する。

public class KeyboardHandler : IKeyboardHandler
{
    private frmMain _frm;
 
    public KeyboardHandler(frmMain frm)
    {
        _frm = frm;
    }
 
    public bool OnKeyEvent(IWebBrowser browserControl, IBrowser browser, KeyType type, int windowsKeyCode, int nativeKeyCode, CefEventFlags modifiers, bool isSystemKey)
    {
        return false;
    }
 
    public bool OnPreKeyEvent(IWebBrowser browserControl, IBrowser browser, KeyType type, int windowsKeyCode, int nativeKeyCode, CefEventFlags modifiers, bool isSystemKey, ref bool isKeyboardShortcut)
    {
        _frm.OnPreviewKeyDown(windowsKeyCode, modifiers);
 
        return false;
    }
}

今回は、Enterキー押下時のみ処理を行う。

var browser = new ChromiumWebBrowser();
 
// キーイベント処理登録
browser.KeyboardHandler = new KeyboardHandler(this);
 
public void OnPreviewKeyDown(int windowsKeyCode, CefEventFlags modifiers)
{
    if (windowsKeyCode != (int)Keys.Return) return;
 
    // Enterキー押下時の処理を記述する。
}        

フォーカスのセット

何故か、browser.Focus() ではフォーカスがセットされなかったため、Win32APIのSetFocusを使用した。
読み込み完了時のイベントで処理している。

// フォーカスセット用
[DllImport("user32.dll", SetLastError = true)]
static extern IntPtr SetFocus(IntPtr hWnd);
 
var browser = new ChromiumWebBrowser();
browser.LoadingStateChanged += OnLoadingStateChanged;
 
private void OnLoadingStateChanged(object sender, LoadingStateChangedEventArgs e)
{
    if (String.IsNullOrEmpty(this.CurrentAddress)) return;
    if (e.IsLoading) return;
 
    // 読込完了時にWebブラウザにフォーカスをセット
    this.Invoke((MethodInvoker)delegate { SetFocus(browser.Handle); });
}

最初は、ブラウザにマウスクリックするとフォーカスがセットされたのでマウスクリックするプログラムを組んだが、SetFocusで出来たのでやめた。

[DllImport("USER32.dll", CallingConvention = CallingConvention.StdCall)]
static extern void SetCursorPos(int X, int Y);
[DllImport("USER32.dll", CallingConvention = CallingConvention.StdCall)]
static extern void mouse_event(int dwFlags, int dx, int dy, int cButtons, int dwExtraInfo);
 
private void SetFocusWebBrowser()
{
    // マウス位置を移動してクリックして元に戻す
    this.Invoke((MethodInvoker)delegate
    {
        int curX = Cursor.Position.X;
        int curY = Cursor.Position.Y;
 
        var point = this.pnlWebBrowser.PointToScreen(this.pnlWebBrowser.Location);
        SetCursorPos(point.X, point.Y);
        mouse_event(MOUSEEVENTF_LEFTDOWN, 0, 0, 0, 0);
        mouse_event(MOUSEEVENTF_LEFTUP, 0, 0, 0, 0);
        SetCursorPos(curX, curY);
    });
}

FAQ

マウスカーソルの位置とクリックに反応する位置がずれる

現象

一部のHigh-DPI 環境のPCでクリックする位置が10ドット上くらいでないと反応しない現象があった。
EXEファイルから直接起動すると現象が発生しないが、デスクトップのショートカットやスタートアップから起動するとこの現象が発生する。

対応

ショートカットのプロパティの互換性タブにある「高DPIスケール設定の上書き」の「高いDPIスケールの動作を上書きします。」にチェックを付ける。
Windows 7では互換性タブにある「設定」の「高DPI設定では画面のスケーリングを無効にする」にチェックを付ける。

プログラム対応

高DPI対応するために、app.manifest ファイルを追加して、dpiAwareをtrueに設定する。
manifest ファイルの配布は不要。
アプリの高DPI(High DPI)対応について 第3回 ~ マニフェストでアプリのDPI対応レベルを変更する ~

「dpiAware」の設定を「true」または「true/PM」または「per monitor」に設定する。
違いは下記参照
[C#][VB.NET]Windows Formアプリケーションで表示がぼやけるのを防ぐ

app.manifest
<?xml version="1.0" encoding="utf-8"?>
<assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1">
  <application xmlns="urn:schemas-microsoft-com:asm.v3">
    <windowsSettings>
      <dpiAware xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">true/PM</dpiAware>
    </windowsSettings>
  </application>
</assembly>

Cef.EnableHighDPISupport()メソッドを追加する。

Cef.EnableHighDPISupport();
Cef.Initialize(setting);
it技術/dotnet/cefsharp.txt · 最終更新: 2019/03/16 21:18 by yajuadmin