Overlay
OverlayBase`1.cs
using RainbowMage.HtmlRenderer;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Drawing;
using System.IO;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Text;
using System.Timers;
using System.Windows.Forms;
using System.Windows.Input;
using UpdateHelper;
namespace RainbowMage.OverlayPlugin
{
public abstract clast OverlayBase : IOverlay, IDisposable where TConfig : OverlayConfigBase
{
protected System.Timers.Timer timer;
protected System.Timers.Timer xivWindowTimer;
public event EventHandler OnLog;
public string Name { get; private set; }
public OverlayForm Overlay { get; private set; }
public TConfig Config { get; private set; }
public IPluginConfig PluginConfig { get; set; }
protected OverlayBase(TConfig config, string name)
{
this.Config = config;
this.Name = name;
this.InitializeOverlay();
this.InitializeTimer();
this.InitializeConfigHandlers();
}
public void Start()
{
this.timer.Start();
this.xivWindowTimer.Start();
}
public void Stop()
{
this.timer.Stop();
this.xivWindowTimer.Stop();
}
protected virtual void InitializeOverlay()
{
try
{
this.Overlay = new OverlayForm(astembly.GetExecutingastembly().GetName().Version.ToString(), this.Name, "about:blank", this.Config.MaxFrameRate);
if (!Util.IsOnScreen((Form) this.Overlay))
this.Overlay.StartPosition = FormStartPosition.WindowsDefaultLocation;
else
this.Overlay.Location = this.Config.Position;
this.Overlay.Text = this.Name;
this.Overlay.Size = this.Config.Size;
this.Overlay.IsClickThru = this.Config.IsClickThru;
this.Overlay.Renderer.BrowserError += (EventHandler) ((o, e) => this.Log(LogLevel.Error, "浏览器错误: {0}, {1}, {2}", (object) e.ErrorCode, (object) e.ErrorText, (object) e.Url));
this.Overlay.Renderer.BrowserLoad += (EventHandler) ((o, e) =>
{
this.Log(LogLevel.Debug, "浏览器读取: {0}: {1}", (object) e.HttpStatusCode, (object) e.Url);
this.NotifyOverlayState();
});
this.Overlay.Renderer.BrowserConsoleLog += (EventHandler) ((o, e) => this.Log(LogLevel.Info, "浏览器控制: {0} (目标: {1}, 行: {2})", (object) e.Message, (object) e.Source, (object) e.Line));
this.Config.UrlChanged += (EventHandler) ((o, e) => this.Navigate(e.NewUrl));
if (this.CheckUrl(this.Config.Url))
this.Navigate(this.Config.Url);
else
this.Navigate("about:blank");
this.Overlay.Show();
this.Overlay.Visible = this.Config.IsVisible;
this.Overlay.Locked = this.Config.IsLocked;
}
catch (Exception ex)
{
this.Log(LogLevel.Error, "初始化美化窗口: {0}", (object) this.Name, (object) ex);
}
}
private ModifierKeys GetModifierKey(Keys modifier)
{
ModifierKeys modifierKeys = (ModifierKeys) 0;
if ((modifier & Keys.Shift) == Keys.Shift)
modifierKeys |= ModifierKeys.Shift;
if ((modifier & Keys.Control) == Keys.Control)
modifierKeys |= ModifierKeys.Control;
if ((modifier & Keys.Alt) == Keys.Alt)
modifierKeys |= ModifierKeys.Alt;
if ((modifier & Keys.LWin) == Keys.LWin || (modifier & Keys.RWin) == Keys.RWin)
modifierKeys |= ModifierKeys.Windows;
return modifierKeys;
}
private bool CheckUrl(string url)
{
try
{
Uri uri = new Uri(url);
if (uri.Scheme == "file")
{
if (!File.Exists(uri.LocalPath))
{
this.Log(LogLevel.Warning, "初始化美化窗口: 本地文件 {0} 不存在!", (object) uri.LocalPath);
return false;
}
}
}
catch (Exception ex)
{
this.Log(LogLevel.Error, "初始化美化窗口: URI解析错误! 请核对URL. (Config.Url = {0}): {1}", (object) this.Config.Url, (object) ex);
return false;
}
return true;
}
protected virtual void InitializeTimer()
{
this.timer = new System.Timers.Timer();
this.timer.Interval = 1000.0;
this.timer.Elapsed += (ElapsedEventHandler) ((o, e) =>
{
try
{
this.Update();
}
catch (Exception ex)
{
this.Log(LogLevel.Error, "更新: {0}", (object) ex.ToString());
}
});
this.xivWindowTimer = new System.Timers.Timer();
this.xivWindowTimer.Interval = 1000.0;
this.xivWindowTimer.Elapsed += (ElapsedEventHandler) ((o, e) =>
{
try
{
if (!this.Config.IsVisible || !this.PluginConfig.HideOverlaysWhenNotActive)
return;
IntPtr foregroundWindow = NativeMethods.GetForegroundWindow();
if (foregroundWindow == IntPtr.Zero)
return;
uint lpdwProcessId;
int windowThreadProcessId = (int) NativeMethods.GetWindowThreadProcessId(foregroundWindow, out lpdwProcessId);
string fileName = Process.GetProcessById((int) lpdwProcessId).MainModule.FileName;
if (Path.GetFileName(fileName.ToString()) == "ffxiv.exe" || Path.GetFileName(fileName.ToString()) == "ffxiv_dx11.exe" || fileName.ToString() == Process.GetCurrentProcess().MainModule.FileName)
this.Overlay.Visible = true;
else
this.Overlay.Visible = false;
}
catch (Exception ex)
{
this.Log(LogLevel.Error, "最终幻想14窗口监视: {0}", (object) ex.ToString());
}
});
}
protected virtual void InitializeConfigHandlers()
{
this.Config.VisibleChanged += (EventHandler) ((o, e) => this.Overlay.Visible = e.IsVisible);
this.Config.ClickThruChanged += (EventHandler) ((o, e) => this.Overlay.IsClickThru = e.IsClickThru);
this.Config.LockChanged += (EventHandler) ((o, e) =>
{
this.Overlay.Locked = e.IsLocked;
this.NotifyOverlayState();
});
}
protected abstract void Update();
public virtual void Dispose()
{
try
{
if (this.timer != null)
this.timer.Stop();
if (this.xivWindowTimer != null)
this.xivWindowTimer.Stop();
if (this.Overlay != null)
{
this.Overlay.Close();
this.Overlay.Dispose();
}
}
catch (Exception ex)
{
this.Log(LogLevel.Error, "清除: {0}", (object) ex);
}
}
public virtual void Navigate(string url)
{
this.Overlay.Url = url;
}
protected void Log(LogLevel level, string message)
{
if (this.OnLog == null)
return;
this.OnLog((object) this, new LogEventArgs(level, string.Format("{0}: {1}", (object) this.Name, (object) message)));
}
protected void Log(LogLevel level, string format, params object[] args)
{
this.Log(level, string.Format(format, args));
}
public void SavePositionAndSize()
{
this.Config.Position = this.Overlay.Location;
this.Config.Size = this.Overlay.Size;
}
private void NotifyOverlayState()
{
string script = string.Format("docameent.dispatchEvent(new CustomEvent('onOverlayStateUpdate', {{ detail: {{ isLocked: {0} }} }}));", this.Config.IsLocked ? (object) "true" : (object) "false");
if (this.Overlay == null || this.Overlay.Renderer == null || this.Overlay.Renderer.Browser == null)
return;
this.Overlay.Renderer.ExecuteScript(script);
}
public void SendMessage(string message)
{
string script = string.Format("docameent.dispatchEvent(new CustomEvent('onBroadcastMessageReceive', {{ detail: {{ message: \"{0}\" }} }}));", (object) Util.CreateJsonSafeString(message));
if (this.Overlay == null || this.Overlay.Renderer == null || this.Overlay.Renderer.Browser == null)
return;
this.Overlay.Renderer.ExecuteScript(script);
}
public virtual void OverlayMessage(string message)
{
}
internal static clast Util
{
public static string CreateJsonSafeString(string str)
{
return str.Replace("\"", "\\\"").Replace("'", "\\'").Replace("\r", "\\r").Replace("\n", "\\n").Replace("\t", "\\t");
}
public static string ReplaceNaNString(string str, string replace)
{
return str.Replace(double.NaN.ToString(), replace);
}
public static bool IsOnScreen(Form form)
{
foreach (Screen allScreen in Screen.AllScreens)
{
if (allScreen.WorkingArea.IntersectsWith(new Rectangle(form.Left, form.Top, form.Width, form.Height)))
return true;
}
return false;
}
public static void HidePreview(Form form)
{
uint num = NativeMethods.GetWindowLong(form.Handle, -20) | 128;
NativeMethods.SetWindowLongA(form.Handle, -20, (IntPtr)num);
}
public static string GetHotkeyString(Keys modifier, Keys key, string defaultText = "")
{
StringBuilder stringBuilder = new StringBuilder();
if ((modifier & Keys.Shift) == Keys.Shift)
stringBuilder.Append("Shift + ");
if ((modifier & Keys.Control) == Keys.Control)
stringBuilder.Append("Ctrl + ");
if ((modifier & Keys.Alt) == Keys.Alt)
stringBuilder.Append("Alt + ");
if ((modifier & Keys.LWin) == Keys.LWin || (modifier & Keys.RWin) == Keys.RWin)
stringBuilder.Append("Win + ");
stringBuilder.Append(Enum.ToObject(typeof(Keys), (object)key).ToString());
return stringBuilder.ToString();
}
public static Keys RemoveModifiers(Keys keyCode, Keys modifiers)
{
Keys keys1 = keyCode;
foreach (Keys keys2 in new List()
{
Keys.ControlKey,
Keys.LControlKey,
Keys.Alt,
Keys.ShiftKey,
Keys.Shift,
Keys.LShiftKey,
Keys.RShiftKey,
Keys.Control,
Keys.LWin,
Keys.RWin
})
{
if (keys1.HasFlag((Enum)keys2) && keys1 == keys2)
keys1 &= ~keys2;
}
return keys1;
}
}
}
public static clast NativeMethods
{
public static readonly IntPtr HWND_TOPMOST = new IntPtr(-1);
public static readonly IntPtr HWND_NOTOPMOST = new IntPtr(-2);
public static readonly IntPtr HWND_TOP = new IntPtr(0);
public static readonly IntPtr HWND_BOTTOM = new IntPtr(1);
public const int GWL_EXSTYLE = -20;
public const int WS_EX_LAYERED = 524288;
public const int WS_EX_TOOLWINDOW = 128;
public const int WS_EX_TRANSPARENT = 32;
public const int LWA_ALPHA = 2;
public const int LWA_COLORKEY = 1;
public const int SW_HIDE = 0;
public const int SW_SHOW = 5;
public const uint SWP_NOSIZE = 1;
public const uint SWP_NOMOVE = 2;
public const uint TOPMOST_FLAGS = 3;
public const int ERROR_INSUFFICIENT_BUFFER = 122;
public const byte AC_SRC_ALPHA = 1;
public const byte AC_SRC_OVER = 0;
public const int ULW_ALPHA = 2;
public const int DIB_RGB_COLORS = 0;
public const uint GW_HWNDPREV = 3;
public const uint SWP_NOACTIVATE = 16;
public const int WM_KEYDOWN = 256;
public const int WM_KEYUP = 257;
public const int WM_CHAR = 258;
public const int WM_SYSKEYDOWN = 260;
public const int WM_SYSKEYUP = 261;
public const int WM_SYSCHAR = 262;
[DllImport("user32")]
public static extern bool UpdateLayeredWindow(
IntPtr hWnd,
IntPtr hdcDst,
[In] ref NativeMethods.Point pptDst,
[In] ref NativeMethods.Size pSize,
IntPtr hdcSrc,
[In] ref NativeMethods.Point pptSrc,
int crKey,
[In] ref NativeMethods.BlendFunction pBlend,
uint dwFlags);
[DllImport("gdi32")]
public static extern IntPtr SelectObject(IntPtr hdc, IntPtr hgdiobj);
[DllImport("gdi32")]
public static extern bool DeleteObject(IntPtr hObject);
[DllImport("gdi32")]
public static extern bool DeleteDC(IntPtr hdc);
[DllImport("gdi32")]
public static extern IntPtr CreateCompatibleDC(IntPtr hdc);
[DllImport("gdi32")]
public static extern IntPtr CreateCompatibleBitmap(IntPtr hdc, int nWidth, int nHeight);
[DllImport("gdi32")]
public static extern IntPtr CreateDIBSection(
IntPtr hdc,
[In] ref NativeMethods.BitmapInfo pbmi,
uint iUsage,
out IntPtr ppvBits,
IntPtr hSection,
uint dwOffset);
[DllImport("user32.dll", SetLastError = true)]
public static extern int SetWindowLong(IntPtr hWnd, int nIndex, int dwNewLong);
[DllImport("kernel32")]
public static extern void CopyMemory(IntPtr dest, IntPtr src, uint count);
[DllImport("user32.dll", SetLastError = true)]
public static extern IntPtr SetWindowLongPtr(IntPtr hWnd, int nIndex, IntPtr dwNewLong);
[DllImport("kernel32.dll")]
public static extern void SetLastError(int dwErrorCode);
private static int ToIntPtr32(IntPtr intPtr)
{
return (int)intPtr.ToInt64();
}
public static IntPtr SetWindowLongA(IntPtr hWnd, int nIndex, IntPtr dwNewLong)
{
IntPtr num1 = IntPtr.Zero;
NativeMethods.SetLastError(0);
int lastWin32Error;
if (IntPtr.Size == 4)
{
int num2 = NativeMethods.SetWindowLong(hWnd, nIndex, NativeMethods.ToIntPtr32(dwNewLong));
lastWin32Error = Marshal.GetLastWin32Error();
num1 = new IntPtr(num2);
}
else
{
num1 = NativeMethods.SetWindowLongPtr(hWnd, nIndex, dwNewLong);
lastWin32Error = Marshal.GetLastWin32Error();
}
if (num1 == IntPtr.Zero && lastWin32Error != 0)
throw new Win32Exception(lastWin32Error);
return num1;
}
[DllImport("user32.dll")]
public static extern IntPtr GetWindow(IntPtr hWnd, uint uCmd);
[DllImport("user32.dll", SetLastError = true)]
public static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint lpdwProcessId);
[DllImport("kernel32.dll", SetLastError = true)]
public static extern int GetModuleFileName(IntPtr hModule, StringBuilder lpFilename, int nSize);
[DllImport("user32.dll")]
public static extern short GetKeyState(int nVirtKey);
public struct BitmapInfo
{
public NativeMethods.BitmapInfoHeader bmiHeader;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 1, ArraySubType = UnmanagedType.Struct)]
public NativeMethods.RgbQuad[] bmiColors;
}
public struct BitmapInfoHeader
{
public uint biSize;
public int biWidth;
public int biHeight;
public ushort biPlanes;
public ushort biBitCount;
public NativeMethods.BitmapCompressionMode biCompression;
public uint biSizeImage;
public int biXPelsPerMeter;
public int biYPelsPerMeter;
public uint biClrUsed;
public uint biClrImportant;
public void Init()
{
this.biSize = (uint)Marshal.SizeOf((object)this);
}
}
public struct BlendFunction
{
public byte BlendOp;
public byte BlendFlags;
public byte SourceConstantAlpha;
public byte AlphaFormat;
}
public struct Point
{
public int X;
public int Y;
}
public struct Size
{
public int Width;
public int Height;
}
public struct RgbQuad
{
public byte rgbBlue;
public byte rgbGreen;
public byte rgbRed;
public byte rgbReserved;
}
public enum BitmapCompressionMode : uint
{
BI_RGB,
BI_RLE8,
BI_RLE4,
BI_BITFIELDS,
BI_JPEG,
BI_PNG,
}
[DllImport("winmm.dll", EntryPoint = "timeBeginPeriod", SetLastError = true)]
internal static extern uint TimeBeginPeriod(uint uMilliseconds);
[DllImport("winmm.dll", EntryPoint = "timeEndPeriod", SetLastError = true)]
internal static extern uint TimeEndPeriod(uint uMilliseconds);
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
internal static extern bool SetWindowPos(
IntPtr hWnd,
IntPtr hWndInsertAfter,
int X,
int Y,
int cx,
int cy,
uint uFlags);
[DllImport("user32.dll")]
internal static extern IntPtr GetForegroundWindow();
[DllImport("user32.dll", CharSet = CharSet.Unicode)]
internal static extern int GetWindowText(IntPtr hWnd, StringBuilder text, int count);
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
internal static extern int SetWindowLong(IntPtr Handle, int nIndex, uint dwNewLong);
[DllImport("user32.dll", SetLastError = true)]
internal static extern uint GetWindowLong(IntPtr hWnd, int nIndex);
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
internal static extern bool ShowWindow(IntPtr Handle, uint nCmdShow);
[DllImport("kernel32.dll")]
internal static extern bool ReadProcessMemory(
IntPtr hProcess,
IntPtr lpBaseAddress,
byte[] lpBuffer,
IntPtr nSize,
ref IntPtr lpNumberOfBytesRead);
}
}