ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [C#] - WinForm Edge WebView2 사용방법 및 팁
    .NET/CSharp 2022. 3. 24. 15:14

    최근에 Windows 10 업데이트가 되면서 아래와 같이 WebView2 런타임이 설치된 걸 볼 수 있습니다. 필자는 최근에 회사 PC 윈도우를 다시 설치하여 2022-03-20 날짜로 나오지만 해당 날짜보다 이전에 설치된 걸로 파악이 됩니다. Windows 10 Enterprise 기준입니다.

    [ 그림 1 - 설치된 Microsoft Edge WebView2 런타임 ]

    Microsoft Edge supported Operating Systems

    https://docs.microsoft.com/en-us/deployedge/microsoft-edge-supported-operating-systems

    윈도우를 최신으로 업데이트하여도 Microsoft Edge WebView2 런타임이 설치가 되어 있지 않을 경우 아래의 경로에서 설치 파일을 다운로드하여 설치합니다. 필자의 Windows 환경은 Windows 10 Professional 이여서 자동으로 설치가 되질 않았던거 같습니다.

    Download the WebView2 Runtime

    https://developer.microsoft.com/en-us/microsoft-edge/webview2/

    해당 WebView2가 설치 되어 있으면 WebView2 모듈(Edge 브라우저 컨트롤)을 사용할 수 있는 환경이 되었다는 것을 뜻합니다.

    실제로는 해당 WebView2 모듈을 사용하기 위해서는 WebView2 런타임 모듈이 설치되어 있는지 프로그램 상에서 파악이 가능해야 합니다. 해당 설치 유무는 레지스트리 정보를 통하여 확인할 수 있습니다.

    Clients\{F3017226-FE2A-4295-8BDF-00C3A9A7E4C5}

     

    Windows x64

    HKEY_LOCAL_MACHINE\SOFTWARE\WOW6432Node\Microsoft\EdgeUpdate\Clients\{F3017226-FE2A-4295-8BDF-00C3A9A7E4C5}

    HKEY_CURRENT_USER\Software\Microsoft\EdgeUpdate\Clients\{F3017226-FE2A-4295-8BDF-00C3A9A7E4C5}

    Windows x86

    HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\EdgeUpdate\Clients\{F3017226-FE2A-4295-8BDF-00C3A9A7E4C5}
    HKEY_CURRENT_USER\Software\Microsoft\EdgeUpdate\Clients\{F3017226-FE2A-4295-8BDF-00C3A9A7E4C5}

     

    HKEY_CURRENT_USER 쪽에는 설치가 되어도 레지스트리 추가가 되어 있지 않아 이유에 대해서는 찾아보도록 하고

    HKEY_LOCAL_MACHINE 에 접근하기 위해서는 관리자 권한으로 실행을 해서 확인하면 되겠습니다.

    [ 그림 2 - WebView2 런타임 레지스트리 위치 ]

    도구(T)/NuGet 패키지 관리자(N) 들어가서 아래와 같이 찾아보기 탭에서 WebView2를 검색하여 설치할 프로젝트를 선택 후 설치를 진행합니다.

    [ 그림 3 - NuGet 패키지 관리자에서 WebView2 설치 ]

    설치가 완료되면 참조 항목을 열어보면 사진과 같이 WebView2 관련 라이브러리가 추가된 걸 볼 수 있습니다. WebView2 라이브러리를 사용하기 위해서는 최소 .NET Framework 버전은 4.5입니다.

     

    MainForm.cs

    using Microsoft.Web.WebView2.Core;
    using System;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Data;
    using System.Diagnostics;
    using System.Drawing;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    using System.Windows.Forms;
    
    namespace UseWebView2
    {
        public partial class MainForm : Form
        {
            private CoreWebView2Environment _env;
            private bool _initializeCoreWebView2;
            public MainForm(CoreWebView2Environment env)
            {
                InitializeComponent();
    
                this._env = env;
    
                // 원래 해당 부분은 관리자실행으로 하여 LocalMahcine, CurrentUser 둘다 체크해야함
                // TODO: CurrentUser 에 레지스트리가 왜 등록이 안되는지 찾아봐야함
                if (WebView2Util.InstalledEdgeWebView2Runtime())
                {
                    Toolkit.TraceWriteLine("Edge WebView2 런타임이 설치되어 있습니다.");
                }
                else
                {
                    Toolkit.TraceWriteLine("Edge WebView2 런타임이 설치되어 있지 않습니다.");
                }
    
                InitializeWebView2Core();
                Text = Text + " " + AppDomain.CurrentDomain.SetupInformation.TargetFrameworkName;
            }
    
            private async void InitializeWebView2Core()
            {
                _WebView2.CoreWebView2InitializationCompleted += WebView2_CoreWebView2InitializationCompleted;
                await _WebView2.EnsureCoreWebView2Async(_env);
    
                _LabelWebView2Version.Text = _LabelWebView2Version.Text + " " + _WebView2.ProductVersion.ToString();
            }
    
            #region MainForm Event
            private void MainForm_Load(object sender, EventArgs e)
            {
    
            }
    
            private void MainForm_Shown(object sender, EventArgs e)
            {
    
            }
    
            private void MainForm_FormClosing(object sender, FormClosingEventArgs e)
            {
    
            }
            #endregion
    
            #region WebView2 Event
    
            private void WebView2_CoreWebView2InitializationCompleted(object sender, CoreWebView2InitializationCompletedEventArgs e)
            {
                if (e.IsSuccess)
                {
                    _WebView2.CoreWebView2.NavigationStarting += CoreWebView2_NavigationStarting;
                    _WebView2.CoreWebView2.SourceChanged += CoreWebView2_SourceChanged;
                    _WebView2.CoreWebView2.ContentLoading += CoreWebView2_ContentLoading;
                    _WebView2.CoreWebView2.HistoryChanged += CoreWebView2_HistoryChanged;
                    _WebView2.CoreWebView2.NavigationCompleted += CoreWebView2_NavigationCompleted;
    
                    _WebView2.WebMessageReceived += new EventHandler<CoreWebView2WebMessageReceivedEventArgs>(CoreWebView2_WebMessageReceived);
    
                    // https://chromedevtools.github.io/devtools-protocol/
                    _WebView2.CoreWebView2.GetDevToolsProtocolEventReceiver("Log.entryAdded").DevToolsProtocolEventReceived += MainForm_DevToolsProtocolEventReceived;
                    _WebView2.CoreWebView2.CallDevToolsProtocolMethodAsync("Log.enable", "{}");
    
                    _WebView2.CoreWebView2.GetDevToolsProtocolEventReceiver("Runtime.consoleAPICalled").DevToolsProtocolEventReceived += MainForm_DevToolsProtocolEventReceived;
                    _WebView2.CoreWebView2.GetDevToolsProtocolEventReceiver("Runtime.exceptionThrown").DevToolsProtocolEventReceived += MainForm_DevToolsProtocolEventReceived;
                    _WebView2.CoreWebView2.CallDevToolsProtocolMethodAsync("Runtime.enable", "{}");
    
                    _initializeCoreWebView2 = true;
                }
                else
                {
                    MessageBox.Show(e.InitializationException.Message, "[ WebView2 에러 발생 ]");
                }
    
            }
    
            private void MainForm_DevToolsProtocolEventReceived(object sender, CoreWebView2DevToolsProtocolEventReceivedEventArgs e)
            {
                if (e != null && e.ParameterObjectAsJson != null)
                {
                    Toolkit.DebugWriteLine("MainForm_DevToolsProtocolEventReceived=" + e.ParameterObjectAsJson);
                }
            }
    
            private void CoreWebView2_NavigationStarting(object sender, CoreWebView2NavigationStartingEventArgs e)
            {
                if (Debugger.IsAttached)
                {
                    Toolkit.DebugWriteLine("1. CoreWebView2_NavigationStarting");
                }
            }
    
            private void CoreWebView2_SourceChanged(object sender, CoreWebView2SourceChangedEventArgs e)
            {
                if (Debugger.IsAttached)
                {
                    Toolkit.DebugWriteLine("2. CoreWebView2_SourceChanged");
                }
            }
    
            private void CoreWebView2_ContentLoading(object sender, CoreWebView2ContentLoadingEventArgs e)
            {
                if (Debugger.IsAttached)
                {
                    Toolkit.DebugWriteLine("3. CoreWebView2_ContentLoading");
                }
            }
    
            private void CoreWebView2_HistoryChanged(object sender, object e)
            {
                if (Debugger.IsAttached)
                {
                    Toolkit.DebugWriteLine("4. CoreWebView2_HistoryChanged");
                }
            }
    
            private void CoreWebView2_NavigationCompleted(object sender, CoreWebView2NavigationCompletedEventArgs e)
            {
                if (Debugger.IsAttached)
                {
                    Toolkit.DebugWriteLine("5. CoreWebView2_NavigationCompleted");
                }
            }
    
            private void CoreWebView2_WebMessageReceived(object sender, CoreWebView2WebMessageReceivedEventArgs e)
            {            
                if (Debugger.IsAttached)
                {
                    Toolkit.DebugWriteLine("CoreWebView2_WebMessageReceived e.Source=" + e.Source);
                    Toolkit.DebugWriteLine("CoreWebView2_WebMessageReceived e.WebMessageAsJson=" + e.WebMessageAsJson);
                }
            }
    
            #endregion
    
            private void ButtonWebView2Navigate_Click(object sender, EventArgs e)
            {
                if (_initializeCoreWebView2 && _TextBoxUri.Text.Length > 1)
                {
                    _WebView2.CoreWebView2.Navigate(_TextBoxUri.Text);
                }
            }
        }
    }

    생성자에서 호출된 InitializeWebView2Core 메서드에서는 WebView2 브라우저 컨트롤 초기화 작업을 진행하고 있습니다. EnsureCoreWebView2Async 메서드가 실제 WebView2.CoreWebView2를 초기화하는 작업을 가지며 초기화에 앞서 초기화가 되면 호출되는 CoreWebView2InitializationCompleted 이벤트핸들러를 등록합니다.

     

    CoreWebView2InitializationCompleted 이벤트 호출 시에 인자로 전달되는 CoreWebView2InitializationCompletedEventArgs 객체의 IsSuccess 속성을 통하여 성공적으로 초기화가 되었는지 여부를 알 수 있으며 초기화가 실패하였을 경우 InitializationException 속성을 통하여 해당 에러에 대한 원인을 알 수가 있습니다.

     

    EnsureCoreWebView2Async 메서드호출시에 CoreWebView2Environment 객체를 인자로 받아서 초기화를 할 수 있는데 해당 방법에 알아봅니다.

     

    Program.cs

    using Microsoft.Web.WebView2.Core;
    using System;
    using System.Collections.Generic;
    using System.IO;
    using System.Linq;
    using System.Threading.Tasks;
    using System.Windows.Forms;
    
    namespace UseWebView2
    {
        static class Program
        {
            /// <summary>
            /// 해당 애플리케이션의 주 진입점입니다.
            /// </summary>
            [STAThread]
            static void Main()
            {
                /*
                Process process = Process.GetCurrentProcess();
                string currentDirectory = Path.GetDirectoryName(process.MainModule.FileName);
                Directory.SetCurrentDirectory(currentDirectory);
                */
    
                // String userDataFolder = Path.GetTempPath() + @"WebView2UserData";
                String userDataFolder = null;
    
                // 실행인자로도 AdditionalBrowserArguments, AllowSingleSignOnUsingOSPrimaryAccount, Language, TargetCompatibleBrowserVersion
                // 설정 가능하게 해야함 이때 Name1=Value1;Name2=Value2 형식으로 Base64 인코딩 처리
    
                CoreWebView2EnvironmentOptions options = new CoreWebView2EnvironmentOptions();
    
                // TODO: 필요한 설정 옵션
                // - options.Language=ko,en
    
                // userDataFolder = EBWebView 캐쉬 디렉토리를 의미합니다.
                // 명시를 하지 않을경우 실행파일 위치 기준으로 
                // 어셈블리이름.exe.WebView2 디렉토리 생성하여 EBWebView 캐쉬 디렉토리 생성
                
                CoreWebView2Environment env = CoreWebView2Environment.CreateAsync("", userDataFolder, options).GetAwaiter().GetResult();
    
                Application.EnableVisualStyles();
                Application.SetCompatibleTextRenderingDefault(false);
                Application.Run(new MainForm(env));
            }
        }
    }

    위와 같이 CoreWebView2EnvironmentOptions 클래스 인스턴스를 생성후 UserDataFolder  값과 생성한 인스턴스를 통하여 CoreWebView2Environment 클래스 인스턴스를 만들어서 WebView2 브라우저 컨트롤을 초기화 합니다. 초기화 할때 사용한 파라미터에 대해 알아봅니다.

     

    UserDataFolder

    [ 그림 4 - 기본 UserDataFolder ]

    실행 디렉토리에 가보면 기본적으로 실행파일명.WebView2 이름으로 디렉토리가 생성되며 안에 EBWebView 디렉토리 안에 실행하는데 필요한 사용자 데이터들을 생성합니다. 생성되는 정보는 아래의 링크를 통하여 확인해 주시기 바랍니다. 해당 위치는 UserDataFolder 위치를 설정함으로써 변경 할 수 있습니다.

     

    위에 UseWebView2.exe.WebView2 는 VS2019 기준이며 VS2015 에서는 디버그 실행시UseWebView2.vshost.exe.WebView2 디렉토리가 별도로 더 생성됩니다.

     

    UDF(UserDataFolder)

    https://docs.microsoft.com/en-us/microsoft-edge/webview2/concepts/user-data-folder?tabs=win32 

     

    String userDataFolder = Path.GetTempPath() + @"WebView2UserData";

    코드를 주석을 풀어서 실행해보면 아래와 같이 해당 위치에 EBWebView 디렉토리가 생성되는걸 볼 수 있습니다.

     

    [ 그림 5 - UserDataFolder 생성되는 위치 변경 ]

     

     

    CoreWebView2EnvironmentOptions 클래스 프로퍼티 

    AdditionalBrowserArguments

    AllowSingleSignOnUsingOSPrimaryAccount : 

    Language : 

    TargetCompatibleBrowserVersion : 

     

     

    개발자 도구 프로토콜

    https://chromedevtools.github.io/devtools-protocol/

     

    CoreWebView2 브라우저 컨트롤 Uri 연결 시 확인하기 위한 이벤트를 정의하고 이후에 보이는 GetDevToolsProtocolEventReceiver, CallDevToolsProtocolMethodAsync 메서드에 대해서 설명합니다.

     

    Edge WebView2 브라우저 컨트롤 환경설정

    UDF/EBWebView/Default/Preferences 파일에는 현재 Edge WebView2 브라우저 컨트롤의 환경설정 값들이 저장되어 있습니다. 

     

    예를들어 개발자도구의 활성화되는 위치를 변경하고 싶을경우 아래와 같이  browser/app_window/_placement/EdgeDevToolsApp 안에 left,top,right,bottom 속성값을 변경후 실행 하면 적용되는걸 확인 할 수 있습니다.

     

     

    해당 Edge WebView2 라이브러리에 대한 연구는 계속하고 있으므로 내용이 계속 추가될 예정입니다.

     

    참고

    https://docs.microsoft.com/en-us/microsoft-edge/webview2/concepts/distribution

    GitHub Source
    https://github.com/soultomind/Blog/tree/develop/UseWebView2

    댓글

Designed by Tistory.