2using System.Collections.Generic;
3using System.ComponentModel;
5using System.Runtime.InteropServices;
8using System.Threading.Tasks;
10using Microsoft.Extensions.Hosting;
11using Microsoft.Extensions.Logging;
38 readonly ILogger<WindowsNetworkPromptReaper>
logger;
53 var gcChildhandlesList = GCHandle.FromIntPtr(lParam);
55 if (gcChildhandlesList.Target ==
null)
58 var childHandles = (List<IntPtr>)gcChildhandlesList.Target;
59 childHandles.Add(hWnd);
71 var childHandles =
new List<IntPtr>();
72 var gcChildhandlesList = GCHandle.Alloc(childHandles);
75 var pointerChildHandlesList = GCHandle.ToIntPtr(gcChildhandlesList);
76 NativeMethods.EnumWindowProc childProc =
new(
EnumWindow);
81 gcChildhandlesList.Free();
95 this.logger =
logger ??
throw new ArgumentNullException(nameof(
logger));
103 ArgumentNullException.ThrowIfNull(process);
108 throw new InvalidOperationException(
"This process has already been registered for network prompt reaping!");
109 logger.LogTrace(
"Registering process {pid}...", process.
Id);
116 logger.LogTrace(
"Unregistering process {pid}...", process.
Id);
120 TaskScheduler.Current);
124 protected override async Task
ExecuteAsync(CancellationToken cancellationToken)
126 logger.LogDebug(
"Starting network prompt reaper...");
129 while (!cancellationToken.IsCancellationRequested)
141 if (window == IntPtr.Zero)
150 logger.LogTrace(
"Identified \"Network Accessibility\" window in owned process {pid}", processId);
155 const int MaxLength = 10;
156 var stringBuilder =
new StringBuilder(MaxLength + 1);
160 logger.LogWarning(
new Win32Exception(),
"Error calling GetWindowText!");
164 var windowText = stringBuilder.ToString();
165 if (windowText ==
"Yes")
168 logger.LogTrace(
"Sending \"Yes\" button clicks...");
171 const int BM_CLICK = 0x00F5;
181 logger.LogDebug(
"Unable to find \"Yes\" button for \"Network Accessibility\" window in owned process {pid}!", processId);
184 catch (OperationCanceledException ex)
186 logger.LogTrace(ex,
"Cancelled!");
191 logger.LogTrace(
"Exiting network prompt reaper...");
Native methods used by the code.
static int GetWindowThreadProcessId(IntPtr hWnd, out int lpdwProcessId)
See https://docs.microsoft.com/en-us/windows/desktop/api/winuser/nf-winuser-getwindowthreadprocessid.
static bool EnumChildWindows(IntPtr window, EnumWindowProc callback, IntPtr lParam)
See https://docs.microsoft.com/en-us/windows/desktop/api/winuser/nf-winuser-enumchildwindows.
static IntPtr FindWindow(string? lpClassName, string lpWindowName)
See https://docs.microsoft.com/en-us/windows/desktop/api/winuser/nf-winuser-findwindoww.
static int SendMessage(IntPtr hWnd, int wMsg, IntPtr wParam, IntPtr lParam)
See https://docs.microsoft.com/en-us/windows/desktop/api/winuser/nf-winuser-sendmessage.
static int GetWindowText(IntPtr hWnd, StringBuilder lpString, int nMaxCount)
See https://docs.microsoft.com/en-us/windows/desktop/api/winuser/nf-winuser-getwindowtextw.
readonly ILogger< WindowsNetworkPromptReaper > logger
The ILogger for the WindowsNetworkPromptReaper.
readonly IAsyncDelayer asyncDelayer
The IAsyncDelayer for the WindowsNetworkPromptReaper.
void RegisterProcess(IProcess process)
Register a given process for network prompt reaping.
WindowsNetworkPromptReaper(IAsyncDelayer asyncDelayer, ILogger< WindowsNetworkPromptReaper > logger)
Initializes a new instance of the WindowsNetworkPromptReaper class.
const int SendMessageCount
Number of times to send the button click message. Should be at least 2 or it may fail to focus the wi...
static bool EnumWindow(IntPtr hWnd, IntPtr lParam)
Callback for NativeMethods.EnumChildWindows(IntPtr, NativeMethods.EnumWindowProc, IntPtr).
const int RecheckDelayMs
Check for prompts each time this amount of milliseconds pass.
static List< IntPtr > GetAllChildHandles(IntPtr mainWindow)
Get all the child windows handles of a given mainWindow .
override async Task ExecuteAsync(CancellationToken cancellationToken)
readonly List< IProcess > registeredProcesses
The list of IProcesss registered.
On Windows, DreamDaemon will show an unskippable prompt when using /world/proc/OpenPort()....
Task< int?> Lifetime
The Task<TResult> resulting in the exit code of the process or null if the process was detached.
Abstraction over a global::System.Diagnostics.Process.
For waiting asynchronously.
ValueTask Delay(TimeSpan timeSpan, CancellationToken cancellationToken)
Create a Task that completes after a given timeSpan .