using System; using System.Collections.Generic; using System.Diagnostics; using System.Drawing; using System.IO; using System.Runtime.InteropServices; using System.Threading.Tasks; using System.Windows.Forms; using Microsoft.Web.WebView2.Core; using Microsoft.Web.WebView2.WinForms; namespace NetWebView2Lib { // --- 1. EVENTS INTERFACE (What C# sends to AutoIt) --- [Guid("B2C3D4E5-F6A7-4B6C-9D0E-1F2A3B4C5D6E")] [InterfaceType(ComInterfaceType.InterfaceIsIDispatch)] [ComVisible(true)] /// /// Events sent from C# to AutoIt. /// public interface IWebViewEvents { [DispId(1)] void OnMessageReceived(string message); } // --- 2. ACTIONS INTERFACE (What AutoIt can call in C#) --- [Guid("CCB12345-6789-4ABC-DEF0-1234567890AB")] [InterfaceType(ComInterfaceType.InterfaceIsIDispatch)] [ComVisible(true)] public interface IWebViewActions { [DispId(101)] void Initialize(object parentHandle, object initialUrlOrNull = null, int x = 0, int y = 0, int width = 0, int height = 0); [DispId(102)] void Navigate(string url); [DispId(103)] void NavigateToString(string htmlContent); [DispId(104)] void ExecuteScript(string script); [DispId(105)] void Resize(int width, int height); [DispId(106)] void Cleanup(); [DispId(107)] IBridgeActions GetBridge(); [DispId(108)] void ExportToPdf(string filePath); [DispId(109)] bool IsReady(); [DispId(110)] void SetContextMenuEnabled(bool enabled); [DispId(111)] void LockWebView(); [DispId(112)] void DisableBrowserFeatures(); [DispId(113)] void GoBack(); [DispId(114)] void ResetZoom(); [DispId(115)] void InjectCss(string cssCode); [DispId(116)] void ClearInjectedCss(); [DispId(117)] void ToggleAuditHighlights(bool enable); [DispId(118)] void SetAdBlock(bool active); [DispId(119)] void AddBlockRule(string domain); [DispId(120)] void ClearBlockRules(); [DispId(121)] void GoForward(); [DispId(122)] void GetHtmlSource(); [DispId(123)] void GetSelectedText(); [DispId(124)] void SetZoom(double factor); [DispId(125)] bool ParseJsonToInternal(string json); [DispId(126)] string GetInternalJsonValue(string path); [DispId(127)] void ClearBrowserData(); [DispId(128)] void Reload(); [DispId(129)] void Stop(); [DispId(130)] void ShowPrintUI(); [DispId(131)] void SetMuted(bool muted); [DispId(132)] bool IsMuted(); } // --- 3. THE MANAGER CLASS --- [Guid("A1B2C3D4-E5F6-4A5B-8C9D-0E1F2A3B4C5D")] [ComSourceInterfaces(typeof(IWebViewEvents))] [ClassInterface(ClassInterfaceType.None)] [ComVisible(true)] [ProgId("NetWebView2.Manager")] public class WebViewManager : IWebViewActions { // --- PRIVATE FIELDS --- private readonly WebView2 _webView; private readonly WebViewBridge _bridge; private readonly JsonParser _internalParser = new JsonParser(); private bool _isAdBlockActive = false; private List _blockList = new List(); private const string StyleId = "autoit-injected-style"; private bool _contextMenuEnabled = true; // --- EVENTS --- public delegate void OnMessageReceivedDelegate(string message); public event OnMessageReceivedDelegate OnMessageReceived; // --- NATIVE METHODS --- [DllImport("user32.dll")] private static extern bool GetClientRect(IntPtr hWnd, out Rect lpRect); [DllImport("user32.dll", SetLastError = true)] private static extern IntPtr SetParent(IntPtr hWndChild, IntPtr hWndNewParent); [StructLayout(LayoutKind.Sequential)] public struct Rect { public int Left, Top, Right, Bottom; } // --- CONSTRUCTOR --- /// /// Initializes a new instance of the WebViewManager class. /// public WebViewManager() { _webView = new WebView2(); _bridge = new WebViewBridge(); } // --- PROPERTIES --- /// Get the Bridge object for AutoIt interaction public IBridgeActions GetBridge() { return _bridge; } /// Check if WebView2 is initialized and ready public bool IsReady() => _webView?.CoreWebView2 != null; // --- INITIALIZATION --- /// /// Initializes the WebView2 control within the specified parent window handle. /// public async void Initialize(object parentHandle, object initialUrlOrNull = null, int x = 0, int y = 0, int width = 0, int height = 0) { try { if (parentHandle == null) throw new ArgumentException("Parent handle is null."); // Create a local copy of the handle to protect the original AutoIt variable long rawHandleValue = Convert.ToInt64(parentHandle); IntPtr localParentPtr = new IntPtr(rawHandleValue); if (localParentPtr == IntPtr.Zero) throw new ArgumentException("Invalid Parent Handle (IntPtr.Zero)."); if (width == 0 || height == 0) { if (GetClientRect(localParentPtr, out Rect rect)) { width = (width == 0) ? Math.Max(0, rect.Right - rect.Left) : width; height = (height == 0) ? Math.Max(0, rect.Bottom - rect.Top) : height; } } InvokeOnUiThread(() => { _webView.Location = new Point(x, y); _webView.Size = new Size(width, height); SetParent(_webView.Handle, localParentPtr); _webView.Visible = false; }); string userDataFolder = Path.Combine(Path.GetTempPath(), "WebView2_AutoIt_Cache"); var env = await CoreWebView2Environment.CreateAsync(null, userDataFolder); await _webView.EnsureCoreWebView2Async(env); //---load extension fail-------------------------------------------------- var options = new CoreWebView2EnvironmentOptions(userDataFolder); options.AreBrowserExtensionsEnabled = true; var path = @"D:\WebView2AutoIt_v0.1\uBOLite_2025.1224.1544.edge\"; await _webView.CoreWebView2.Profile.AddBrowserExtensionAsync(path); //----------------------------------------------------------------------- await Task.Delay(150); // Stabilization delay ConfigureSettings(); RegisterEvents(); InvokeOnUiThread(() => _webView.Visible = true); // Notify AutoIt that WebView2 is ready OnMessageReceived?.Invoke("INIT_READY"); if (initialUrlOrNull is string url && !string.IsNullOrEmpty(url)) { Navigate(url); } } catch (Exception ex) { Debug.WriteLine($"[WebView2] Init Error: {ex.Message}"); OnMessageReceived?.Invoke($"ERROR|INIT_FAILED|{ex.Message}"); } } // --- CONFIGURATION --- /// Configure WebView2 settings private void ConfigureSettings() { var settings = _webView.CoreWebView2.Settings; settings.IsWebMessageEnabled = true; // Enable Web Messages settings.AreDevToolsEnabled = true; // Enable DevTools by default settings.AreDefaultContextMenusEnabled = false; // Disable default context menus _webView.DefaultBackgroundColor = Color.Transparent; } /// Disable certain browser features for a controlled environment public void DisableBrowserFeatures() { InvokeOnUiThread(() => { if (_webView?.CoreWebView2 != null) { var settings = _webView.CoreWebView2.Settings; settings.AreDevToolsEnabled = false; // Disable DevTools settings.IsStatusBarEnabled = false; // Disable Status Bar settings.IsZoomControlEnabled = false; // Disable Zoom Control } }); } // --- EVENT REGISTRATION --- /// Register event handlers for WebView2 events private void RegisterEvents() { // Context Menu Event _webView.CoreWebView2.ContextMenuRequested += (s, e) => { if (!_contextMenuEnabled) { e.Handled = true; return; } string contextType = e.ContextMenuTarget.Kind.ToString(); string linkUri = e.ContextMenuTarget.HasLinkUri ? e.ContextMenuTarget.LinkUri : ""; string sourceUri = e.ContextMenuTarget.HasSourceUri ? e.ContextMenuTarget.SourceUri : ""; OnMessageReceived?.Invoke($"CONTEXT_MENU|{e.Location.X}|{e.Location.Y}|{contextType}|{linkUri}|{sourceUri}"); }; // Ad Blocking _webView.CoreWebView2.AddWebResourceRequestedFilter("*", CoreWebView2WebResourceContext.All); _webView.CoreWebView2.WebResourceRequested += (s, e) => { if (!_isAdBlockActive) return; string uri = e.Request.Uri.ToLower(); foreach (var domain in _blockList) { if (uri.Contains(domain)) { e.Response = _webView.CoreWebView2.Environment.CreateWebResourceResponse(null, 403, "Forbidden", ""); OnMessageReceived?.Invoke($"BLOCKED_AD|{uri}"); return; } } }; // Navigation and State Events _webView.CoreWebView2.DownloadStarting += (s, e) => OnMessageReceived?.Invoke($"DOWNLOAD_STARTING|{e.DownloadOperation.ResultFilePath}|{e.DownloadOperation.Uri}"); _webView.CoreWebView2.NewWindowRequested += (s, e) => { e.Handled = true; _webView.CoreWebView2.Navigate(e.Uri); }; _webView.NavigationStarting += (s, e) => OnMessageReceived?.Invoke("NAV_STARTING"); _webView.NavigationCompleted += (s, e) => { if (e.IsSuccess) { OnMessageReceived?.Invoke("NAV_COMPLETED"); OnMessageReceived?.Invoke("TITLE_CHANGED|" + _webView.CoreWebView2.DocumentTitle); } else OnMessageReceived?.Invoke("NAV_ERROR|" + e.WebErrorStatus); }; _webView.CoreWebView2.SourceChanged += (s, e) => OnMessageReceived?.Invoke("URL_CHANGED|" + _webView.Source); // --- Η ΔΙΟΡΘΩΣΗ ΕΔΩ --- // Αντί να στέλνουμε το WebMessage στο OnMessageReceived (Manager), // το στέλνουμε στο Bridge για να το πιάσει το Bridge_OnMessageReceived στο AutoIt. _webView.CoreWebView2.WebMessageReceived += (s, e) => { string message = e.TryGetWebMessageAsString(); _bridge.RaiseMessage(message); // Στέλνει το μήνυμα στο σωστό κανάλι }; _webView.CoreWebView2.AddHostObjectToScript("autoit", _bridge); } // --- PUBLIC API METHODS --- /// Clear browser data (cookies, cache, history, etc.) public async void ClearBrowserData() { await _webView.EnsureCoreWebView2Async(); // Clears cookies, history, cache, etc. await _webView.CoreWebView2.Profile.ClearBrowsingDataAsync(); OnMessageReceived?.Invoke("DATA_CLEARED"); } /// Lock down the WebView by disabling certain features public void LockWebView() { InvokeOnUiThread(() => { if (_webView?.CoreWebView2 != null) { var s = _webView.CoreWebView2.Settings; s.AreDefaultContextMenusEnabled = false; // Disable context menus s.AreDevToolsEnabled = false; // Disable DevTools s.IsZoomControlEnabled = false; // Disable Zoom Control s.IsBuiltInErrorPageEnabled = false; // Disable built-in error pages } }); } /// Stops any ongoing navigation or loading public void Stop() { _webView?.CoreWebView2.Stop(); } /// Shows the print UI dialog public void ShowPrintUI() { _webView?.CoreWebView2.ShowPrintUI(); } /// Sets the mute status for audio public void SetMuted(bool muted) { if (_webView?.CoreWebView2 != null) _webView.CoreWebView2.IsMuted = muted; } /// Gets the current mute status public bool IsMuted() { return _webView?.CoreWebView2?.IsMuted ?? false; } /// Reload the current page public void Reload() { // Check if CoreWebView2 is initialized to avoid null reference exceptions if (_webView != null && _webView.CoreWebView2 != null) { _webView.CoreWebView2.Reload(); } } /// Navigate back in history public void GoBack() => InvokeOnUiThread(() => { if (_webView?.CoreWebView2 != null && _webView.CoreWebView2.CanGoBack) _webView.CoreWebView2.GoBack(); }); /// Navigate forward in history public void GoForward() => InvokeOnUiThread(() => { if (_webView?.CoreWebView2 != null && _webView.CoreWebView2.CanGoForward) _webView.CoreWebView2.GoForward(); }); /// Reset zoom to default (100%) public void ResetZoom() => SetZoom(1.0); /// Clear all ad block rules public void ClearBlockRules() => _blockList.Clear(); /// Enable or disable the default context menu public void SetContextMenuEnabled(bool enabled) { _contextMenuEnabled = enabled; // Store preference InvokeOnUiThread(() => { if (_webView?.CoreWebView2 != null) _webView.CoreWebView2.Settings.AreDefaultContextMenusEnabled = enabled; }); } /// Navigate to a specified URL public void Navigate(string url) => InvokeOnUiThread(() => _webView.CoreWebView2?.Navigate(url)); /// Navigate to a string containing HTML content public void NavigateToString(string htmlContent) { _webView.Invoke(new Action(async () => { int attempts = 0; while (_webView.CoreWebView2 == null && attempts < 20) { await Task.Delay(50); attempts++; } _webView.CoreWebView2?.NavigateToString(htmlContent); })); } /// Execute arbitrary JavaScript code public void ExecuteScript(string script) { if (_webView?.CoreWebView2 != null) _webView.Invoke(new Action(() => _webView.CoreWebView2.ExecuteScriptAsync(script))); } /// Inject CSS code into the current page public void InjectCss(string cssCode) { string js = $"(function() {{ let style = document.getElementById('{StyleId}'); if (!style) {{ style = document.createElement('style'); style.id = '{StyleId}'; document.head.appendChild(style); }} style.innerHTML = `{cssCode}`; }})();"; ExecuteScript(js); } /// Remove previously injected CSS public void ClearInjectedCss() => ExecuteScript($"(function() {{ let style = document.getElementById('{StyleId}'); if (style) style.remove(); }})();"); /// Toggle audit highlights on/off public void ToggleAuditHighlights(bool enable) { if (enable) InjectCss("img, h1, h2, h3, table, a { outline: 3px solid #FF6A00 !important; outline-offset: -3px !important; }"); else ClearInjectedCss(); } /// Retrieve the full HTML source of the current page public async void GetHtmlSource() { if (_webView?.CoreWebView2 == null) return; string html = await _webView.CoreWebView2.ExecuteScriptAsync("document.documentElement.outerHTML"); OnMessageReceived?.Invoke("HTML_SOURCE|" + CleanJsString(html)); } /// Retrieve the currently selected text on the page public async void GetSelectedText() { if (_webView?.CoreWebView2 == null) return; string selectedText = await _webView.CoreWebView2.ExecuteScriptAsync("window.getSelection().toString()"); OnMessageReceived?.Invoke("SELECTED_TEXT|" + CleanJsString(selectedText)); } /// Clean up JavaScript string results private string CleanJsString(string input) { string decoded = System.Text.RegularExpressions.Regex.Unescape(input); if (decoded.StartsWith("\"") && decoded.EndsWith("\"") && decoded.Length >= 2) decoded = decoded.Substring(1, decoded.Length - 2); return decoded; } /// Resize the WebView control public void Resize(int w, int h) => InvokeOnUiThread(() => _webView.Size = new Size(w, h)); /// Clean up resources public void Cleanup() => _webView?.Dispose(); /// Set the zoom factor public void SetZoom(double factor) => InvokeOnUiThread(() => _webView.ZoomFactor = factor); /// Export the current page to a PDF file public void ExportToPdf(string filePath) { InvokeOnUiThread(async () => { try { if (_webView?.CoreWebView2 != null) { await _webView.CoreWebView2.PrintToPdfAsync(filePath, null); OnMessageReceived?.Invoke("PDF_SUCCESS|" + filePath); } } catch (Exception ex) { OnMessageReceived?.Invoke("PDF_ERROR|" + ex.Message); } }); } /// Ad Block Methods public void SetAdBlock(bool active) => _isAdBlockActive = active; /// Add a domain to the block list public void AddBlockRule(string domain) { if (!string.IsNullOrEmpty(domain)) _blockList.Add(domain.ToLower()); } /// Parse JSON into the internal parser public bool ParseJsonToInternal(string json) => _internalParser.Parse(json); /// Get a value from the internal JSON parser public string GetInternalJsonValue(string path) => _internalParser.GetTokenValue(path); // --- HELPER METHODS --- /// Invoke actions on the UI thread private void InvokeOnUiThread(Action action) { if (_webView == null || _webView.IsDisposed) return; if (_webView.InvokeRequired) _webView.Invoke(action); else action(); } } }