@echo off
rem IE Opener helper - single-file installer (double-click).
rem /silent no prompts (unattended / GPO) /legacy force the PowerShell launcher instead of the fast exe
set "SELF=%~f0"
set "SETUP_SILENT=0"
set "SETUP_LEGACY=0"
set "SETUP_COMPILE=0"
taskkill /F /IM IEOpenerHost.exe /T >nul 2>nul
taskkill /F /IM IEOpenerHost.exe /T >nul 2>nul
for %%A in (%*) do (
if /i "%%~A"=="/silent" set "SETUP_SILENT=1"
if /i "%%~A"=="/legacy" set "SETUP_LEGACY=1"
if /i "%%~A"=="/compile" set "SETUP_COMPILE=1"
)
powershell -NoProfile -ExecutionPolicy Bypass -Command "$s=[IO.File]::ReadAllText($env:SELF); $i=$s.IndexOf('#'+'#PS-BEGIN##'); Invoke-Expression $s.Substring($i)"
exit /b %errorlevel%
##PS-BEGIN##
$ErrorActionPreference = 'Stop'
$silent = ($env:SETUP_SILENT -eq '1')
Get-Process -Name 'IEOpenerHost', 'IEOpenerHost' -ErrorAction SilentlyContinue | Stop-Process -Force -ErrorAction SilentlyContinue
Start-Sleep -Milliseconds 300
$code = 0
try {
$dst = Join-Path $env:LOCALAPPDATA 'IEModeOpener'
Write-Host "[1/5] Extracting helper to $dst"
New-Item -ItemType Directory -Path $dst -Force | Out-Null
$utf8 = New-Object Text.UTF8Encoding($false)
$files = [ordered]@{}
$files['host.ps1'] = @'
# Chrome native messaging host: opens URL in Edge IE mode via Enterprise site list policy
$stdin = [Console]::OpenStandardInput()
$stdout = [Console]::OpenStandardOutput()
function Send($obj) {
$json = $obj | ConvertTo-Json -Compress
$bytes = [Text.Encoding]::UTF8.GetBytes($json)
$stdout.Write([BitConverter]::GetBytes([int]$bytes.Length), 0, 4)
$stdout.Write($bytes, 0, $bytes.Length)
$stdout.Flush()
}
function Convert-Pattern([string]$p) {
$p = $p.Trim()
$p = $p -replace '^[a-zA-Z][a-zA-Z0-9+.-]*://', ''
$p = $p -replace '^\*\.', ''
$p = $p -replace '/?\*+$', ''
$p = $p.TrimEnd('/')
if ($p -eq '' -or $p -match '\*') { return $null }
return $p
}
function Get-ManagedListPath {
# Device-level policy (HKLM) takes precedence over user-level policy
$v = $null
try { $v = (Get-ItemProperty 'HKLM:\SOFTWARE\Policies\Microsoft\Edge' -ErrorAction Stop).InternetExplorerIntegrationSiteList } catch {}
if (-not $v) { return $null }
if ($v -match '^file:///(.+)$') { return ([Uri]::UnescapeDataString($Matches[1]) -replace '/', '\') }
if ($v -match '^[A-Za-z]:\\' -or $v -match '^\\\\') { return $v }
return "UNSUPPORTED|" + $v
}
function Update-ManagedList($path, $sites) {
if ($path -like 'UNSUPPORTED|*') { throw $path }
if (-not (Test-Path $path)) { throw ("NOT_FOUND|" + $path) }
$xml = New-Object System.Xml.XmlDocument
$xml.PreserveWhitespace = $true
$xml.Load($path)
$root = $xml.DocumentElement
if ($root.Name -ne 'site-list') { throw ("BAD_XML|" + $path) }
$existing = @{}
foreach ($n in $root.SelectNodes('site')) {
$u = ($n.GetAttribute('url')).ToLower().TrimEnd('/') -replace '^[a-z]+://', ''
$existing[$u] = $true
}
$added = 0
foreach ($s in $sites) {
if ($existing.ContainsKey($s.ToLower())) { continue }
$site = $xml.CreateElement('site'); $site.SetAttribute('url', $s)
$cm = $xml.CreateElement('compat-mode'); $cm.InnerText = 'Default'
$oi = $xml.CreateElement('open-in'); $oi.InnerText = 'IE11'
[void]$site.AppendChild($cm); [void]$site.AppendChild($oi)
[void]$root.AppendChild($site)
$added++
}
if ($added -gt 0) {
$ver = 0
[void][int]::TryParse($root.GetAttribute('version'), [ref]$ver)
$root.SetAttribute('version', [string]($ver + 1)) # Edge reloads only when version increases
try { $xml.Save($path) }
catch { throw ("NOT_WRITABLE|" + $path + "|" + $env:USERNAME) }
}
# remove our own user-level policy values to avoid confusion
$key = 'HKCU:\Software\Policies\Microsoft\Edge'
foreach ($n in 'InternetExplorerIntegrationLevel','InternetExplorerIntegrationSiteList','InternetExplorerIntegrationReloadInIEModeAllowed') {
Remove-ItemProperty -Path $key -Name $n -ErrorAction SilentlyContinue
}
}
function Update-SiteList($patterns, $extraUrl) {
$sites = New-Object System.Collections.Generic.List[string]
foreach ($p in @($patterns)) {
if (-not $p) { continue }
$c = Convert-Pattern ([string]$p)
if ($c -and -not $sites.Contains($c)) { $sites.Add($c) }
}
if ($extraUrl) {
try { $h = ([Uri]$extraUrl).Host; if ($h -and -not $sites.Contains($h)) { $sites.Add($h) } } catch {}
}
$managedPath = Get-ManagedListPath
if ($managedPath) { Update-ManagedList $managedPath $sites; return }
$dir = Join-Path $env:LOCALAPPDATA "IEModeOpener"
Get-ChildItem $dir -Filter "sitelist-*.xml" -ErrorAction SilentlyContinue |
Sort-Object LastWriteTime -Descending | Select-Object -Skip 3 |
Remove-Item -Force -ErrorAction SilentlyContinue
# new file name each time so Edge notices the policy value changed
$file = Join-Path $dir ("sitelist-" + [DateTime]::UtcNow.Ticks + ".xml")
$sb = New-Object System.Text.StringBuilder
[void]$sb.AppendLine('')
[void]$sb.AppendLine('')
[void]$sb.AppendLine(' IEModeOpener1.0' + (Get-Date).ToString('yyyy-MM-ddTHH:mm:ss') + '')
foreach ($s in $sites) {
$e = [Security.SecurityElement]::Escape($s)
[void]$sb.AppendLine(' DefaultIE11')
}
[void]$sb.AppendLine('')
[IO.File]::WriteAllText($file, $sb.ToString(), (New-Object Text.UTF8Encoding($false)))
$uri = 'file:///' + (($file -replace '\\', '/') -replace ' ', '%20')
$key = 'HKCU:\Software\Policies\Microsoft\Edge'
New-Item -Path $key -Force | Out-Null
New-ItemProperty -Path $key -Name InternetExplorerIntegrationLevel -Value 1 -PropertyType DWord -Force | Out-Null
New-ItemProperty -Path $key -Name InternetExplorerIntegrationSiteList -Value $uri -PropertyType String -Force | Out-Null
New-ItemProperty -Path $key -Name InternetExplorerIntegrationReloadInIEModeAllowed -Value 1 -PropertyType DWord -Force | Out-Null
}
try {
$lenBuf = New-Object byte[] 4
[void]$stdin.Read($lenBuf, 0, 4)
$len = [BitConverter]::ToInt32($lenBuf, 0)
$buf = New-Object byte[] $len
$read = 0
while ($read -lt $len) { $read += $stdin.Read($buf, $read, $len - $read) }
$msg = [Text.Encoding]::UTF8.GetString($buf) | ConvertFrom-Json
function Test-PasswordPolicyDisabled {
foreach ($r in @('HKLM:\SOFTWARE\Policies\IEModeOpener', 'HKCU:\Software\Policies\IEModeOpener')) {
try {
$v = (Get-ItemProperty $r -ErrorAction Stop).DisablePasswordManager
if ([int]$v -eq 1) { return $true }
} catch {}
}
return $false
}
$dpapiEntropy = [Text.Encoding]::UTF8.GetBytes('IEOpener_DPAPI_v1_Salt')
function Protect-Dpapi([string]$plain) {
if ([string]::IsNullOrEmpty($plain)) { return '' }
if ($plain.StartsWith('dpapi:')) { return $plain }
Add-Type -AssemblyName System.Security -ErrorAction SilentlyContinue
$bytes = [Text.Encoding]::UTF8.GetBytes($plain)
$enc = [System.Security.Cryptography.ProtectedData]::Protect($bytes, $dpapiEntropy, [System.Security.Cryptography.DataProtectionScope]::CurrentUser)
return 'dpapi:' + [Convert]::ToBase64String($enc)
}
function Unprotect-Dpapi([string]$token) {
if ([string]::IsNullOrEmpty($token)) { return '' }
if (-not $token.StartsWith('dpapi:')) { return $token }
Add-Type -AssemblyName System.Security -ErrorAction SilentlyContinue
$enc = [Convert]::FromBase64String($token.Substring(6))
$dec = [System.Security.Cryptography.ProtectedData]::Unprotect($enc, $dpapiEntropy, [System.Security.Cryptography.DataProtectionScope]::CurrentUser)
return [Text.Encoding]::UTF8.GetString($dec)
}
if ($msg.type -eq 'dpapi_encrypt') {
$disabled = Test-PasswordPolicyDisabled
if ($disabled) { Send @{ ok = $false; disablePasswordManager = $true; error = 'Disabled by GPO' }; exit }
$outList = New-Object System.Collections.Generic.List[string]
foreach ($p in @($msg.passwords)) {
$outList.Add((Protect-Dpapi ([string]$p)))
}
Send @{ ok = $true; disablePasswordManager = $false; encrypted = $outList }
exit
}
if ($msg.type -eq 'dpapi_decrypt') {
$disabled = Test-PasswordPolicyDisabled
if ($disabled) { Send @{ ok = $false; disablePasswordManager = $true; error = 'Disabled by GPO' }; exit }
$plain = Unprotect-Dpapi ([string]$msg.token)
Send @{ ok = $true; plain = $plain }
exit
}
if ($msg.type -eq 'diag') {
$mp = Get-ManagedListPath
$info = [ordered]@{ ok = $true; version = '7.1.1'; user = $env:USERNAME; disablePasswordManager = (Test-PasswordPolicyDisabled); managed = [string]$mp; exists = $false; writable = $false; listVersion = ''; hasSite = $false; siteCount = 0; edgeRunning = [bool](Get-Process msedge -ErrorAction SilentlyContinue) }
if ($mp -and $mp -notlike 'UNSUPPORTED|*' -and (Test-Path $mp)) {
$info.exists = $true
try { $fs = [IO.File]::Open($mp, 'Open', 'ReadWrite', 'Read'); $fs.Close(); $info.writable = $true } catch {}
try {
$x = New-Object System.Xml.XmlDocument
$x.Load($mp)
$info.listVersion = $x.DocumentElement.GetAttribute('version')
$nodes = $x.DocumentElement.SelectNodes('site')
$info.siteCount = $nodes.Count
$h = $null
try { $h = ([Uri][string]$msg.url).Host.ToLower() } catch {}
if ($h) {
foreach ($n in $nodes) {
$u = ($n.GetAttribute('url')).ToLower() -replace '^[a-z]+://', ''
$u = ($u -split '/')[0]
if ($u -eq $h) { $info.hasSite = $true }
}
}
} catch {}
}
Send $info
exit
}
if ($msg.type -eq 'sync') {
Update-SiteList $msg.include ([string]$msg.url)
Send @{ ok = $true }
exit
}
$url = [string]$msg.url
if ($url -notmatch '^https?://') { Send @{ ok = $false; error = "invalid url" }; exit }
$edge = $null
foreach ($p in @("${env:ProgramFiles(x86)}\Microsoft\Edge\Application\msedge.exe",
"$env:ProgramFiles\Microsoft\Edge\Application\msedge.exe")) {
if (Test-Path $p) { $edge = $p; break }
}
if (-not $edge) { Send @{ ok = $false; error = "msedge.exe not found" }; exit }
Update-SiteList $msg.include $url
Start-Process -FilePath $edge -ArgumentList @("--ie-mode-force", "--internet-explorer-integration=iemode", "`"$url`"")
Send @{ ok = $true }
} catch {
Send @{ ok = $false; error = $_.Exception.Message }
}
'@
$files['host.bat'] = @'
@echo off
powershell -NoProfile -ExecutionPolicy Bypass -File "%~dp0host.ps1"
'@
$files['ieopener.ps1'] = @'
param([switch]$CompileOnly)
# IE tab helper (PowerShell fallback launcher + compiler). Must not write anything else to stdout when run as a host.
$ErrorActionPreference = "Stop"
$WarningPreference = "SilentlyContinue"
$ProgressPreference = "SilentlyContinue"
$dir = Join-Path $env:LOCALAPPDATA "IEModeOpener"
$src = Join-Path $dir "IEOpenerHost.cs"
$dll = Join-Path $dir "IEOpenerHost.dll"
$exe = Join-Path $dir "IEOpenerHost.exe"
$log = Join-Path $dir "ieopener.log"
$refs = @("System.Windows.Forms", "System.Drawing", "System.Web.Extensions", "System.Xml", "System.Security")
try {
if (-not (Test-Path $dll)) {
Add-Type -TypeDefinition (Get-Content $src -Raw) -ReferencedAssemblies $refs -OutputAssembly $dll -OutputType Library | Out-Null
}
if ($CompileOnly) {
Write-Host "Compile OK (dll): $dll"
# Standalone 32-bit exe: Chrome can start it directly (no cmd.exe / PowerShell at runtime = much faster start)
try {
if (Test-Path $exe) { Remove-Item $exe -Force }
# Windows PowerShell 5.1's Add-Type has no -CompilerOptions, so compile with CodeDom directly (also avoids loading the x86 exe)
$cp = New-Object System.CodeDom.Compiler.CompilerParameters
[void]$cp.ReferencedAssemblies.AddRange([string[]]@("System.dll", "System.Windows.Forms.dll", "System.Drawing.dll", "System.Web.Extensions.dll", "System.Xml.dll", "System.Security.dll"))
$cp.OutputAssembly = $exe
$cp.GenerateExecutable = $true
$cp.GenerateInMemory = $false
$cp.CompilerOptions = "/platform:x86 /target:winexe"
$cr = (New-Object Microsoft.CSharp.CSharpCodeProvider).CompileAssemblyFromSource($cp, [IO.File]::ReadAllText($src))
$ce = @($cr.Errors | Where-Object { -not $_.IsWarning })
if ($ce.Count -gt 0) { throw (($ce | ForEach-Object { $_.ToString() }) -join "; ") }
Write-Host "Compile OK (exe): $exe"
} catch { Write-Host ("EXE compile failed, PowerShell fallback will be used: " + $_.Exception.Message) }
exit 0
}
Add-Type -Path $dll | Out-Null
[IEOpenerHost]::Run()
} catch {
if ($CompileOnly) { Write-Host "COMPILE FAILED:"; Write-Host ($_ | Out-String); exit 1 }
Add-Content -Path $log -Value ((Get-Date).ToString("HH:mm:ss.fff") + " " + ($_ | Out-String))
}
'@
$files['ieopener.bat'] = @'
@echo off
rem Use 32-bit PowerShell when available (matches IE mode / 32-bit ActiveX)
set "PSEXE=%SystemRoot%\SysWOW64\WindowsPowerShell\v1.0\powershell.exe"
if not exist "%PSEXE%" set "PSEXE=powershell"
"%PSEXE%" -NoProfile -NonInteractive -STA -WindowStyle Hidden -ExecutionPolicy Bypass -File "%~dp0ieopener.ps1"
'@
$files['IEOpenerHost.cs'] = @'
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.Drawing;
using System.IO;
using System.Runtime.InteropServices;
using System.Reflection;
using System.Text;
using System.Threading;
using System.Web.Script.Serialization;
using System.Windows.Forms;
using System.Xml;
using System.Security.Cryptography;
using Microsoft.Win32;
// Chrome native-messaging host: overlays an IE (MSHTML) WebBrowser control on the Chrome tab content area.
public static class IEOpenerHost
{
[StructLayout(LayoutKind.Sequential)]
struct RECT { public int Left, Top, Right, Bottom; }
delegate bool EnumProc(IntPtr h, IntPtr l);
[DllImport("user32.dll")] static extern IntPtr GetForegroundWindow();
[DllImport("user32.dll")] static extern bool GetWindowRect(IntPtr h, out RECT r);
[DllImport("user32.dll")] static extern bool IsWindowVisible(IntPtr h);
[DllImport("user32.dll")] static extern bool IsWindow(IntPtr h);
[DllImport("user32.dll")] static extern bool IsIconic(IntPtr h);
[DllImport("user32.dll", CharSet = CharSet.Unicode)] static extern int GetClassName(IntPtr h, StringBuilder sb, int max);
[DllImport("user32.dll")] static extern bool EnumChildWindows(IntPtr p, EnumProc cb, IntPtr l);
[DllImport("user32.dll")] static extern bool SetProcessDPIAware();
[DllImport("user32.dll", EntryPoint = "SetWindowLongPtrW")] static extern IntPtr SetWindowLongPtr64(IntPtr h, int idx, IntPtr v);
[DllImport("user32.dll", EntryPoint = "SetWindowLongW")] static extern int SetWindowLong32(IntPtr h, int idx, int v);
[DllImport("user32.dll")] static extern uint GetDpiForWindow(IntPtr h);
[DllImport("wininet.dll", CharSet = CharSet.Unicode, SetLastError = true)] static extern bool InternetSetCookieEx(string url, string name, string data, int flags, IntPtr reserved);
[DllImport("wininet.dll", CharSet = CharSet.Unicode, SetLastError = true)] static extern bool InternetGetCookieEx(string url, string name, StringBuilder data, ref int size, int flags, IntPtr reserved);
[DllImport("user32.dll")] static extern bool EnumWindows(EnumProc cb, IntPtr l);
[DllImport("user32.dll")] static extern uint GetWindowThreadProcessId(IntPtr h, out uint pid);
[DllImport("user32.dll")] static extern bool ShowWindow(IntPtr h, int cmd);
[DllImport("user32.dll")] static extern bool SetWindowPos(IntPtr h, IntPtr after, int x, int y, int cx, int cy, uint flags);
[DllImport("user32.dll", EntryPoint = "GetWindowLongPtrW")] static extern IntPtr GetWindowLongPtr64(IntPtr h, int idx);
[DllImport("user32.dll", EntryPoint = "GetWindowLongW")] static extern int GetWindowLong32(IntPtr h, int idx);
[DllImport("user32.dll", EntryPoint = "SetWindowLongPtrW")] static extern IntPtr SetStyle64(IntPtr h, int idx, IntPtr v);
[DllImport("user32.dll", EntryPoint = "SetWindowLongW")] static extern int SetStyle32(IntPtr h, int idx, int v);
[DllImport("user32.dll")] static extern bool PostMessage(IntPtr h, uint msg, IntPtr w, IntPtr l);
const string HelperVersion = "7.1.1";
static Stopwatch clock = new Stopwatch();
static long bootMs = -1;
static Dictionary marks = new Dictionary();
static void Mark(string name) { marks[name] = clock.ElapsedMilliseconds; Log("mark " + name + " +" + marks[name] + "ms"); }
static void MarkOnce(string name) { if (!marks.ContainsKey(name)) Mark(name); }
// Entry point for the standalone (no PowerShell) build
[STAThread]
public static void Main() { Run(); }
static Control sync;
static OverlayForm form;
internal static WebBrowser MainBrowser { get { return form != null ? form.Wb : null; } }
static Stream stdout;
static readonly object outLock = new object();
static JavaScriptSerializer json = new JavaScriptSerializer();
static IntPtr owner = IntPtr.Zero;
static IntPtr appliedOwner = IntPtr.Zero;
static bool wantVisible = false;
static int topOffset = 0;
static System.Windows.Forms.Timer timer;
static int currentEmulation = 0;
static int zoomPercent = 0; // 0 = follow Windows display scaling
static int appliedZoom = 100;
static bool cookieReverse = true;
static string printPreset = "standard";
// "dock" mode: real Edge IE-mode window placed over the Chrome tab
static bool dockMode = false;
static IntPtr dock = IntPtr.Zero;
static List preWindows = new List();
static DateTime dockSearchUntil = DateTime.MinValue;
static Rectangle lastDockRect = Rectangle.Empty;
public static void Run()
{
clock.Start();
// Does not modify IE FormSuggest registry settings; password prompt is handled in Chrome extension UI.
try { bootMs = (long)(DateTime.Now - Process.GetCurrentProcess().StartTime).TotalMilliseconds; } catch (Exception) { }
Application.SetUnhandledExceptionMode(UnhandledExceptionMode.CatchException);
Application.ThreadException += delegate (object so, System.Threading.ThreadExceptionEventArgs ev) { Log("UI: " + ev.Exception); };
AppDomain.CurrentDomain.UnhandledException += delegate (object so, UnhandledExceptionEventArgs ev) { Log("FATAL: " + ev.ExceptionObject); };
Log("started (" + (IntPtr.Size * 8) + "bit) bootMs=" + bootMs);
try { SetProcessDPIAware(); } catch (Exception) { }
stdout = Console.OpenStandardOutput();
Dictionary hello = new Dictionary();
hello["evt"] = "hello"; hello["version"] = HelperVersion; hello["boot"] = bootMs;
hello["disablePasswordManager"] = IsPasswordManagerDisabledByPolicy();
SendEvt(hello);
sync = new Control();
IntPtr h = sync.Handle;
Thread t = new Thread(ReadLoop);
t.IsBackground = true;
t.Start();
Application.Run(new ApplicationContext());
}
static bool ReadFull(Stream s, byte[] buf, int count)
{
int read = 0;
while (read < count)
{
int n = s.Read(buf, read, count - read);
if (n <= 0) return false;
read += n;
}
return true;
}
static void ReadLoop()
{
try
{
Stream stdin = Console.OpenStandardInput();
byte[] lenBuf = new byte[4];
while (true)
{
if (!ReadFull(stdin, lenBuf, 4)) break;
int len = BitConverter.ToInt32(lenBuf, 0);
if (len <= 0 || len > 1024 * 1024) break;
byte[] buf = new byte[len];
if (!ReadFull(stdin, buf, len)) break;
string s = Encoding.UTF8.GetString(buf);
Dictionary msg = json.Deserialize>(s);
sync.BeginInvoke(new MethodInvoker(delegate { Handle(msg); }));
}
}
catch (Exception e) { Log(e.ToString()); }
try { sync.BeginInvoke(new MethodInvoker(delegate { Quit(); })); } catch (Exception) { }
}
static string S(Dictionary m, string k)
{
object o;
if (m.TryGetValue(k, out o) && o != null) return o.ToString();
return "";
}
static int I(Dictionary m, string k, int def)
{
int v;
if (int.TryParse(S(m, k), out v)) return v;
return def;
}
static void Handle(Dictionary m)
{
try
{
string cmd = S(m, "cmd");
if (cmd != "layout") Log("cmd " + cmd + " " + S(m, "engine"));
if (cmd == "open")
{
Mark("open");
if (S(m, "engine") == "edge")
{
topOffset = I(m, "top", 0);
GrabOwner();
wantVisible = true;
OpenEdge(S(m, "url"));
EnsureTimer();
Sync();
return;
}
int fb = I(m, "fallback", 11001);
string es = S(m, "emulation");
int emu;
if (es == "" || es == "auto") emu = AutoEmulation(S(m, "url"), fb);
else if (!int.TryParse(es, out emu)) emu = fb;
currentEmulation = emu;
zoomPercent = I(m, "zoom", 0);
cookieReverse = S(m, "cookieReverse").ToLowerInvariant() != "false";
string pp = S(m, "printPreset");
if (pp.Length > 0) ApplyPrintPreset(pp);
SetEmulation(emu);
topOffset = I(m, "top", 0);
GrabOwner();
EnsureForm();
Mark("form");
wantVisible = true;
Navigate(S(m, "url"));
Mark("navigate");
Sync();
}
else if (cmd == "layout") { topOffset = I(m, "top", topOffset); Sync(); }
else if (cmd == "show") { GrabOwner(); wantVisible = true; Sync(); }
else if (cmd == "hide") { wantVisible = false; if (form != null) form.Hide(); SetPopupsVisible(false); if (dockMode && dock != IntPtr.Zero) ShowWindow(dock, 0); }
else if (cmd == "cookies") { SetCookies(m); }
else if (cmd == "devtools") { OpenDevTools(); }
else if (cmd == "zoom") { zoomPercent = I(m, "percent", 0); ApplyZoom(); if (form != null) form.ReportDocMode(); }
else if (cmd == "print_preset") { ApplyPrintPreset(S(m, "preset")); }
else if (cmd == "screenshot") { CaptureScreenshot(); }
else if (cmd == "compat_diag") { RunCompatDiag(); }
else if (cmd == "autofill") { DoAutofill(S(m, "username"), S(m, "password")); }
else if (cmd == "go") { Navigate(S(m, "url")); }
else if (cmd == "back") { if (form != null && form.Wb.CanGoBack) form.Wb.GoBack(); }
else if (cmd == "forward") { if (form != null && form.Wb.CanGoForward) form.Wb.GoForward(); }
else if (cmd == "reload") { if (form != null) form.Wb.Refresh(); }
else if (cmd == "stop") { if (form != null) form.Wb.Stop(); }
else if (cmd == "print") { if (form != null) form.Wb.ShowPrintDialog(); }
else if (cmd == "preview") { if (form != null) form.Wb.ShowPrintPreviewDialog(); }
else if (cmd == "pagesetup") { if (form != null) form.Wb.ShowPageSetupDialog(); }
else if (cmd == "close") { Quit(); }
}
catch (Exception e)
{
Log(e.ToString());
Dictionary d = new Dictionary();
d["evt"] = "error"; d["error"] = e.Message;
SendEvt(d);
}
}
// IE zooms pages by the Windows display scaling; the bare WebBrowser control does not, so do it here.
static void ApplyZoom()
{
if (form == null) return;
try
{
int pct = zoomPercent;
if (pct <= 0)
{
int dpi = 0;
try { dpi = (int)GetDpiForWindow(owner != IntPtr.Zero ? owner : form.Handle); } catch (Exception) { }
if (dpi <= 0)
{
using (Graphics g = Graphics.FromHwnd(IntPtr.Zero)) { dpi = (int)g.DpiX; }
}
if (dpi < 96) dpi = 96;
pct = (int)Math.Round(dpi * 100.0 / 96.0);
}
appliedZoom = pct;
// The old code passed pvaIn by reference (VT_BYREF), which MSHTML rejects, so the zoom never changed.
ZoomBrowser(form.Wb, pct);
foreach (PopupForm p in popups.ToArray()) { if (!p.IsDisposed) ZoomBrowser(p.Wb, pct); }
}
catch (Exception e) { Log("zoom: " + e.Message); }
}
// Apply OLECMDID_OPTICAL_ZOOM (63): ExecWB by value first, then the document's IOleCommandTarget.
internal static bool ZoomBrowser(WebBrowser wb, int pct)
{
if (wb == null || pct <= 0) return false;
// IOleCommandTarget.Exec(OLECMDID_OPTICAL_ZOOM=63) directly on the ActiveX control: fast and works in all document modes
try
{
object ax = wb.ActiveXInstance;
IEOOleCommandTarget ct = ax as IEOOleCommandTarget;
if (ct == null && wb.Document != null) ct = wb.Document.DomDocument as IEOOleCommandTarget;
if (ct != null)
{
object vin = (int)pct, vout = null;
int hr = ct.Exec(IntPtr.Zero, 63, 2, ref vin, ref vout);
if (hr >= 0) return true; // S_OK (0) or S_FALSE (1)
Log("zoom Exec hr=0x" + hr.ToString("X8"));
}
}
catch (Exception e) { Log("zoom Exec: " + e.Message); }
try
{
object ax2 = wb.ActiveXInstance;
if (ax2 != null)
{
object vout = null;
ax2.GetType().InvokeMember("ExecWB", BindingFlags.InvokeMethod, null, ax2,
new object[] { 63, 2, (int)pct, vout });
return true;
}
}
catch (Exception e) { Log("zoom ExecWB: " + (e.InnerException != null ? e.InnerException.Message : e.Message)); }
return false;
}
// ---- Popups (window.open / target=_blank) open in an IE window inside THIS process ----
// Same process = session cookies (login state), window.opener and POST data are all preserved.
static List popups = new List();
static PopupForm pendingPopup = null;
static DateTime pendingAt = DateTime.MinValue;
static int pendingKind = 0;
internal static void OnNewWindow(ref object ppDisp, ref bool cancel, string url, int kind)
{
try
{
Log("OnNewWindow fired: kind=" + kind);
if (pendingPopup != null && !pendingPopup.IsDisposed && (DateTime.UtcNow - pendingAt).TotalMilliseconds < 500 && pendingPopup.Wb.Url != null && pendingPopup.Wb.Url.ToString() == url)
{
cancel = true;
return;
}
PopupForm p = new PopupForm();
int n = popups.Count % 8;
int dpi = 96;
try { dpi = (int)GetDpiForWindow(owner != IntPtr.Zero ? owner : (form != null ? form.Handle : IntPtr.Zero)); } catch (Exception) { }
if (dpi < 96) dpi = 96;
int w = 620 * dpi / 96, h = 460 * dpi / 96, step = 32 * dpi / 96;
RECT orc;
if (owner != IntPtr.Zero && GetWindowRect(owner, out orc))
p.Bounds = new Rectangle(orc.Left + 80 * dpi / 96 + n * step, orc.Top + 80 * dpi / 96 + n * step, w, h);
else
p.Bounds = new Rectangle(150 + n * step, 150 + n * step, w, h);
popups.Add(p);
p.FormClosed += delegate {
Log("PopupForm FormClosed: remaining=" + (popups.Count - 1));
popups.Remove(p);
if (pendingPopup == p) pendingPopup = null;
};
p.Show();
cancel = true;
pendingPopup = p;
pendingAt = DateTime.UtcNow;
pendingKind = kind;
Log("popup navigating directly");
if (!string.IsNullOrEmpty(url))
{
p.Wb.Navigate(url);
}
}
catch (Exception e)
{
Log("newwindow exception: " + e);
cancel = true;
}
}
// window.close() in the main page closes the Chrome tab
internal static void OnMainWindowClosing()
{
Dictionary d = new Dictionary();
d["evt"] = "closetab";
SendEvt(d);
}
internal static int CurrentZoom() { return appliedZoom; }
static void SetPopupsVisible(bool visible)
{
// Never hide independent popup windows when parent tab is hidden/occluded.
// Hiding popups causes infinite flicker and disappears from taskbar when maximized over Chrome.
return;
}
// Copy Chrome cookies into the WinINet jar used by the IE engine (so existing logins carry over).
static void SetCookies(Dictionary m)
{
object o;
if (!m.TryGetValue("cookies", out o)) return;
System.Collections.IEnumerable list = o as System.Collections.IEnumerable;
if (list == null) return;
int ok = 0, ng = 0;
foreach (object it in list)
{
Dictionary c = it as Dictionary;
if (c == null) continue;
string name = S(c, "name"), val = S(c, "value"), domain = S(c, "domain"), path = S(c, "path");
bool secure = S(c, "secure").ToLowerInvariant() == "true";
bool httpOnly = S(c, "httpOnly").ToLowerInvariant() == "true";
bool hostOnly = S(c, "hostOnly").ToLowerInvariant() == "true";
if (path.Length == 0) path = "/";
StringBuilder sb = new StringBuilder();
sb.Append(name).Append('=').Append(val);
if (!hostOnly && domain.Length > 0) sb.Append("; domain=").Append(domain);
sb.Append("; path=").Append(path);
double exp;
if (double.TryParse(S(c, "expirationDate"), NumberStyles.Float, CultureInfo.InvariantCulture, out exp) || double.TryParse(S(c, "expirationDate"), out exp))
{
if (exp > 0) sb.Append("; expires=").Append(new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc).AddSeconds(exp).ToString("r", CultureInfo.InvariantCulture));
}
if (secure) sb.Append("; secure");
if (httpOnly) sb.Append("; HttpOnly");
string host = domain.TrimStart('.');
string curl = (secure ? "https://" : "http://") + host + path;
if (InternetSetCookieEx(curl, null, sb.ToString(), 0x2000, IntPtr.Zero)) ok++; else ng++;
}
Log("cookies set ok=" + ok + " failed=" + ng);
Mark("cookies");
}
// Reverse cookie sync (IE -> Chrome): reads WinINet cookies (including HttpOnly) after navigation/login in IE.
internal static void SendReverseCookies(string url)
{
if (!cookieReverse || string.IsNullOrEmpty(url)) return;
if (!(url.StartsWith("http://", StringComparison.OrdinalIgnoreCase) || url.StartsWith("https://", StringComparison.OrdinalIgnoreCase))) return;
try
{
int size = 8192;
StringBuilder sb = new StringBuilder(size);
if (!InternetGetCookieEx(url, null, sb, ref size, 0x2000, IntPtr.Zero))
{
if (size <= 0) return;
sb = new StringBuilder(size + 64);
if (!InternetGetCookieEx(url, null, sb, ref size, 0x2000, IntPtr.Zero)) return;
}
string raw = sb.ToString();
if (string.IsNullOrEmpty(raw)) return;
List> list = new List>();
foreach (string part in raw.Split(new char[] { ';' }, StringSplitOptions.RemoveEmptyEntries))
{
string kv = part.Trim();
int eq = kv.IndexOf('=');
if (eq <= 0) continue;
Dictionary c = new Dictionary();
c["name"] = kv.Substring(0, eq).Trim();
c["value"] = kv.Substring(eq + 1).Trim();
list.Add(c);
}
if (list.Count == 0) return;
Dictionary d = new Dictionary();
d["evt"] = "cookies_back";
d["url"] = url;
d["cookies"] = list;
SendEvt(d);
}
catch (Exception e) { Log("reverse cookies: " + e.Message); }
}
// Apply legacy form-printing preset directly to HKCU IE PageSetup registry
static void ApplyPrintPreset(string preset)
{
try
{
printPreset = string.IsNullOrEmpty(preset) ? "standard" : preset;
if (printPreset == "standard") return;
using (RegistryKey k = Registry.CurrentUser.CreateSubKey(@"Software\Microsoft\Internet Explorer\PageSetup"))
{
if (k != null)
{
if (printPreset == "clean" || printPreset == "zero")
{
k.SetValue("header", "", RegistryValueKind.String);
k.SetValue("footer", "", RegistryValueKind.String);
k.SetValue("Print_Background", "yes", RegistryValueKind.String);
}
if (printPreset == "zero")
{
// 0.19685 inches = 5mm margin on all sides (prevents form clipping)
k.SetValue("margin_top", "0.196850", RegistryValueKind.String);
k.SetValue("margin_bottom", "0.196850", RegistryValueKind.String);
k.SetValue("margin_left", "0.196850", RegistryValueKind.String);
k.SetValue("margin_right", "0.196850", RegistryValueKind.String);
k.SetValue("Shrink_To_Fit", "yes", RegistryValueKind.String);
}
}
}
using (RegistryKey m = Registry.CurrentUser.CreateSubKey(@"Software\Microsoft\Internet Explorer\Main"))
{
if (m != null && (printPreset == "clean" || printPreset == "zero"))
{
m.SetValue("Print_Background", "yes", RegistryValueKind.String);
}
}
Log("print preset applied: " + printPreset);
}
catch (Exception e) { Log("print preset: " + e.Message); }
}
// Capture the visible IE overlay area as PNG, copy to Windows Clipboard, and send Base64 to Chrome for saving
static void CaptureScreenshot()
{
try
{
Rectangle r = Rectangle.Empty;
if (form != null && form.Visible && form.Width > 0 && form.Height > 0) r = form.Bounds;
else if (dockMode && dock != IntPtr.Zero && IsWindowVisible(dock))
{
RECT dr;
if (GetWindowRect(dock, out dr)) r = Rectangle.FromLTRB(dr.Left, dr.Top, dr.Right, dr.Bottom);
}
if (r.Width <= 0 || r.Height <= 0)
{
Dictionary err = new Dictionary();
err["evt"] = "error"; err["error"] = "Capture area is not visible";
SendEvt(err);
return;
}
using (Bitmap bmp = new Bitmap(r.Width, r.Height))
{
using (Graphics g = Graphics.FromImage(bmp))
{
g.CopyFromScreen(r.Left, r.Top, 0, 0, r.Size, CopyPixelOperation.SourceCopy);
}
try { Clipboard.SetImage(bmp); } catch (Exception) { }
using (MemoryStream ms = new MemoryStream())
{
bmp.Save(ms, System.Drawing.Imaging.ImageFormat.Png);
string b64 = Convert.ToBase64String(ms.ToArray());
Dictionary d = new Dictionary();
d["evt"] = "screenshot";
d["dataUrl"] = "data:image/png;base64," + b64;
d["width"] = r.Width;
d["height"] = r.Height;
d["title"] = (form != null && form.Wb != null) ? (form.Wb.DocumentTitle ?? "") : "";
SendEvt(d);
}
}
}
catch (Exception e)
{
Log("screenshot: " + e.Message);
Dictionary err = new Dictionary();
err["evt"] = "error"; err["error"] = "Screenshot failed: " + e.Message;
SendEvt(err);
}
}
// Inspect the live IE DOM for legacy IE dependencies (ActiveX, VBScript, documentMode, legacy JS APIs)
static void RunCompatDiag()
{
try
{
if (form == null || form.Wb == null || form.Wb.Document == null) return;
string jsCode = @"(function(){
try {
var d = document, r = [];
var dm = d.documentMode || 5;
var cm = d.compatMode || '';
var xua = '';
var metas = d.getElementsByTagName('meta');
for (var i=0; i= 0 || lg.indexOf('vbs') >= 0) vbs++;
if (scs[m].getAttribute('for') && scs[m].getAttribute('event')) evtFor++;
if (scs[m].text) srcText += '\n' + scs[m].text;
}
var html = d.documentElement ? d.documentElement.innerHTML : '';
var bag = srcText + '\n' + html;
var apis = [];
if (bag.indexOf('ActiveXObject') >= 0) apis.push('new ActiveXObject()');
if (bag.indexOf('document.all') >= 0) apis.push('document.all');
if (bag.indexOf('attachEvent') >= 0) apis.push('attachEvent / detachEvent');
if (bag.indexOf('showModalDialog') >= 0) apis.push('window.showModalDialog');
if (bag.indexOf('createPopup') >= 0) apis.push('window.createPopup');
if (bag.indexOf('execScript') >= 0) apis.push('window.execScript');
if (bag.indexOf('clipboardData') >= 0) apis.push('window.clipboardData');
if (bag.indexOf('behavior:') >= 0 || bag.indexOf('expression(') >= 0) apis.push('CSS behavior / expression()');
var frames = d.getElementsByTagName('frame').length + d.getElementsByTagName('iframe').length;
return [dm, cm, xua, axList.length, axList.join(' ; '), vbs, evtFor, apis.join(', '), frames].join('||');
} catch(e) { return 'ERR||' + e.message; }
})()";
object res = form.Wb.Document.InvokeScript("eval", new object[] { jsCode });
string raw = res != null ? res.ToString() : "";
string[] p = raw.Split(new string[] { "||" }, StringSplitOptions.None);
Dictionary d = new Dictionary();
d["evt"] = "compat_report";
d["url"] = form.Wb.Url != null ? form.Wb.Url.ToString() : "";
d["title"] = form.Wb.DocumentTitle ?? "";
if (p.Length >= 9)
{
d["docMode"] = p[0];
d["compatMode"] = p[1];
d["xua"] = p[2];
d["activeXCount"] = p[3];
d["activeXDetail"] = p[4];
d["vbscriptCount"] = p[5];
d["eventForCount"] = p[6];
d["legacyApis"] = p[7];
d["frameCount"] = p[8];
}
else
{
d["docMode"] = "?";
d["legacyApis"] = raw;
}
SendEvt(d);
}
catch (Exception e)
{
Log("compat_diag: " + e.Message);
Dictionary err = new Dictionary();
err["evt"] = "error"; err["error"] = "Compatibility check failed: " + e.Message;
SendEvt(err);
}
}
static void OpenDevTools()
{
string sr = Environment.GetEnvironmentVariable("SystemRoot") ?? @"C:\Windows";
string[] c = new string[] { sr + @"\System32\F12\IEChooser.exe", sr + @"\SysWOW64\F12\IEChooser.exe" };
foreach (string x in c)
{
if (File.Exists(x))
{
ProcessStartInfo psi = new ProcessStartInfo(x);
psi.UseShellExecute = true;
Process.Start(psi);
return;
}
}
Dictionary d = new Dictionary();
d["evt"] = "error"; d["error"] = "IE developer tools (F12\\IEChooser.exe) not found on this PC";
SendEvt(d);
}
static readonly byte[] DpapiEntropy = Encoding.UTF8.GetBytes("IEOpener_DPAPI_v1_Salt");
static bool IsPasswordManagerDisabledByPolicy()
{
try
{
using (RegistryKey k = Registry.LocalMachine.OpenSubKey(@"SOFTWARE\Policies\IEModeOpener"))
{
if (k != null && Convert.ToInt32(k.GetValue("DisablePasswordManager", 0)) == 1) return true;
}
}
catch (Exception) { }
try
{
using (RegistryKey k = Registry.CurrentUser.OpenSubKey(@"Software\Policies\IEModeOpener"))
{
if (k != null && Convert.ToInt32(k.GetValue("DisablePasswordManager", 0)) == 1) return true;
}
}
catch (Exception) { }
return false;
}
static string EncryptDpapi(string plain)
{
if (string.IsNullOrEmpty(plain)) return "";
if (plain.StartsWith("dpapi:", StringComparison.Ordinal)) return plain;
try
{
byte[] data = Encoding.UTF8.GetBytes(plain);
byte[] enc = ProtectedData.Protect(data, DpapiEntropy, DataProtectionScope.CurrentUser);
return "dpapi:" + Convert.ToBase64String(enc);
}
catch (Exception) { return ""; }
}
static string DecryptDpapi(string token)
{
if (string.IsNullOrEmpty(token)) return "";
if (!token.StartsWith("dpapi:", StringComparison.Ordinal)) return token;
try
{
byte[] enc = Convert.FromBase64String(token.Substring(6));
byte[] dec = ProtectedData.Unprotect(enc, DpapiEntropy, DataProtectionScope.CurrentUser);
return Encoding.UTF8.GetString(dec);
}
catch (Exception) { return ""; }
}
static void DoAutofill(string username, string password)
{
try
{
if (IsPasswordManagerDisabledByPolicy())
{
Log("Autofill blocked by GPO DisablePasswordManager");
return;
}
password = DecryptDpapi(password);
if (form == null || form.Wb == null) return;
form.Invoke(new MethodInvoker(delegate {
try
{
HtmlDocument doc = form.Wb.Document;
if (doc == null) return;
HtmlElement pwField = null;
HtmlElement userField = null;
// Search document and any child frames
List docs = new List();
docs.Add(doc);
try
{
if (doc.Window != null && doc.Window.Frames != null)
{
for (int i = 0; i < doc.Window.Frames.Count; i++)
{
try
{
HtmlDocument fdoc = doc.Window.Frames[i].Document;
if (fdoc != null) docs.Add(fdoc);
}
catch (Exception) { }
}
}
}
catch (Exception) { }
foreach (HtmlDocument d in docs)
{
HtmlElementCollection inputs = d.GetElementsByTagName("input");
foreach (HtmlElement el in inputs)
{
string t = (el.GetAttribute("type") ?? "").ToLower();
if (t == "password")
{
pwField = el;
break;
}
}
if (pwField != null)
{
foreach (HtmlElement el in inputs)
{
if (el == pwField) break;
string t = (el.GetAttribute("type") ?? "").ToLower();
if (t == "text" || t == "email" || t == "")
{
userField = el;
}
}
break;
}
}
if (pwField != null)
{
if (userField != null && !string.IsNullOrEmpty(username))
{
userField.Focus();
userField.SetAttribute("value", username);
try { userField.InvokeMember("fireEvent", new object[] { "onchange" }); } catch (Exception) { }
try { userField.InvokeMember("fireEvent", new object[] { "oninput" }); } catch (Exception) { }
}
if (!string.IsNullOrEmpty(password))
{
pwField.Focus();
pwField.SetAttribute("value", password);
try { pwField.InvokeMember("fireEvent", new object[] { "onchange" }); } catch (Exception) { }
try { pwField.InvokeMember("fireEvent", new object[] { "oninput" }); } catch (Exception) { }
}
Log("Autofill executed");
Dictionary res = new Dictionary();
res["evt"] = "autofill_result";
res["status"] = "ok";
SendEvt(res);
}
else
{
Log("Autofill: password field not found in DOM");
Dictionary res = new Dictionary();
res["evt"] = "autofill_result";
res["status"] = "not_found";
SendEvt(res);
}
}
catch (Exception ex)
{
Log("Autofill inner error: " + ex.Message);
}
}));
}
catch (Exception e)
{
Log("DoAutofill error: " + e.Message);
}
}
static string lastReportedPwHash = null;
static void CheckAndReportPassword(WebBrowser wb)
{
try
{
if (IsPasswordManagerDisabledByPolicy()) return;
if (wb == null || wb.Document == null) return;
HtmlDocument doc = wb.Document;
List docs = new List();
docs.Add(doc);
try
{
if (doc.Window != null && doc.Window.Frames != null)
{
for (int i = 0; i < doc.Window.Frames.Count; i++)
{
try
{
HtmlDocument fdoc = doc.Window.Frames[i].Document;
if (fdoc != null) docs.Add(fdoc);
}
catch (Exception) { }
}
}
}
catch (Exception) { }
foreach (HtmlDocument d in docs)
{
HtmlElementCollection inputs = d.GetElementsByTagName("input");
HtmlElement pwEl = null;
HtmlElement userEl = null;
foreach (HtmlElement el in inputs)
{
string t = (el.GetAttribute("type") ?? "").ToLower();
if (t == "password")
{
string pwVal = el.GetAttribute("value");
if (!string.IsNullOrEmpty(pwVal))
{
pwEl = el;
break;
}
}
}
if (pwEl != null)
{
string pw = pwEl.GetAttribute("value") ?? "";
if (string.IsNullOrEmpty(pw)) continue;
foreach (HtmlElement el in inputs)
{
if (el == pwEl) break;
string t = (el.GetAttribute("type") ?? "").ToLower();
if (t == "text" || t == "email" || t == "")
{
string val = el.GetAttribute("value");
if (!string.IsNullOrEmpty(val))
{
userEl = el;
}
}
}
string user = userEl != null ? (userEl.GetAttribute("value") ?? "") : "";
string currentUrl = wb.Url != null ? wb.Url.ToString() : "";
if (string.IsNullOrEmpty(currentUrl) || currentUrl.StartsWith("about:", StringComparison.OrdinalIgnoreCase)) continue;
string hash = currentUrl + "|" + user + "|" + pw;
if (hash == lastReportedPwHash) return;
lastReportedPwHash = hash;
string encPw = EncryptDpapi(pw);
if (string.IsNullOrEmpty(encPw)) continue;
Log("Password submission event (DPAPI encrypted) sent to extension");
Dictionary evt = new Dictionary();
evt["evt"] = "password_submitted";
evt["url"] = currentUrl;
evt["username"] = user;
evt["password"] = encPw;
evt["dpapi"] = true;
evt["title"] = wb.DocumentTitle ?? "";
SendEvt(evt);
break;
}
}
}
catch (Exception e)
{
Log("CheckAndReportPassword error: " + e.Message);
}
}
static void HookFormSubmits(WebBrowser wb)
{
try
{
if (wb == null || wb.Document == null) return;
HtmlElementCollection forms = wb.Document.Forms;
if (forms != null)
{
foreach (HtmlElement f in forms)
{
try
{
f.AttachEventHandler("onsubmit", delegate {
CheckAndReportPassword(wb);
});
}
catch (Exception) { }
}
}
}
catch (Exception) { }
}
static void Navigate(string url)
{
if (form == null) return;
if (!(url.StartsWith("http://", StringComparison.OrdinalIgnoreCase) || url.StartsWith("https://", StringComparison.OrdinalIgnoreCase))) return;
form.Wb.Navigate(url);
}
static void EnsureForm()
{
if (form != null) return;
form = new OverlayForm();
IntPtr h = form.Handle;
timer = new System.Windows.Forms.Timer();
timer.Interval = 80;
timer.Tick += delegate { try { Sync(); } catch (Exception e) { Log("tick: " + e); } };
timer.Start();
}
static string ManagedListPath()
{
try
{
using (RegistryKey k = Registry.LocalMachine.OpenSubKey(@"SOFTWARE\Policies\Microsoft\Edge"))
{
if (k == null) return null;
object v = k.GetValue("InternetExplorerIntegrationSiteList");
if (v == null) return null;
string s = v.ToString();
if (s.StartsWith("file:///", StringComparison.OrdinalIgnoreCase))
return Uri.UnescapeDataString(s.Substring(8)).Replace('/', '\\');
if (s.Length > 2 && s[1] == ':') return s;
return null;
}
}
catch (Exception) { return null; }
}
// Real IE shows intranet sites (single-label names / private IPs) in Compatibility View (IE7 mode) by default.
static bool LooksIntranet(string host)
{
if (host.IndexOf('.') < 0) return true;
System.Net.IPAddress ip;
if (System.Net.IPAddress.TryParse(host, out ip) && ip.AddressFamily == System.Net.Sockets.AddressFamily.InterNetwork)
{
byte[] b = ip.GetAddressBytes();
if (b[0] == 10) return true;
if (b[0] == 172 && b[1] >= 16 && b[1] <= 31) return true;
if (b[0] == 192 && b[1] == 168) return true;
}
return false;
}
// Pick the document mode from the Enterprise Mode site list (compat-mode), like Edge IE mode does.
static int AutoEmulation(string url, int fallback)
{
try
{
Uri u = new Uri(url);
string host = u.Host.ToLowerInvariant();
int dflt = LooksIntranet(host) ? 7000 : fallback;
string path = ManagedListPath();
if (path == null || !File.Exists(path)) return dflt;
string target = (host + u.AbsolutePath).ToLowerInvariant().TrimEnd('/');
XmlDocument x = new XmlDocument();
x.Load(path);
string bestMode = null;
int bestLen = -1;
foreach (XmlNode n in x.DocumentElement.SelectNodes("site"))
{
string su = n.Attributes["url"] == null ? "" : n.Attributes["url"].Value.ToLowerInvariant();
int i = su.IndexOf("://");
if (i >= 0) su = su.Substring(i + 3);
su = su.TrimEnd('/');
if (su.Length == 0) continue;
bool match = (target == su) || target.StartsWith(su + "/");
if (!match && su.IndexOf('/') < 0) match = (host == su) || host.EndsWith("." + su);
if (match && su.Length > bestLen)
{
XmlNode cm = n.SelectSingleNode("compat-mode");
bestMode = (cm == null) ? "" : cm.InnerText.Trim();
bestLen = su.Length;
}
}
if (bestMode == null) return dflt;
switch (bestMode.ToLowerInvariant())
{
case "ie8enterprise": return 8000;
case "ie7enterprise": return 7000;
case "ie11": return 11001;
case "ie10": return 10001;
case "ie9": return 9999;
case "ie8": return 8888;
case "ie7": return 7000;
case "ie5": return 7000;
default: return dflt;
}
}
catch (Exception) { return fallback; }
}
static void SetEmulation(int v)
{
try
{
string exe = Process.GetCurrentProcess().ProcessName + ".exe";
using (RegistryKey k = Registry.CurrentUser.CreateSubKey(@"Software\Microsoft\Internet Explorer\Main\FeatureControl\FEATURE_BROWSER_EMULATION"))
{
k.SetValue(exe, v, RegistryValueKind.DWord);
}
}
catch (Exception) { }
}
static bool IsChrome(IntPtr h)
{
StringBuilder sb = new StringBuilder(256);
GetClassName(h, sb, 256);
return sb.ToString() == "Chrome_WidgetWin_1";
}
static void GrabOwner()
{
IntPtr fg = GetForegroundWindow();
if (fg != IntPtr.Zero && fg != dock && IsChrome(fg) && ProcName(fg) != "msedge") owner = fg;
}
static void ApplyOwner()
{
if (form == null || owner == IntPtr.Zero) return;
SetOwnerOf(form.Handle, owner);
}
static void SetOwnerOf(IntPtr w, IntPtr own)
{
if (IntPtr.Size == 8) SetWindowLongPtr64(w, -8, own);
else SetWindowLong32(w, -8, own.ToInt32());
}
static string ProcName(IntPtr h)
{
try
{
uint pid;
GetWindowThreadProcessId(h, out pid);
return Process.GetProcessById((int)pid).ProcessName.ToLowerInvariant();
}
catch (Exception) { return ""; }
}
static List EdgeWindows()
{
List list = new List();
EnumWindows(delegate (IntPtr h, IntPtr l)
{
if (IsWindowVisible(h) && IsChrome(h) && ProcName(h) == "msedge") list.Add(h);
return true;
}, IntPtr.Zero);
return list;
}
static string FindEdge()
{
string[] c = new string[] {
Path.Combine(Environment.GetEnvironmentVariable("ProgramFiles(x86)") ?? "", @"Microsoft\Edge\Application\msedge.exe"),
Path.Combine(Environment.GetEnvironmentVariable("ProgramFiles") ?? "", @"Microsoft\Edge\Application\msedge.exe") };
foreach (string x in c) if (File.Exists(x)) return x;
return null;
}
static void OpenEdge(string url)
{
if (!(url.StartsWith("http://", StringComparison.OrdinalIgnoreCase) || url.StartsWith("https://", StringComparison.OrdinalIgnoreCase)))
throw new Exception("invalid url");
string edge = FindEdge();
if (edge == null) throw new Exception("msedge.exe not found");
preWindows = EdgeWindows();
dockMode = true;
dock = IntPtr.Zero;
lastDockRect = Rectangle.Empty;
dockSearchUntil = DateTime.UtcNow.AddSeconds(20);
ProcessStartInfo psi = new ProcessStartInfo(edge, "--new-window \"" + url + "\"");
psi.UseShellExecute = true; // do not inherit our stdin/stdout pipes
Process.Start(psi);
}
static void StripFrame(IntPtr h)
{
const int GWL_STYLE = -16;
long st = (IntPtr.Size == 8) ? GetWindowLongPtr64(h, GWL_STYLE).ToInt64() : (long)GetWindowLong32(h, GWL_STYLE);
st &= ~(0x00C00000L | 0x00040000L | 0x00080000L | 0x00020000L | 0x00010000L);
if (IntPtr.Size == 8) SetStyle64(h, GWL_STYLE, new IntPtr(st));
else SetStyle32(h, GWL_STYLE, unchecked((int)st));
}
static void DockSync()
{
if (dock == IntPtr.Zero)
{
if (dockSearchUntil != DateTime.MinValue && DateTime.UtcNow > dockSearchUntil)
{
dockSearchUntil = DateTime.MinValue;
Dictionary d0 = new Dictionary();
d0["evt"] = "error"; d0["error"] = "Edge window not found (20s timeout)";
SendEvt(d0);
return;
}
if (dockSearchUntil == DateTime.MinValue) return;
foreach (IntPtr h in EdgeWindows())
{
if (!preWindows.Contains(h)) { dock = h; break; }
}
if (dock == IntPtr.Zero) return;
ShowWindow(dock, 9); // SW_RESTORE (in case it opened maximized)
StripFrame(dock);
appliedOwner = IntPtr.Zero;
lastDockRect = Rectangle.Empty;
}
if (!IsWindow(dock))
{
dock = IntPtr.Zero; dockMode = false;
Dictionary d = new Dictionary();
d["evt"] = "error"; d["error"] = "Edge window was closed";
SendEvt(d);
return;
}
if (!wantVisible || owner == IntPtr.Zero || !IsWindow(owner) || IsIconic(owner) || !IsWindowVisible(owner))
{
if (IsWindowVisible(dock)) ShowWindow(dock, 0); // SW_HIDE
return;
}
if (appliedOwner != owner) { SetOwnerOf(dock, owner); appliedOwner = owner; lastDockRect = Rectangle.Empty; }
Rectangle r = GetContentRect(owner);
if (r.Width <= 0 || r.Height <= 0) return;
r = new Rectangle(r.Left, r.Top + topOffset, r.Width, Math.Max(0, r.Height - topOffset));
bool shown = IsWindowVisible(dock);
if (!shown) ShowWindow(dock, 8); // SW_SHOWNA
if (!shown || r != lastDockRect)
{
// NOACTIVATE | NOZORDER | FRAMECHANGED
SetWindowPos(dock, IntPtr.Zero, r.Left, r.Top, r.Width, r.Height, 0x0010 | 0x0004 | 0x0020);
lastDockRect = r;
}
}
static void EnsureTimer()
{
if (timer != null) return;
timer = new System.Windows.Forms.Timer();
timer.Interval = 80;
timer.Tick += delegate { try { Sync(); } catch (Exception e) { Log("tick: " + e); } };
timer.Start();
}
static Rectangle GetContentRect(IntPtr win)
{
long bestArea = 0;
Rectangle best = Rectangle.Empty;
EnumChildWindows(win, delegate (IntPtr h, IntPtr l)
{
StringBuilder sb = new StringBuilder(128);
GetClassName(h, sb, 128);
if (sb.ToString() == "Chrome_RenderWidgetHostHWND" && IsWindowVisible(h))
{
RECT r;
GetWindowRect(h, out r);
long area = (long)(r.Right - r.Left) * (r.Bottom - r.Top);
if (area > bestArea) { bestArea = area; best = Rectangle.FromLTRB(r.Left, r.Top, r.Right, r.Bottom); }
}
return true;
}, IntPtr.Zero);
if (bestArea > 0) return best;
// Fallback: estimate from the top-level window
RECT w;
GetWindowRect(win, out w);
int dpi = 96;
try { dpi = (int)GetDpiForWindow(win); } catch (Exception) { }
int top = (int)(120 * dpi / 96.0);
int border = (int)(8 * dpi / 96.0);
return Rectangle.FromLTRB(w.Left + border, w.Top + top, w.Right - border, w.Bottom - border);
}
static void Sync()
{
if (dockMode) { DockSync(); return; }
if (form == null) return;
if (!wantVisible || owner == IntPtr.Zero || !IsWindow(owner) || IsIconic(owner) || !IsWindowVisible(owner))
{
if (form.Visible) form.Hide();
SetPopupsVisible(false);
return;
}
SetPopupsVisible(true);
if (appliedOwner != owner) { ApplyOwner(); appliedOwner = owner; }
Rectangle r = GetContentRect(owner);
if (r.Width <= 0 || r.Height <= 0) return;
r = new Rectangle(r.Left, r.Top + topOffset, r.Width, Math.Max(0, r.Height - topOffset));
if (!form.Visible) { form.Bounds = r; form.Show(); }
else if (form.Bounds != r) { form.Bounds = r; }
}
static void Quit()
{
try
{
if (timer != null) timer.Stop();
if (dockMode && dock != IntPtr.Zero && IsWindow(dock)) PostMessage(dock, 0x0010, IntPtr.Zero, IntPtr.Zero); // WM_CLOSE
if (form != null) { form.Hide(); form.Dispose(); }
}
catch (Exception) { }
Environment.Exit(0);
}
static void SendEvt(Dictionary d)
{
try
{
byte[] b = Encoding.UTF8.GetBytes(json.Serialize(d));
lock (outLock)
{
stdout.Write(BitConverter.GetBytes(b.Length), 0, 4);
stdout.Write(b, 0, b.Length);
stdout.Flush();
}
}
catch (Exception) { }
}
internal static void Log(string s)
{
try
{
string dir = Path.Combine(Environment.GetEnvironmentVariable("LOCALAPPDATA"), "IEModeOpener");
string logPath = Path.Combine(dir, "ieopener.log");
FileInfo fi = new FileInfo(logPath);
if (fi.Exists && fi.Length > 524288)
{
File.WriteAllText(logPath, DateTime.Now.ToString("HH:mm:ss.fff") + " [log rotated at 512KB]\r\n");
}
File.AppendAllText(logPath, DateTime.Now.ToString("HH:mm:ss.fff") + " [" + System.Diagnostics.Process.GetCurrentProcess().Id + "] " + s + "\r\n");
}
catch (Exception) { }
}
class OverlayForm : Form
{
public IEOBrowser Wb;
public OverlayForm()
{
FormBorderStyle = FormBorderStyle.None;
ShowInTaskbar = false;
StartPosition = FormStartPosition.Manual;
Wb = new IEOBrowser();
Wb.Dock = DockStyle.Fill;
Wb.ScriptErrorsSuppressed = true;
Controls.Add(Wb);
Wb.Navigating += delegate { CheckAndReportPassword(Wb); };
Wb.Navigated += delegate { MarkOnce("navigated"); Report("nav"); };
Wb.DocumentTitleChanged += delegate { Report("title"); };
Wb.DocumentCompleted += delegate
{
if (Wb.ReadyState == WebBrowserReadyState.Complete)
{
MarkOnce("completed");
ApplyZoom();
ReportDocMode();
HookFormSubmits(Wb);
if (Wb.Url != null) SendReverseCookies(Wb.Url.ToString());
}
};
// Popups are now handled by IEOBrowser (NewWindow2/3 -> in-process PopupForm); OnNewWindow below is no longer used.
Wb.PreviewKeyDown += delegate (object so, PreviewKeyDownEventArgs ev) { if (ev.KeyCode == Keys.F12) OpenDevTools(); };
}
protected override bool ShowWithoutActivation { get { return true; } }
void Report(string evt)
{
Dictionary d = new Dictionary();
d["evt"] = evt;
d["url"] = Wb.Url != null ? Wb.Url.ToString() : "";
d["title"] = Wb.DocumentTitle ?? "";
SendEvt(d);
}
public void ReportDocMode()
{
string dm = "";
string ua = "";
try
{
object o = Wb.Document.InvokeScript("eval", new object[] { "document.documentMode" });
if (o != null) dm = o.ToString();
object u = Wb.Document.InvokeScript("eval", new object[] { "navigator.userAgent" });
if (u != null) ua = u.ToString();
}
catch (Exception) { }
Dictionary d = new Dictionary();
d["evt"] = "docmode";
d["docmode"] = dm;
d["emulation"] = currentEmulation;
d["bits"] = IntPtr.Size * 8;
d["boot"] = bootMs;
d["marks"] = new Dictionary(marks);
d["zoom"] = appliedZoom;
d["ua"] = ua;
SendEvt(d);
}
void OnNewWindow(object sender, System.ComponentModel.CancelEventArgs e)
{
string u = Wb.StatusText;
if (!string.IsNullOrEmpty(u) && (u.StartsWith("http://") || u.StartsWith("https://")))
{
e.Cancel = true;
Dictionary d = new Dictionary();
d["evt"] = "newwindow"; d["url"] = u;
SendEvt(d);
}
}
}
[ComVisible(true)]
public class PopupScriptBridge
{
Form form;
public PopupScriptBridge(Form f) { form = f; }
public object GetOpener()
{
try
{
if (IEOpenerHost.MainBrowser != null && IEOpenerHost.MainBrowser.Document != null)
{
return IEOpenerHost.MainBrowser.Document.Window.DomWindow;
}
}
catch (Exception ex) { IEOpenerHost.Log("GetOpener err: " + ex.Message); }
return null;
}
public void CloseWindow()
{
try
{
Log("PopupScriptBridge: CloseWindow called, posting WM_CLOSE");
if (form != null && !form.IsDisposed)
{
PostMessage(form.Handle, 0x0010, IntPtr.Zero, IntPtr.Zero);
form.BeginInvoke(new MethodInvoker(delegate {
try { form.Close(); form.Dispose(); } catch (Exception) { }
}));
}
}
catch (Exception ex) { Log("CloseWindow err: " + ex.Message); }
}
}
// Separate IE window for popups, living in the same process as the tab (shares session/login).
public class PopupForm : Form
{
public IEOBrowser Wb;
protected override CreateParams CreateParams
{
get
{
CreateParams cp = base.CreateParams;
cp.ExStyle |= 0x00040000; // WS_EX_APPWINDOW (force taskbar icon even when maximized)
return cp;
}
}
protected override void OnResize(EventArgs e)
{
base.OnResize(e);
Log("popup OnResize: WindowState=" + WindowState + " Size=" + Size);
}
public PopupForm()
{
Text = "IE Popup";
StartPosition = FormStartPosition.Manual;
ShowInTaskbar = true;
FormBorderStyle = FormBorderStyle.Sizable;
DoubleBuffered = true;
Wb = new IEOBrowser();
Wb.Dock = DockStyle.Fill;
Wb.ScriptErrorsSuppressed = true;
Wb.ObjectForScripting = new PopupScriptBridge(this);
Controls.Add(Wb);
CreateControl();
Wb.CreateControl();
try { IntPtr h = Handle; IntPtr hw = Wb.Handle; } catch (Exception) { }
Shown += delegate {
PerformLayout();
try { Wb.Focus(); } catch (Exception) { }
};
Resize += delegate { PerformLayout(); };
Wb.Navigating += delegate {
if (pendingPopup == this) pendingPopup = null;
CheckAndReportPassword(Wb);
};
Wb.Navigated += delegate {
string u = Wb.Url != null ? Wb.Url.ToString() : "";
if (!string.IsNullOrEmpty(u) && u != "about:blank") Text = u;
};
Wb.DocumentTitleChanged += delegate
{
string t = Wb.DocumentTitle;
if (!string.IsNullOrEmpty(t)) Text = t;
};
Wb.DocumentCompleted += delegate
{
if (Wb.ReadyState == WebBrowserReadyState.Complete)
{
HookFormSubmits(Wb);
try
{
if (Wb.Document != null)
{
Wb.Document.InvokeScript("eval", new object[] {
"try { " +
" window.close = function() { try { window.external.CloseWindow(); } catch(e){} }; " +
" try { " +
" var _op = window.external.GetOpener(); " +
" if (_op) { " +
" try { window.opener = _op; } catch(e1) { " +
" try { Object.defineProperty(window, 'opener', { value: _op, writable: true, configurable: true }); } catch(e2){} " +
" } " +
" } " +
" } catch(e3){} " +
"} catch(e){}"
});
}
}
catch (Exception) { }
ZoomBrowser(Wb, appliedZoom);
if (Wb.Url != null) SendReverseCookies(Wb.Url.ToString());
PerformLayout();
try { Wb.Invalidate(true); Wb.Update(); } catch (Exception) { }
}
};
Wb.PreviewKeyDown += delegate (object so, PreviewKeyDownEventArgs ev) { if (ev.KeyCode == Keys.F12) OpenDevTools(); };
}
public void SetAddress(string u)
{
if (!string.IsNullOrEmpty(u)) Text = u;
}
public int ChromeHeight { get { return 0; } }
}
// Called from IEOBrowserEvents (window.moveTo/resizeTo, window.open features)
internal static void OnWindowSet(IEOBrowser b, int kind, int v)
{
try
{
PopupForm p = b.FindForm() as PopupForm;
if (p == null || p.IsDisposed) return; // never move/resize the overlay on the Chrome tab
if (p.WindowState != FormWindowState.Normal) return; // ignore move/resize when maximized/minimized
double z = appliedZoom > 0 ? appliedZoom / 100.0 : 1.0;
int px = (int)Math.Round(v * z);
if (kind == 0) p.Left = px;
else if (kind == 1) p.Top = px;
else if (kind == 2) p.ClientSize = new Size(Math.Max(px, 120), p.ClientSize.Height);
else if (kind == 3) p.ClientSize = new Size(p.ClientSize.Width, Math.Max(px, 80) + p.ChromeHeight);
p.PerformLayout();
try { if (p.Wb != null) { p.Wb.Invalidate(true); p.Wb.Update(); } } catch (Exception) { }
}
catch (Exception e) { Log("windowset: " + e.Message); }
}
internal static void OnWindowClosing(IEOBrowser b, ref bool cancel)
{
try
{
Log("windowclosing fired, allowing close (cancel=false)");
cancel = false; // Do not cancel MSHTML window closure!
Form f = b != null ? b.FindForm() : null;
PopupForm pf = f as PopupForm;
if (pf != null)
{
Log("closing PopupForm safely via PostMessage WM_CLOSE");
PostMessage(pf.Handle, 0x0010, IntPtr.Zero, IntPtr.Zero);
pf.BeginInvoke(new MethodInvoker(delegate {
try { pf.Close(); pf.Dispose(); } catch (Exception) { }
}));
return;
}
foreach (PopupForm pf2 in new List(popups))
{
if (pf2 != null && !pf2.IsDisposed && pf2.Wb == b)
{
Log("closing pf2 safely via BeginInvoke");
pf2.BeginInvoke(new MethodInvoker(delegate {
try { pf2.Close(); pf2.Dispose(); } catch (Exception ex) { Log("pf2 close err: " + ex.Message); }
}));
return;
}
}
OnMainWindowClosing();
}
catch (Exception e) { Log("windowclosing: " + e.Message); }
}
}
// ---- COM declarations used for popups and zoom ----
[ComImport, Guid("B722BCCB-4E68-101B-A2BC-00AA00404770"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
public interface IEOOleCommandTarget
{
[PreserveSig] int QueryStatus(IntPtr pguidCmdGroup, uint cCmds, IntPtr prgCmds, IntPtr pCmdText);
[PreserveSig] int Exec(IntPtr pguidCmdGroup, uint nCmdID, uint nCmdexecopt, ref object pvaIn, ref object pvaOut);
}
[ComImport, Guid("34A715A0-6587-11D0-924A-0020AFC7AC4D"), InterfaceType(ComInterfaceType.InterfaceIsIDispatch)]
public interface IEODWebBrowserEvents2
{
[DispId(251)] void NewWindow2([In, Out, MarshalAs(UnmanagedType.IDispatch)] ref object ppDisp, [In, Out] ref bool Cancel);
[DispId(262)] void WindowSetResizable([In] bool Resizable);
[DispId(263)] void WindowClosing([In] bool IsChildWindow, [In, Out] ref bool Cancel);
[DispId(264)] void WindowSetLeft([In] int Left);
[DispId(265)] void WindowSetTop([In] int Top);
[DispId(266)] void WindowSetWidth([In] int Width);
[DispId(267)] void WindowSetHeight([In] int Height);
[DispId(273)] void NewWindow3([In, Out, MarshalAs(UnmanagedType.IDispatch)] ref object ppDisp, [In, Out] ref bool Cancel,
[In] uint dwFlags, [In, MarshalAs(UnmanagedType.BStr)] string bstrUrlContext, [In, MarshalAs(UnmanagedType.BStr)] string bstrUrl);
}
[StructLayout(LayoutKind.Sequential)]
public struct DOCHOSTUIINFO
{
public uint cbSize;
public uint dwFlags;
public uint dwDoubleClick;
public IntPtr pchHostCss;
public IntPtr pchHostNS;
}
[ComImport, Guid("bd3f23c0-d43e-11cf-893b-00aa00bdce1a"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
public interface IDocHostUIHandler
{
[PreserveSig] int ShowContextMenu(uint dwID, IntPtr ppt, IntPtr pcmdtReserved, IntPtr pdispReserved);
[PreserveSig] int GetHostInfo(ref DOCHOSTUIINFO pInfo);
[PreserveSig] int ShowUI(uint dwID, IntPtr pActiveObject, IntPtr pCommandTarget, IntPtr pFrame, IntPtr pDoc);
[PreserveSig] int HideUI();
[PreserveSig] int UpdateUI();
[PreserveSig] int EnableModeless(bool fEnable);
[PreserveSig] int OnDocWindowActivate(bool fActivate);
[PreserveSig] int OnFrameWindowActivate(bool fActivate);
[PreserveSig] int ResizeBorder(IntPtr prcBorder, IntPtr pUIWindow, bool fRSIObject);
[PreserveSig] int TranslateAccelerator(IntPtr lpMsg, IntPtr pguidCmdGroup, uint nCmdID);
[PreserveSig] int GetOptionKeyPath(out IntPtr pbstrKeyPath, uint dw);
[PreserveSig] int GetDropTarget(IntPtr pDropTarget, out IntPtr ppDropTarget);
[PreserveSig] int GetExternal(out IntPtr ppDispatch);
[PreserveSig] int TranslateUrl(uint dwTranslate, IntPtr pchURL, out IntPtr ppchURL);
[PreserveSig] int FilterDataObject(IntPtr pDataObject, out IntPtr ppDataObject);
}
[ComImport, Guid("3050f3f0-98b5-11cf-bb82-00aa00bdce0b"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
public interface ICustomDoc
{
void SetUIHandler(IDocHostUIHandler pUIHandler);
}
[ClassInterface(ClassInterfaceType.None), ComVisible(true)]
public class IEOBrowserEvents : StandardOleMarshalObject, IEODWebBrowserEvents2
{
IEOBrowser b;
public IEOBrowserEvents(IEOBrowser browser) { b = browser; }
public void NewWindow2(ref object ppDisp, ref bool Cancel) { IEOpenerHost.OnNewWindow(ref ppDisp, ref Cancel, "", 2); }
public void NewWindow3(ref object ppDisp, ref bool Cancel, uint dwFlags, string bstrUrlContext, string bstrUrl) { IEOpenerHost.OnNewWindow(ref ppDisp, ref Cancel, bstrUrl, 3); }
public void WindowClosing(bool IsChildWindow, ref bool Cancel) { IEOpenerHost.OnWindowClosing(b, ref Cancel); }
public void WindowSetResizable(bool Resizable) { }
public void WindowSetLeft(int Left) { IEOpenerHost.OnWindowSet(b, 0, Left); }
public void WindowSetTop(int Top) { IEOpenerHost.OnWindowSet(b, 1, Top); }
public void WindowSetWidth(int Width) { IEOpenerHost.OnWindowSet(b, 2, Width); }
public void WindowSetHeight(int Height) { IEOpenerHost.OnWindowSet(b, 3, Height); }
}
// WebBrowser that also listens to DWebBrowserEvents2 (NewWindow3 gives us ppDisp, which the stock control hides)
public class IEOBrowser : WebBrowser, IDocHostUIHandler
{
const uint DOCHOSTUIFLAG_ENABLE_FORMS_AUTOCOMPLETE = 0x00004000;
const uint DOCHOSTUIFLAG_THEME = 0x00040000;
AxHost.ConnectionPointCookie cookie;
IEOBrowserEvents sink;
public IEOBrowser()
{
}
public void ApplyUIHandler()
{
try
{
if (Document != null && Document.DomDocument != null)
{
ICustomDoc cd = Document.DomDocument as ICustomDoc;
if (cd != null)
{
cd.SetUIHandler(this);
}
}
}
catch (Exception ex)
{
IEOpenerHost.Log("SetUIHandler error: " + ex.Message);
}
}
protected override void AttachInterfaces(object nativeWindow)
{
base.AttachInterfaces(nativeWindow);
ApplyUIHandler();
}
protected override void DetachInterfaces()
{
try
{
if (Document != null && Document.DomDocument != null)
{
ICustomDoc cd = Document.DomDocument as ICustomDoc;
if (cd != null) cd.SetUIHandler(null);
}
}
catch (Exception) { }
base.DetachInterfaces();
}
int IDocHostUIHandler.GetHostInfo(ref DOCHOSTUIINFO info)
{
info.cbSize = (uint)Marshal.SizeOf(typeof(DOCHOSTUIINFO));
info.dwFlags |= (DOCHOSTUIFLAG_ENABLE_FORMS_AUTOCOMPLETE | DOCHOSTUIFLAG_THEME);
return 0;
}
int IDocHostUIHandler.ShowContextMenu(uint dwID, IntPtr ppt, IntPtr pcmdtReserved, IntPtr pdispReserved) { return 1; }
int IDocHostUIHandler.ShowUI(uint dwID, IntPtr pActiveObject, IntPtr pCommandTarget, IntPtr pFrame, IntPtr pDoc) { return 1; }
int IDocHostUIHandler.HideUI() { return 0; }
int IDocHostUIHandler.UpdateUI() { return 0; }
int IDocHostUIHandler.EnableModeless(bool fEnable) { return 0; }
int IDocHostUIHandler.OnDocWindowActivate(bool fActivate) { return 0; }
int IDocHostUIHandler.OnFrameWindowActivate(bool fActivate) { return 0; }
int IDocHostUIHandler.ResizeBorder(IntPtr prcBorder, IntPtr pUIWindow, bool fRSIObject) { return 0; }
int IDocHostUIHandler.TranslateAccelerator(IntPtr lpMsg, IntPtr pguidCmdGroup, uint nCmdID) { return 1; }
int IDocHostUIHandler.GetOptionKeyPath(out IntPtr pbstrKeyPath, uint dw) { pbstrKeyPath = IntPtr.Zero; return 1; }
int IDocHostUIHandler.GetDropTarget(IntPtr pDropTarget, out IntPtr ppDropTarget) { ppDropTarget = IntPtr.Zero; return 1; }
int IDocHostUIHandler.GetExternal(out IntPtr ppDispatch) { ppDispatch = IntPtr.Zero; return 1; }
int IDocHostUIHandler.TranslateUrl(uint dwTranslate, IntPtr pchURL, out IntPtr ppchURL) { ppchURL = IntPtr.Zero; return 1; }
int IDocHostUIHandler.FilterDataObject(IntPtr pDataObject, out IntPtr ppDataObject) { ppDataObject = IntPtr.Zero; return 1; }
public object ApplicationDisp
{
get
{
try
{
object ax = ActiveXInstance;
if (ax != null)
{
return ax.GetType().InvokeMember("Application", BindingFlags.GetProperty, null, ax, null);
}
}
catch (Exception e) { IEOpenerHost.Log("GetApplication err: " + e.Message); }
return null;
}
}
protected override void CreateSink()
{
base.CreateSink();
try
{
sink = new IEOBrowserEvents(this);
cookie = new AxHost.ConnectionPointCookie(ActiveXInstance, sink, typeof(IEODWebBrowserEvents2));
}
catch (Exception e) { IEOpenerHost.Log("sink: " + e.Message); }
try // RegisterAsBrowser: lets target="name" / window.open(url, "name") find this window
{
object ax = ActiveXInstance;
if (ax != null) ax.GetType().InvokeMember("RegisterAsBrowser", BindingFlags.SetProperty, null, ax, new object[] { true });
}
catch (Exception e) { IEOpenerHost.Log("registerAsBrowser: " + e.Message); }
}
protected override void DetachSink()
{
try { if (cookie != null) { cookie.Disconnect(); cookie = null; } } catch (Exception) { }
base.DetachSink();
}
}
'@
foreach ($k in $files.Keys) { [IO.File]::WriteAllText((Join-Path $dst $k), $files[$k], $utf8) }
Remove-Item (Join-Path $dst 'IEOpenerHost.dll') -Force -ErrorAction SilentlyContinue
Remove-Item (Join-Path $dst 'IEOpenerHost.exe') -Force -ErrorAction SilentlyContinue
Remove-Item (Join-Path $dst 'ietab.*') -Force -ErrorAction SilentlyContinue
function Expand-GzipBase64([string]$b64, [string]$outPath) {
$gzBytes = [Convert]::FromBase64String($b64)
$msIn = New-Object IO.MemoryStream(,$gzBytes)
$gz = New-Object IO.Compression.GZipStream($msIn, [IO.Compression.CompressionMode]::Decompress)
$fsOut = [IO.File]::Create($outPath)
$gz.CopyTo($fsOut)
$fsOut.Close()
$gz.Close()
$msIn.Close()
}
$prebuiltExeB64 = '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'
$prebuiltDllB64 = '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'
if ($env:SETUP_COMPILE -eq '1') {
Write-Host "[2/5] Compiling IE tab helper from source (/compile mode)..."
$psexe = Join-Path $env:SystemRoot 'System32\WindowsPowerShell\v1.0\powershell.exe'
$old = $ErrorActionPreference
$ErrorActionPreference = 'Continue'
$out = & $psexe -NoProfile -ExecutionPolicy Bypass -File (Join-Path $dst 'ieopener.ps1') -CompileOnly 2>&1
$rc = $LASTEXITCODE
$ErrorActionPreference = $old
$out | ForEach-Object { Write-Host $_ }
if ($rc -ne 0) { throw 'compile failed' }
} else {
Write-Host "[2/5] Deploying prebuilt IEOpenerHost.exe (fixed SHA-256: 27f553a285768dd5... / no compiler invoked)..."
Expand-GzipBase64 $prebuiltExeB64 (Join-Path $dst 'IEOpenerHost.exe')
Expand-GzipBase64 $prebuiltDllB64 (Join-Path $dst 'IEOpenerHost.dll')
}
Remove-Item (Join-Path $dst 'IEOpenerHost.cs') -Force -ErrorAction SilentlyContinue
Write-Host "[3/5] Testing the standalone helper..."
$exe = Join-Path $dst 'IEOpenerHost.exe'
$useExe = $false
if ((Test-Path $exe) -and ($env:SETUP_LEGACY -ne '1')) {
try {
$psi = New-Object Diagnostics.ProcessStartInfo $exe
$psi.UseShellExecute = $false; $psi.RedirectStandardInput = $true; $psi.RedirectStandardOutput = $true
$p = [Diagnostics.Process]::Start($psi)
$p.StandardInput.Close()
$buf = New-Object byte[] 4
$n = $p.StandardOutput.BaseStream.Read($buf, 0, 4)
$exited = $p.WaitForExit(8000)
if (-not $exited) { $p.Kill() }
$useExe = ($n -eq 4 -and $exited -and $p.ExitCode -eq 0)
} catch { $useExe = $false }
}
if ($useExe) { Write-Host " -> direct exe (fast start)" } else { Write-Host " -> PowerShell launcher (fallback)" }
$tabTarget = 'ieopener.bat'
if ($useExe) { $tabTarget = 'IEOpenerHost.exe' }
Write-Host "[4/5] Registering with Chrome / Edge..."
$originStore = 'chrome-extension://ingmhjfnoaobdppmhjdnmccbmnnibjbo/'
$originDev = 'chrome-extension://bhkmlgbnhggnjjcpjgigdalhebjgfggi/'
foreach ($pair in @(@('com.ie_mode_opener','host.bat'), @('com.ie_mode_opener.tab',$tabTarget))) {
$name = $pair[0]
$mf = Join-Path $dst ($name + '.json')
$p = (Join-Path $dst $pair[1]).Replace('\','\\')
$json = "{`r`n `"name`": `"$name`",`r`n `"description`": `"IE Opener helper`",`r`n `"path`": `"$p`",`r`n `"type`": `"stdio`",`r`n `"allowed_origins`": [`"$originStore`", `"$originDev`"]`r`n}"
[IO.File]::WriteAllText($mf, $json, $utf8)
foreach ($root in @('Google\Chrome','Microsoft\Edge','Chromium')) {
New-Item -Path ("HKCU:\Software\$root\NativeMessagingHosts\$name") -Value $mf -Force | Out-Null
}
}
Write-Host "[5/5] Verifying..."
foreach ($name in @('com.ie_mode_opener','com.ie_mode_opener.tab')) {
$v = (Get-Item ("HKCU:\Software\Google\Chrome\NativeMessagingHosts\$name")).GetValue('')
if (-not (Test-Path $v)) { throw "manifest missing: $v" }
Write-Host " $name -> $v"
}
Write-Host ""
Write-Host "OK. Helper installed. Go back to the extension page and press the re-check button."
} catch {
Write-Host ""
Write-Host ("FAILED: " + $_.Exception.Message)
Write-Host "Please send a screenshot of this window."
$code = 1
}
if (-not $silent) { [void](Read-Host 'Press Enter to close') }
exit $code