using System; using System.IO; using System.Net; using System.Diagnostics; using System.Windows.Forms; using System.Threading.Tasks; namespace VNCServerList { public static class WebView2Installer { private const string DOWNLOAD_URL = "https://go.microsoft.com/fwlink/p/?LinkId=2124703"; private const string INSTALLER_NAME = "MicrosoftEdgeWebview2Setup.exe"; public static async Task InstallWebView2Async() { try { // 다운로드 진행 상황 표시 using (var progressForm = new WebView2DownloadForm()) { progressForm.Show(); // 임시 폴더에 설치 파일 다운로드 string tempFolder = Path.GetTempPath(); string installerPath = Path.Combine(tempFolder, INSTALLER_NAME); // 기존 파일이 있으면 삭제 if (File.Exists(installerPath)) { File.Delete(installerPath); } // 다운로드 using (var client = new WebClient()) { client.DownloadProgressChanged += (sender, e) => { progressForm.UpdateProgress(e.ProgressPercentage, e.BytesReceived, e.TotalBytesToReceive); }; await client.DownloadFileTaskAsync(new Uri(DOWNLOAD_URL), installerPath); } progressForm.Close(); // 설치 실행 var startInfo = new ProcessStartInfo { FileName = installerPath, Arguments = "/silent /install", // 자동 설치 UseShellExecute = true }; var process = Process.Start(startInfo); // 설치 완료 대기 (최대 5분) if (process != null) { await Task.Run(() => process.WaitForExit(300000)); // 5분 // 설치 파일 정리 try { if (File.Exists(installerPath)) { File.Delete(installerPath); } } catch { /* 무시 */ } return process.ExitCode == 0; } return false; } } catch (Exception ex) { MessageBox.Show( $"WebView2 런타임 설치 중 오류가 발생했습니다:\n{ex.Message}", "설치 오류", MessageBoxButtons.OK, MessageBoxIcon.Error); return false; } } public static bool IsWebView2Installed() { try { // 레지스트리에서 WebView2 설치 확인 using (var key = Microsoft.Win32.Registry.LocalMachine.OpenSubKey(@"SOFTWARE\WOW6432Node\Microsoft\EdgeUpdate\Clients\{F3017226-FE2A-4295-8BDF-00C3A9A7E4C5}")) { if (key != null) { return true; } } // 64비트 시스템에서 32비트 레지스트리 확인 using (var key = Microsoft.Win32.Registry.LocalMachine.OpenSubKey(@"SOFTWARE\Microsoft\EdgeUpdate\Clients\{F3017226-FE2A-4295-8BDF-00C3A9A7E4C5}")) { if (key != null) { return true; } } return false; } catch { return false; } } } public partial class WebView2DownloadForm : Form { private ProgressBar progressBar; private Label statusLabel; public WebView2DownloadForm() { InitializeComponent(); } private void InitializeComponent() { this.progressBar = new ProgressBar(); this.statusLabel = new Label(); this.SuspendLayout(); // Form 설정 this.Text = "WebView2 런타임 다운로드"; this.Size = new System.Drawing.Size(400, 150); this.StartPosition = FormStartPosition.CenterScreen; this.FormBorderStyle = FormBorderStyle.FixedDialog; this.MaximizeBox = false; this.MinimizeBox = false; // Status Label this.statusLabel.AutoSize = true; this.statusLabel.Location = new System.Drawing.Point(20, 20); this.statusLabel.Size = new System.Drawing.Size(360, 20); this.statusLabel.Text = "WebView2 런타임을 다운로드하고 있습니다..."; // Progress Bar this.progressBar.Location = new System.Drawing.Point(20, 50); this.progressBar.Size = new System.Drawing.Size(360, 30); this.progressBar.Style = ProgressBarStyle.Continuous; // Controls 추가 this.Controls.Add(this.statusLabel); this.Controls.Add(this.progressBar); this.ResumeLayout(false); this.PerformLayout(); } public void UpdateProgress(int percentage, long bytesReceived, long totalBytes) { if (this.InvokeRequired) { this.Invoke(new Action(() => UpdateProgress(percentage, bytesReceived, totalBytes))); return; } this.progressBar.Value = percentage; this.statusLabel.Text = $"다운로드 중... {percentage}% ({FormatBytes(bytesReceived)} / {FormatBytes(totalBytes)})"; } private string FormatBytes(long bytes) { string[] suffixes = { "B", "KB", "MB", "GB" }; int counter = 0; decimal number = bytes; while (Math.Round(number / 1024) >= 1) { number /= 1024; counter++; } return $"{number:n1} {suffixes[counter]}"; } } }