terminal/src/host/ft_uia/Elements/WinEventSystem.cs
Dustin Howett d4d59fa339 Initial release of the Windows Terminal source code
This commit introduces all of the Windows Terminal and Console Host source,
under the MIT license.
2019-05-02 15:29:04 -07:00

178 lines
6.8 KiB
C#

//----------------------------------------------------------------------------------------------------------------------
// <copyright file="WinEventSystem.cs" company="Microsoft">
// Copyright (c) Microsoft Corporation. All rights reserved.
// </copyright>
// <summary>Helper and wrapper for attaching a WinEvent framework around a console application.</summary>
//----------------------------------------------------------------------------------------------------------------------
namespace Conhost.UIA.Tests.Elements
{
using Common.NativeMethods;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using WEX.Logging.Interop;
using WEX.TestExecution;
public interface IWinEventCallbacks
{
void CaretSelection(int x, int y);
void CaretVisible(int x, int y);
void UpdateRegion(int left, int top, int right, int bottom);
void UpdateSimple(int x, int y, int character, int attribute);
void UpdateScroll(int deltaX, int deltaY);
void Layout();
void StartApplication(int processId, int childId);
void EndApplication(int processId, int childId);
}
public class WinEventSystem : IDisposable
{
public WinEventSystem(IWinEventCallbacks callbacks, uint pid)
{
this.AttachWinEventHook(callbacks, pid);
}
~WinEventSystem()
{
this.Dispose(false);
}
public void Dispose()
{
this.Dispose(true);
GC.SuppressFinalize(this);
}
private bool isDisposed = false;
protected virtual void Dispose(bool disposing)
{
if (!this.isDisposed)
{
DetachWinEventHook();
this.isDisposed = true;
}
}
bool messagePumpDone = false;
Task messagePumpTask = null;
private void AttachWinEventHook(IWinEventCallbacks callbacks, uint pid)
{
if (messagePumpTask == null)
{
messagePumpDone = false;
messagePumpTask = Task.Run(() => MessagePump(callbacks, pid));
}
}
private void DetachWinEventHook()
{
if (messagePumpTask != null)
{
messagePumpDone = true;
messagePumpTask.Wait();
messagePumpTask = null;
}
}
// This must be public or marshalling cannot call back to it.
public class WinEventCallback
{
private uint pid;
private IWinEventCallbacks callbacks;
public WinEventCallback(IWinEventCallbacks callbacks, uint pidOfInterest)
{
this.pid = pidOfInterest;
this.callbacks = callbacks;
}
public void WinEventProc(IntPtr hWinEventHook, User32.WinEventId eventType, IntPtr hwnd, int idObject, int idChild, uint dwEventThread, uint dwmsEventTime)
{
uint dwProcessId;
User32.GetWindowThreadProcessId(hwnd, out dwProcessId);
if (dwProcessId != pid)
{
return;
}
switch (eventType)
{
case User32.WinEventId.EVENT_CONSOLE_CARET:
switch (idObject)
{
case 1:
callbacks.CaretSelection(idChild.LoWord(), idChild.HiWord());
break;
case 2:
callbacks.CaretVisible(idChild.LoWord(), idChild.HiWord());
break;
default:
Verify.Fail($" idObject: {idObject} - INVALID VALUE!!- ");
break;
}
break;
case User32.WinEventId.EVENT_CONSOLE_UPDATE_REGION:
callbacks.UpdateRegion(idObject.LoWord(), idObject.HiWord(), idChild.LoWord(), idChild.HiWord());
break;
case User32.WinEventId.EVENT_CONSOLE_UPDATE_SIMPLE:
callbacks.UpdateSimple(idObject.LoWord(), idObject.HiWord(), idChild.LoWord(), idChild.HiWord());
break;
case User32.WinEventId.EVENT_CONSOLE_UPDATE_SCROLL:
callbacks.UpdateScroll(idObject, idChild);
break;
case User32.WinEventId.EVENT_CONSOLE_LAYOUT:
callbacks.Layout();
break;
case User32.WinEventId.EVENT_CONSOLE_START_APPLICATION:
callbacks.StartApplication(idObject, idChild);
break;
case User32.WinEventId.EVENT_CONSOLE_END_APPLICATION:
callbacks.EndApplication(idObject, idChild);
break;
}
}
}
private void MessagePump(IWinEventCallbacks callbacks, uint pid)
{
Log.Comment("Accessibility message pump thread started");
WinEventCallback callback = new WinEventCallback(callbacks, pid);
IntPtr hWinEventHook = User32.SetWinEventHook(
User32.WinEventId.EVENT_CONSOLE_CARET,
User32.WinEventId.EVENT_CONSOLE_END_APPLICATION,
IntPtr.Zero, // Use our own module
new User32.WinEventDelegate(callback.WinEventProc), // Our callback function
0, // All processes
0, // All threads
User32.WinEventFlags.WINEVENT_SKIPOWNPROCESS | User32.WinEventFlags.WINEVENT_SKIPOWNTHREAD);
NativeMethods.Win32NullHelper(hWinEventHook, "Registering accessibility event hook.");
Log.Comment("Entering accessibility pump loop.");
while (!messagePumpDone)
{
User32.MSG msg;
if (User32.PeekMessage(out msg, IntPtr.Zero, 0, 0, User32.PM.PM_REMOVE))
{
User32.DispatchMessage(ref msg);
}
Thread.Sleep(200);
}
Log.Comment("Exiting accessibility pump loop.");
NativeMethods.Win32BoolHelper(User32.UnhookWinEvent(hWinEventHook), "Unregistering accessibility event hook.");
Log.Comment("Accessibility message pump thread ended");
}
}
}