|
using System; |
|
using System.Collections.Generic; |
|
using System.Globalization; |
|
using System.Linq; |
|
using System.Runtime.InteropServices; |
|
using System.Text; |
|
using System.Threading; |
|
using System.Threading.Tasks; |
|
using MSHTML; |
|
using SHDocVw; |
|
|
|
namespace OnQuitDemo |
|
{ |
|
public delegate bool EnumWindowsProc(IntPtr hwnd, ref ProcessWindowInfo lParam); |
|
|
|
[StructLayout(LayoutKind.Sequential)] |
|
public struct ProcessWindowInfo |
|
{ |
|
public int processId; |
|
public IntPtr windowHandle; |
|
} |
|
|
|
[Flags] |
|
public enum SendMessageTimeoutFlags : uint |
|
{ |
|
SMTO_NORMAL = 0x0, |
|
SMTO_BLOCK = 0x1, |
|
SMTO_ABORTIFHUNG = 0x2, |
|
SMTO_NOTIMEOUTIFNOTHUNG = 0x8 |
|
} |
|
|
|
[ComImport] |
|
[Guid("6d5140c1-7436-11ce-8034-00aa006009fa")] |
|
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] |
|
public interface IServiceProvider |
|
{ |
|
void QueryService( |
|
ref Guid serviceInterfaceId, |
|
ref Guid requestedInterfaceId, |
|
[MarshalAs(UnmanagedType.Interface)] out object requestedObject); |
|
} |
|
|
|
[StructLayout(LayoutKind.Sequential)] |
|
public struct ProcessInformation |
|
{ |
|
public IntPtr hProcess; |
|
public IntPtr hThread; |
|
public int dwProcessId; |
|
public int dwThreadId; |
|
} |
|
|
|
public static class NativeMethods |
|
{ |
|
[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)] |
|
public static extern int SendMessageTimeout( |
|
IntPtr hWnd, |
|
uint Msg, |
|
UIntPtr wParam, |
|
IntPtr lParam, |
|
SendMessageTimeoutFlags fuFlags, |
|
uint uTimeout, |
|
out UIntPtr lpdwResult); |
|
|
|
[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)] |
|
public static extern uint RegisterWindowMessage(string lpString); |
|
|
|
[DllImport("oleacc.dll", PreserveSig = false)] |
|
[return: MarshalAs(UnmanagedType.Interface)] |
|
public static extern object ObjectFromLresult( |
|
UIntPtr lResult, |
|
[MarshalAs(UnmanagedType.LPStruct)] Guid refiid, |
|
IntPtr wParam); |
|
|
|
[DllImport("user32.dll")] |
|
[return: MarshalAs(UnmanagedType.Bool)] |
|
public static extern bool EnumWindows( |
|
EnumWindowsProc lpEnumFunction, |
|
ref ProcessWindowInfo lParam); |
|
|
|
[DllImport("user32.dll")] |
|
[return: MarshalAs(UnmanagedType.Bool)] |
|
public static extern bool EnumChildWindows( |
|
IntPtr handle, |
|
EnumWindowsProc lpEnumFunction, |
|
ref ProcessWindowInfo lParam); |
|
|
|
[DllImport("user32.dll")] |
|
public static extern int GetClassName( |
|
IntPtr hwnd, |
|
StringBuilder lpClassName, |
|
int nMaxCount); |
|
|
|
[DllImport("user32.dll", SetLastError = true)] |
|
public static extern uint GetWindowThreadProcessId( |
|
IntPtr handle, |
|
out uint processId); |
|
|
|
[DllImport("kernel32.dll", SetLastError = true)] |
|
public static extern bool CloseHandle(IntPtr handle); |
|
|
|
[DllImport("ieframe.dll", CharSet = CharSet.Unicode)] |
|
public static extern int IELaunchURL( |
|
[In]string url, |
|
out ProcessInformation procInfo, |
|
IntPtr ipInfo); |
|
} |
|
|
|
class Program |
|
{ |
|
static string DemoPageUrlTemplate = "http://localhost:{0}/main.html"; |
|
|
|
[STAThread] |
|
static void Main(string[] args) |
|
{ |
|
Console.WriteLine("Enter the port on which the local website is running:"); |
|
string portString = Console.ReadLine(); |
|
|
|
int port = 0; |
|
if (!int.TryParse(portString, out port)) |
|
{ |
|
Console.WriteLine("Port isn't a valid number!"); |
|
} |
|
else |
|
{ |
|
LaunchAndAttach(port); |
|
} |
|
|
|
while (true) |
|
{ |
|
Thread.Sleep(100); |
|
} |
|
} |
|
|
|
private static void LaunchAndAttach(int port) |
|
{ |
|
IntPtr windowHandle = LaunchInternetExplorer(port); |
|
IHTMLDocument2 doc = GetDocumentFromWindowHandle(windowHandle); |
|
IWebBrowser2 browser = GetBrowserFromDocument(doc); |
|
InternetExplorer app = browser as InternetExplorer; |
|
if (app != null) |
|
{ |
|
app.BeforeNavigate2 += new DWebBrowserEvents2_BeforeNavigate2EventHandler(BeforeNavigate2); |
|
app.DocumentComplete += new DWebBrowserEvents2_DocumentCompleteEventHandler(DocumentComplete); |
|
app.OnQuit += new DWebBrowserEvents2_OnQuitEventHandler(OnQuit); |
|
app.NewProcess += new DWebBrowserEvents2_NewProcessEventHandler(NewProcess); |
|
Console.WriteLine("IE is started, and events are connected."); |
|
} |
|
else |
|
{ |
|
Console.WriteLine("Was unable to connect to IE and wire up events."); |
|
} |
|
} |
|
|
|
private static IntPtr LaunchInternetExplorer(int port) |
|
{ |
|
ProcessInformation processInfo = new ProcessInformation(); |
|
NativeMethods.IELaunchURL(string.Format(DemoPageUrlTemplate, port), out processInfo, IntPtr.Zero); |
|
|
|
IntPtr foundWindowHandle = IntPtr.Zero; |
|
DateTime end = DateTime.Now.Add(TimeSpan.FromSeconds(10)); |
|
while (foundWindowHandle == IntPtr.Zero && DateTime.Now < end) |
|
{ |
|
ProcessWindowInfo info = new ProcessWindowInfo(); |
|
info.windowHandle = IntPtr.Zero; |
|
info.processId = processInfo.dwProcessId; |
|
EnumWindowsProc finder = FindBrowserWindow; |
|
NativeMethods.EnumWindows(finder, ref info); |
|
foundWindowHandle = info.windowHandle; |
|
} |
|
|
|
if (processInfo.hThread != IntPtr.Zero) |
|
{ |
|
NativeMethods.CloseHandle(processInfo.hThread); |
|
} |
|
|
|
if (processInfo.hProcess != IntPtr.Zero) |
|
{ |
|
NativeMethods.CloseHandle(processInfo.hProcess); |
|
} |
|
|
|
return foundWindowHandle; |
|
} |
|
|
|
static bool FindBrowserWindow(IntPtr handle, ref ProcessWindowInfo lParam) |
|
{ |
|
StringBuilder classBuilder = new StringBuilder(8); |
|
NativeMethods.GetClassName(handle, classBuilder, 8); |
|
if (classBuilder.ToString() != "IEFrame") |
|
{ |
|
return true; |
|
} |
|
|
|
EnumWindowsProc finder = FindChildWindowForProcess; |
|
return NativeMethods.EnumChildWindows(handle, finder, ref lParam); |
|
} |
|
|
|
private static bool FindChildWindowForProcess(IntPtr handle, ref ProcessWindowInfo lParam) |
|
{ |
|
StringBuilder classBuilder = new StringBuilder(25); |
|
NativeMethods.GetClassName(handle, classBuilder, 25); |
|
if (classBuilder.ToString() != "Internet Explorer_Server") |
|
{ |
|
return true; |
|
} |
|
|
|
uint processId = 0; |
|
uint threadId = NativeMethods.GetWindowThreadProcessId(handle, out processId); |
|
if (processId == lParam.processId) |
|
{ |
|
lParam.windowHandle = handle; |
|
return false; |
|
} |
|
|
|
return true; |
|
} |
|
|
|
static void DocumentComplete(object pDisp, ref object URL) |
|
{ |
|
Console.WriteLine("Called DocumentComplete event (URL = {0})", URL.ToString()); |
|
} |
|
|
|
static void BeforeNavigate2(object pDisp, ref object URL, ref object Flags, ref object TargetFrameName, ref object PostData, ref object Headers, ref bool Cancel) |
|
{ |
|
Console.WriteLine("Called BeforeNavigate2 event (URL = {0})", URL.ToString()); |
|
} |
|
|
|
private static void NewProcess(int lCauseFlag, object pWB2, ref bool Cancel) |
|
{ |
|
Console.WriteLine("Called NewProcess event"); |
|
if (pWB2 == null) |
|
{ |
|
Console.WriteLine("web browser pointer is null"); |
|
} |
|
} |
|
|
|
private static void OnQuit() |
|
{ |
|
Console.WriteLine("OnQuit event fired!"); |
|
} |
|
|
|
private static IHTMLDocument2 GetDocumentFromWindowHandle(IntPtr windowHandle) |
|
{ |
|
uint msg = NativeMethods.RegisterWindowMessage("WM_HTML_GETOBJECT"); |
|
UIntPtr documentPtr = UIntPtr.Zero; |
|
int result = NativeMethods.SendMessageTimeout(windowHandle, msg, UIntPtr.Zero, IntPtr.Zero, SendMessageTimeoutFlags.SMTO_ABORTIFHUNG, 1000, out documentPtr); |
|
if (result == 0) |
|
{ |
|
Console.WriteLine("SendMessageTimeout failed with error code {0}", Marshal.GetLastWin32Error()); |
|
} |
|
|
|
return NativeMethods.ObjectFromLresult(documentPtr, typeof(IHTMLDocument2).GUID, IntPtr.Zero) as IHTMLDocument2; |
|
} |
|
|
|
private static IWebBrowser2 GetBrowserFromDocument(IHTMLDocument2 document) |
|
{ |
|
if (document == null) |
|
{ |
|
Console.WriteLine("Document was null"); |
|
return null; |
|
} |
|
|
|
IServiceProvider windowServiceProvider = document.parentWindow as IServiceProvider; |
|
if (windowServiceProvider == null) |
|
{ |
|
Console.WriteLine("Cast to IServiceProvider on parentWindow returned null"); |
|
return null; |
|
} |
|
|
|
Guid browserAppInterfaceId = typeof(IWebBrowserApp).GUID; |
|
Guid webBrowserInterfaceId = typeof(IWebBrowser2).GUID; |
|
try |
|
{ |
|
object webBrowserObject; |
|
windowServiceProvider.QueryService(ref browserAppInterfaceId, ref webBrowserInterfaceId, out webBrowserObject); |
|
return webBrowserObject as IWebBrowser2; |
|
} |
|
catch (Exception) |
|
{ |
|
Console.WriteLine("QueryService failed with error code {0}", Marshal.GetLastWin32Error()); |
|
} |
|
|
|
return null; |
|
} |
|
} |
|
} |
Did you resolve this? I am getting the same result.