Move Process list manager into a separate class
This commit is contained in:
@@ -110,6 +110,10 @@
|
||||
<Compile Include="Configuration\IAppConfig.cs" />
|
||||
<Compile Include="Configuration\IThumbnailConfiguration.cs" />
|
||||
<Compile Include="Configuration\ZoomAnchor.cs" />
|
||||
<Compile Include="Services\Implementation\ProcessInfo.cs" />
|
||||
<Compile Include="Services\Implementation\ProcessMonitor.cs" />
|
||||
<Compile Include="Services\Interface\IProcessInfo.cs" />
|
||||
<Compile Include="Services\Interface\IProcessMonitor.cs" />
|
||||
<Compile Include="WindowManager\Implementation\DwmThumbnail.cs" />
|
||||
<Compile Include="WindowManager\Interface\IDwmThumbnail.cs" />
|
||||
<Compile Include="WindowManager\Interface\InteropConstants.cs" />
|
||||
|
@@ -1,24 +1,25 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Drawing;
|
||||
using System.Windows.Threading;
|
||||
using EveOPreview.Configuration;
|
||||
using EveOPreview.Mediator;
|
||||
using EveOPreview.Services;
|
||||
using EveOPreview.WindowManager;
|
||||
|
||||
namespace EveOPreview.UI
|
||||
{
|
||||
public class ThumbnailManager : IThumbnailManager
|
||||
class ThumbnailManager : IThumbnailManager
|
||||
{
|
||||
#region Private constants
|
||||
private const int WindowPositionThreshold = -5000;
|
||||
private const int WindowSizeThreshold = -5000;
|
||||
|
||||
private const string ClientProcessName = "ExeFile";
|
||||
private const string DefaultClientTitle = "EVE";
|
||||
#endregion
|
||||
|
||||
#region Private fields
|
||||
private readonly IProcessMonitor _processMonitor;
|
||||
private readonly IWindowManager _windowManager;
|
||||
private readonly IThumbnailConfiguration _configuration;
|
||||
private readonly DispatcherTimer _thumbnailUpdateTimer;
|
||||
@@ -32,8 +33,9 @@ namespace EveOPreview.UI
|
||||
private bool _isHoverEffectActive;
|
||||
#endregion
|
||||
|
||||
public ThumbnailManager(IWindowManager windowManager, IThumbnailConfiguration configuration, IThumbnailViewFactory factory)
|
||||
public ThumbnailManager(IMediator mediator, IThumbnailConfiguration configuration, IProcessMonitor processMonitor, IWindowManager windowManager, IThumbnailViewFactory factory)
|
||||
{
|
||||
this._processMonitor = processMonitor;
|
||||
this._windowManager = windowManager;
|
||||
this._configuration = configuration;
|
||||
this._thumbnailViewFactory = factory;
|
||||
@@ -189,15 +191,9 @@ namespace EveOPreview.UI
|
||||
this._ignoreViewEvents = true;
|
||||
}
|
||||
|
||||
private static Process[] GetClientProcesses()
|
||||
{
|
||||
return Process.GetProcessesByName(ThumbnailManager.ClientProcessName);
|
||||
}
|
||||
|
||||
private void UpdateThumbnailsList()
|
||||
{
|
||||
Process[] clientProcesses = ThumbnailManager.GetClientProcesses();
|
||||
List<IntPtr> processHandles = new List<IntPtr>(clientProcesses.Length);
|
||||
this._processMonitor.GetUpdatedProcesses(out ICollection<IProcessInfo> addedProcesses, out ICollection<IProcessInfo> updatedProcesses, out ICollection<IProcessInfo> removedProcesses);
|
||||
|
||||
IntPtr foregroundWindowHandle = this._windowManager.GetForegroundWindowHandle();
|
||||
|
||||
@@ -205,78 +201,74 @@ namespace EveOPreview.UI
|
||||
List<IThumbnailView> viewsUpdated = new List<IThumbnailView>();
|
||||
List<IThumbnailView> viewsRemoved = new List<IThumbnailView>();
|
||||
|
||||
foreach (Process process in clientProcesses)
|
||||
foreach (IProcessInfo process in addedProcesses)
|
||||
{
|
||||
IntPtr processHandle = process.MainWindowHandle;
|
||||
string processTitle = process.MainWindowTitle;
|
||||
processHandles.Add(processHandle);
|
||||
IThumbnailView view = this._thumbnailViewFactory.Create(process.Handle, process.Title, this._configuration.ThumbnailSize);
|
||||
view.IsEnabled = true;
|
||||
view.IsOverlayEnabled = this._configuration.ShowThumbnailOverlays;
|
||||
view.SetFrames(this._configuration.ShowThumbnailFrames);
|
||||
// Max/Min size limitations should be set AFTER the frames are disabled
|
||||
// Otherwise thumbnail window will be unnecessary resized
|
||||
view.SetSizeLimitations(this._configuration.ThumbnailMinimumSize, this._configuration.ThumbnailMaximumSize);
|
||||
view.SetTopMost(this._configuration.ShowThumbnailsAlwaysOnTop);
|
||||
|
||||
IThumbnailView view;
|
||||
this._thumbnailViews.TryGetValue(processHandle, out view);
|
||||
view.ThumbnailLocation = this.IsManageableThumbnail(view)
|
||||
? this._configuration.GetThumbnailLocation(process.Title, this._activeClientTitle, view.ThumbnailLocation)
|
||||
: this._configuration.GetDefaultThumbnailLocation();
|
||||
|
||||
if ((view == null) && (processTitle != ""))
|
||||
this._thumbnailViews.Add(process.Handle, view);
|
||||
|
||||
view.ThumbnailResized = this.ThumbnailViewResized;
|
||||
view.ThumbnailMoved = this.ThumbnailViewMoved;
|
||||
view.ThumbnailFocused = this.ThumbnailViewFocused;
|
||||
view.ThumbnailLostFocus = this.ThumbnailViewLostFocus;
|
||||
view.ThumbnailActivated = this.ThumbnailActivated;
|
||||
view.ThumbnailDeactivated = this.ThumbnailDeactivated;
|
||||
|
||||
view.RegisterHotkey(this._configuration.GetClientHotkey(process.Title));
|
||||
|
||||
this.ApplyClientLayout(process.Handle, process.Title);
|
||||
|
||||
viewsAdded.Add(view);
|
||||
|
||||
if (process.Handle == foregroundWindowHandle)
|
||||
{
|
||||
view = this._thumbnailViewFactory.Create(processHandle, processTitle, this._configuration.ThumbnailSize);
|
||||
view.IsEnabled = true;
|
||||
view.IsOverlayEnabled = this._configuration.ShowThumbnailOverlays;
|
||||
view.SetFrames(this._configuration.ShowThumbnailFrames);
|
||||
// Max/Min size limitations should be set AFTER the frames are disabled
|
||||
// Otherwise thumbnail window will be unnecessary resized
|
||||
view.SetSizeLimitations(this._configuration.ThumbnailMinimumSize, this._configuration.ThumbnailMaximumSize);
|
||||
view.SetTopMost(this._configuration.ShowThumbnailsAlwaysOnTop);
|
||||
|
||||
view.ThumbnailLocation = this.IsManageableThumbnail(view)
|
||||
? this._configuration.GetThumbnailLocation(processTitle, this._activeClientTitle, view.ThumbnailLocation)
|
||||
: this._configuration.GetDefaultThumbnailLocation();
|
||||
|
||||
this._thumbnailViews.Add(processHandle, view);
|
||||
|
||||
view.ThumbnailResized = this.ThumbnailViewResized;
|
||||
view.ThumbnailMoved = this.ThumbnailViewMoved;
|
||||
view.ThumbnailFocused = this.ThumbnailViewFocused;
|
||||
view.ThumbnailLostFocus = this.ThumbnailViewLostFocus;
|
||||
view.ThumbnailActivated = this.ThumbnailActivated;
|
||||
view.ThumbnailDeactivated = this.ThumbnailDeactivated;
|
||||
|
||||
view.RegisterHotkey(this._configuration.GetClientHotkey(processTitle));
|
||||
|
||||
this.ApplyClientLayout(processHandle, processTitle);
|
||||
|
||||
viewsAdded.Add(view);
|
||||
this._activeClientHandle = process.Handle;
|
||||
this._activeClientTitle = process.Title;
|
||||
}
|
||||
else if ((view != null) && (processTitle != view.Title)) // update thumbnail title
|
||||
}
|
||||
|
||||
foreach (IProcessInfo process in updatedProcesses)
|
||||
{
|
||||
this._thumbnailViews.TryGetValue(process.Handle, out IThumbnailView view);
|
||||
|
||||
if (view == null)
|
||||
{
|
||||
view.Title = processTitle;
|
||||
view.RegisterHotkey(this._configuration.GetClientHotkey(processTitle));
|
||||
// Something went terribly wrong
|
||||
continue;
|
||||
}
|
||||
|
||||
this.ApplyClientLayout(processHandle, processTitle);
|
||||
if (process.Title != view.Title) // update thumbnail title
|
||||
{
|
||||
view.Title = process.Title;
|
||||
view.RegisterHotkey(this._configuration.GetClientHotkey(process.Title));
|
||||
|
||||
this.ApplyClientLayout(process.Handle, process.Title);
|
||||
viewsUpdated.Add(view);
|
||||
}
|
||||
|
||||
if (process.MainWindowHandle == foregroundWindowHandle)
|
||||
if (process.Handle == foregroundWindowHandle)
|
||||
{
|
||||
this._activeClientHandle = process.MainWindowHandle;
|
||||
this._activeClientTitle = process.MainWindowTitle;
|
||||
this._activeClientHandle = process.Handle;
|
||||
this._activeClientTitle = process.Title;
|
||||
}
|
||||
}
|
||||
|
||||
// Cleanup
|
||||
IList<IntPtr> obsoleteThumbnails = new List<IntPtr>();
|
||||
|
||||
foreach (IntPtr processHandle in this._thumbnailViews.Keys)
|
||||
foreach (IProcessInfo process in removedProcesses)
|
||||
{
|
||||
if (!processHandles.Contains(processHandle))
|
||||
{
|
||||
obsoleteThumbnails.Add(processHandle);
|
||||
}
|
||||
}
|
||||
IThumbnailView view = this._thumbnailViews[process.Handle];
|
||||
|
||||
foreach (IntPtr processHandle in obsoleteThumbnails)
|
||||
{
|
||||
IThumbnailView view = this._thumbnailViews[processHandle];
|
||||
|
||||
this._thumbnailViews.Remove(processHandle);
|
||||
this._thumbnailViews.Remove(process.Handle);
|
||||
|
||||
view.UnregisterHotkey();
|
||||
|
||||
@@ -453,11 +445,11 @@ namespace EveOPreview.UI
|
||||
|
||||
private void UpdateClientLayouts()
|
||||
{
|
||||
Process[] clientProcesses = ThumbnailManager.GetClientProcesses();
|
||||
ICollection<IProcessInfo> processes = this._processMonitor.GetAllProcesses();
|
||||
|
||||
foreach (Process process in clientProcesses)
|
||||
foreach (IProcessInfo process in processes)
|
||||
{
|
||||
this._windowManager.GetWindowCoordinates(process.MainWindowHandle, out int left, out int top, out int right, out int bottom);
|
||||
this._windowManager.GetWindowCoordinates(process.Handle, out int left, out int top, out int right, out int bottom);
|
||||
|
||||
int width = Math.Abs(right - left);
|
||||
int height = Math.Abs(bottom - top);
|
||||
@@ -467,7 +459,7 @@ namespace EveOPreview.UI
|
||||
continue;
|
||||
}
|
||||
|
||||
this._configuration.SetClientLayout(process.MainWindowTitle, new ClientLayout(left, top, width, height));
|
||||
this._configuration.SetClientLayout(process.Title, new ClientLayout(left, top, width, height));
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -3,6 +3,7 @@ using System.Threading;
|
||||
using System.Windows.Forms;
|
||||
using EveOPreview.Configuration;
|
||||
using EveOPreview.Mediator;
|
||||
using EveOPreview.Services;
|
||||
using EveOPreview.UI;
|
||||
using EveOPreview.WindowManager;
|
||||
|
||||
@@ -41,13 +42,13 @@ namespace EveOPreview
|
||||
{
|
||||
// The code might look overcomplicated here for a single Mutex operation
|
||||
// Yet we had already experienced a Windows-level issue
|
||||
// where .NET finalizer theread was literally paralyzed by
|
||||
// where .NET finalizer thread was literally paralyzed by
|
||||
// a failed Mutex operation. That did lead to weird OutOfMemory
|
||||
// exceptions later
|
||||
try
|
||||
{
|
||||
Mutex mutex = Mutex.OpenExisting(Program.MutexName);
|
||||
// if that didn't fail then anotherinstance is already running
|
||||
Mutex.OpenExisting(Program.MutexName);
|
||||
// if that didn't fail then another instance is already running
|
||||
return null;
|
||||
}
|
||||
catch (UnauthorizedAccessException)
|
||||
@@ -75,6 +76,7 @@ namespace EveOPreview
|
||||
// Low-level services
|
||||
container.Register<IMediator>();
|
||||
container.Register<IWindowManager>();
|
||||
container.Register<IProcessMonitor>();
|
||||
|
||||
// Configuration services
|
||||
container.Register<IConfigurationStorage>();
|
||||
|
16
Eve-O-Preview/Services/Implementation/ProcessInfo.cs
Normal file
16
Eve-O-Preview/Services/Implementation/ProcessInfo.cs
Normal file
@@ -0,0 +1,16 @@
|
||||
using System;
|
||||
|
||||
namespace EveOPreview.Services.Implementation
|
||||
{
|
||||
sealed class ProcessInfo : IProcessInfo
|
||||
{
|
||||
public ProcessInfo(IntPtr handle, string title)
|
||||
{
|
||||
this.Handle = handle;
|
||||
this.Title = title;
|
||||
}
|
||||
|
||||
public IntPtr Handle { get; }
|
||||
public string Title { get; }
|
||||
}
|
||||
}
|
93
Eve-O-Preview/Services/Implementation/ProcessMonitor.cs
Normal file
93
Eve-O-Preview/Services/Implementation/ProcessMonitor.cs
Normal file
@@ -0,0 +1,93 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace EveOPreview.Services.Implementation
|
||||
{
|
||||
sealed class ProcessMonitor : IProcessMonitor
|
||||
{
|
||||
#region Private constants
|
||||
private const string DefaultProcessName = "ExeFile";
|
||||
#endregion
|
||||
|
||||
#region Private fields
|
||||
private readonly IDictionary<IntPtr, string> _processCache;
|
||||
#endregion
|
||||
|
||||
public ProcessMonitor()
|
||||
{
|
||||
this._processCache = new Dictionary<IntPtr, string>(512);
|
||||
}
|
||||
|
||||
private bool IsMonitoredProcess(string processName)
|
||||
{
|
||||
// This is a possible extension point
|
||||
return String.Equals(processName, ProcessMonitor.DefaultProcessName, StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
public void GetUpdatedProcesses(out ICollection<IProcessInfo> addedProcesses, out ICollection<IProcessInfo> updatedProcesses, out ICollection<IProcessInfo> removedProcesses)
|
||||
{
|
||||
addedProcesses = new List<IProcessInfo>(16);
|
||||
updatedProcesses = new List<IProcessInfo>(16);
|
||||
removedProcesses = new List<IProcessInfo>(16);
|
||||
|
||||
IList<IntPtr> knownProcesses = new List<IntPtr>(this._processCache.Keys);
|
||||
foreach (Process process in Process.GetProcesses())
|
||||
{
|
||||
string processName = process.ProcessName;
|
||||
|
||||
if (!this.IsMonitoredProcess(processName))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
IntPtr mainWindowHandle = process.MainWindowHandle;
|
||||
if (mainWindowHandle == IntPtr.Zero)
|
||||
{
|
||||
continue; // No need to monitor non-visual processes
|
||||
}
|
||||
|
||||
string mainWindowTitle = process.MainWindowTitle;
|
||||
this._processCache.TryGetValue(mainWindowHandle, out string cachedTitle);
|
||||
|
||||
if (cachedTitle == null)
|
||||
{
|
||||
// This is a new process in the list
|
||||
this._processCache.Add(mainWindowHandle, mainWindowTitle);
|
||||
addedProcesses.Add(new ProcessInfo(mainWindowHandle, mainWindowTitle));
|
||||
}
|
||||
else
|
||||
{
|
||||
// This is an already known process
|
||||
if (cachedTitle != mainWindowTitle)
|
||||
{
|
||||
this._processCache[mainWindowHandle] = mainWindowTitle;
|
||||
updatedProcesses.Add((IProcessInfo)new ProcessInfo(mainWindowHandle, mainWindowTitle));
|
||||
}
|
||||
|
||||
knownProcesses.Remove(mainWindowHandle);
|
||||
}
|
||||
}
|
||||
|
||||
foreach (IntPtr index in knownProcesses)
|
||||
{
|
||||
string title = this._processCache[index];
|
||||
removedProcesses.Add(new ProcessInfo(index, title));
|
||||
this._processCache.Remove(index);
|
||||
}
|
||||
}
|
||||
|
||||
public ICollection<IProcessInfo> GetAllProcesses()
|
||||
{
|
||||
ICollection<IProcessInfo> result = new List<IProcessInfo>(this._processCache.Count);
|
||||
|
||||
// TODO Lock list here just in case
|
||||
foreach (KeyValuePair<IntPtr, string> entry in this._processCache)
|
||||
{
|
||||
result.Add(new ProcessInfo(entry.Key, entry.Value));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
10
Eve-O-Preview/Services/Interface/IProcessInfo.cs
Normal file
10
Eve-O-Preview/Services/Interface/IProcessInfo.cs
Normal file
@@ -0,0 +1,10 @@
|
||||
using System;
|
||||
|
||||
namespace EveOPreview.Services
|
||||
{
|
||||
public interface IProcessInfo
|
||||
{
|
||||
IntPtr Handle { get; }
|
||||
string Title { get; }
|
||||
}
|
||||
}
|
10
Eve-O-Preview/Services/Interface/IProcessMonitor.cs
Normal file
10
Eve-O-Preview/Services/Interface/IProcessMonitor.cs
Normal file
@@ -0,0 +1,10 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace EveOPreview.Services
|
||||
{
|
||||
public interface IProcessMonitor
|
||||
{
|
||||
ICollection<IProcessInfo> GetAllProcesses();
|
||||
void GetUpdatedProcesses(out ICollection<IProcessInfo> addedProcesses, out ICollection<IProcessInfo> updatedProcesses, out ICollection<IProcessInfo> removedProcesses);
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user