using System;
using System.Diagnostics;
using System.IO;
using System.IO.Pipes;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.ConstrainedExecution;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using AutoHotkey.Interop.Pipes;
using AutoHotkey.Interop.Util;
using Microsoft.Win32.SafeHandles;
[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)]
[assembly: AssemblyTitle("AutoHotkey.Interop")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("AutoHotkey.Interop")]
[assembly: AssemblyCopyright("Copyright © 2014")]
[assembly: AssemblyTrademark("")]
[assembly: ComVisible(false)]
[assembly: Guid("8a7cbbfe-ccb2-42ef-b90f-d937ae6ed741")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: InternalsVisibleTo("AutoHotkey.Interop.Tests")]
[assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")]
[assembly: AssemblyVersion("1.0.0.0")]
namespace AutoHotkey.Interop
{
internal class AhkEscape
{
public static string Quote(string msg)
{
if (msg == null)
{
throw new ArgumentNullException("msg");
}
if (msg.StartsWith("\"") && msg.EndsWith("\""))
{
msg = msg.Remove(0, 1);
msg = msg.Remove(msg.Length - 1, 1);
msg = "\"" + Escape(msg) + "\"";
}
else
{
msg = "\"" + Escape(msg) + "\"";
}
return msg;
}
public static string Escape(string msg)
{
if (msg == null)
{
throw new ArgumentNullException("msg");
}
return msg.Replace("`", "``").Replace("\r", "`r").Replace("\n", "`n")
.Replace("\t", "`t")
.Replace("\"", "\"\"");
}
}
internal static class AutoHotkeyDll
{
[StructLayout(LayoutKind.Sequential, Size = 1)]
public struct Execute
{
public const byte Add = 0;
public const byte Run = 1;
public const byte RunWait = 2;
}
private const string DLLPATH = "AutoHotkey.dll";
[DllImport("AutoHotkey.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Unicode)]
public static extern uint ahkdll([MarshalAs(UnmanagedType.LPWStr)] string Path, [MarshalAs(UnmanagedType.LPWStr)] string Options, [MarshalAs(UnmanagedType.LPWStr)] string Parameters);
[DllImport("AutoHotkey.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Unicode)]
public static extern uint ahktextdll([MarshalAs(UnmanagedType.LPWStr)] string Code, [MarshalAs(UnmanagedType.LPWStr)] string Options, [MarshalAs(UnmanagedType.LPWStr)] string Parameters);
[DllImport("AutoHotkey.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Unicode)]
public static extern bool ahkReady();
[DllImport("AutoHotkey.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Unicode)]
public static extern void ahkTerminate(uint timeout);
[DllImport("AutoHotkey.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Unicode)]
public static extern void ahkReload();
[DllImport("AutoHotkey.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Unicode)]
public static extern void ahkPause([MarshalAs(UnmanagedType.LPWStr)] string strState);
[DllImport("AutoHotkey.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Unicode)]
public static extern uint addFile([MarshalAs(UnmanagedType.LPWStr)] string FilePath, byte AllowDuplicateInclude, byte IgnoreLoadFailure);
[DllImport("AutoHotkey.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Unicode)]
public static extern uint addScript([MarshalAs(UnmanagedType.LPWStr)] string code, byte execute);
[DllImport("AutoHotkey.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Unicode)]
public static extern bool ahkExec([MarshalAs(UnmanagedType.LPWStr)] string code);
[DllImport("AutoHotkey.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Unicode)]
public static extern bool ahkLabel([MarshalAs(UnmanagedType.LPWStr)] string labelName, bool noWait);
[DllImport("AutoHotkey.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Unicode)]
public static extern IntPtr ahkFunction([MarshalAs(UnmanagedType.LPWStr)] string functionName, [MarshalAs(UnmanagedType.LPWStr)] string parameter1, [MarshalAs(UnmanagedType.LPWStr)] string parameter2, [MarshalAs(UnmanagedType.LPWStr)] string parameter3, [MarshalAs(UnmanagedType.LPWStr)] string parameter4, [MarshalAs(UnmanagedType.LPWStr)] string parameter5, [MarshalAs(UnmanagedType.LPWStr)] string parameter6, [MarshalAs(UnmanagedType.LPWStr)] string parameter7, [MarshalAs(UnmanagedType.LPWStr)] string parameter8, [MarshalAs(UnmanagedType.LPWStr)] string parameter9, [MarshalAs(UnmanagedType.LPWStr)] string parameter10);
[DllImport("AutoHotkey.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Unicode)]
public static extern bool ahkPostFunction([MarshalAs(UnmanagedType.LPWStr)] string functionName, [MarshalAs(UnmanagedType.LPWStr)] string Parameters);
[DllImport("AutoHotkey.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Unicode)]
public static extern bool ahkassign([MarshalAs(UnmanagedType.LPWStr)] string VariableName, [MarshalAs(UnmanagedType.LPWStr)] string NewValue);
[DllImport("AutoHotkey.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Unicode)]
public static extern IntPtr ahkgetvar([MarshalAs(UnmanagedType.LPWStr)] string VariableName, [MarshalAs(UnmanagedType.I4)] int GetPointer);
[DllImport("AutoHotkey.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Unicode)]
public static extern IntPtr ahkFindFunc([MarshalAs(UnmanagedType.LPWStr)] string FuncName);
[DllImport("AutoHotkey.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Unicode)]
public static extern IntPtr ahkFindLabel([MarshalAs(UnmanagedType.LPWStr)] string LabelName);
[DllImport("AutoHotkey.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Unicode, EntryPoint = "getVar")]
public static extern IntPtr GetVar([MarshalAs(UnmanagedType.LPWStr)] string Variable);
}
public class AutoHotkeyEngine
{
private static Lazy<AutoHotkeyEngine> lazyInstance = new Lazy<AutoHotkeyEngine>(() => new AutoHotkeyEngine());
public static AutoHotkeyEngine Instance => lazyInstance.Value;
private AutoHotkeyEngine()
{
AutoHotkeyDllLoader.EnsureDllIsLoaded();
AutoHotkeyDll.ahktextdll("", "", "");
}
public string GetVar(string variableName)
{
IntPtr ptr = AutoHotkeyDll.ahkgetvar(variableName, 0);
return Marshal.PtrToStringUni(ptr);
}
public void SetVar(string variableName, string value)
{
if (value == null)
{
value = "";
}
AutoHotkeyDll.ahkassign(variableName, value);
}
public string Eval(string code)
{
string code2 = "A__EVAL:=" + code;
AutoHotkeyDll.ahkExec(code2);
return GetVar("A__EVAL");
}
public void LoadFile(string filePath)
{
string fullPath = Path.GetFullPath(filePath);
string scriptText = File.ReadAllText(filePath);
LoadScript(scriptText);
}
public void LoadScript(string scriptText)
{
AutoHotkeyDll.addScript(scriptText, 2);
}
public void ExecRaw(string code)
{
AutoHotkeyDll.ahkExec(code);
}
public void Terminate()
{
AutoHotkeyDll.ahkTerminate(1000u);
}
public void Reset()
{
Terminate();
AutoHotkeyDll.ahkReload();
AutoHotkeyDll.ahktextdll("", "", "");
}
public void Suspend()
{
ExecRaw("Suspend, On");
}
public void UnSuspend()
{
ExecRaw("Suspend, Off");
}
public string ExecFunction(string functionName, string param1 = null, string param2 = null, string param3 = null, string param4 = null, string param5 = null, string param6 = null, string param7 = null, string param8 = null, string param9 = null, string param10 = null)
{
IntPtr intPtr = AutoHotkeyDll.ahkFunction(functionName, param1, param2, param3, param4, param5, param6, param7, param8, param9, param10);
if (intPtr == IntPtr.Zero)
{
return null;
}
return Marshal.PtrToStringUni(intPtr);
}
public bool FunctionExists(string functionName)
{
IntPtr intPtr = AutoHotkeyDll.ahkFindFunc(functionName);
return intPtr != IntPtr.Zero;
}
public void ExecLabel(string labelName)
{
AutoHotkeyDll.ahkLabel(labelName, noWait: false);
}
public bool LabelExists(string labelName)
{
IntPtr intPtr = AutoHotkeyDll.ahkFindLabel(labelName);
return intPtr != IntPtr.Zero;
}
public void InitalizePipesModule(Func<string, string> sendPipeMessageHandler)
{
PipesModuleLoader.LoadPipesModule(sendPipeMessageHandler);
}
}
[SecurityPermission(SecurityAction.LinkDemand, UnmanagedCode = true)]
internal sealed class SafeLibraryHandle : SafeHandleZeroOrMinusOneIsInvalid
{
[DllImport("kernel32.dll", SetLastError = true)]
public static extern SafeLibraryHandle LoadLibrary(string lpFileName);
[DllImport("kernel32.dll", SetLastError = true)]
[SuppressUnmanagedCodeSecurity]
[ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
private static extern bool FreeLibrary(IntPtr hModule);
private SafeLibraryHandle()
: base(ownsHandle: true)
{
}
protected override bool ReleaseHandle()
{
return FreeLibrary(handle);
}
protected override void Dispose(bool disposing)
{
base.Dispose(disposing);
}
}
}
namespace AutoHotkey.Interop.Util
{
internal static class AutoHotkeyDllLoader
{
private static readonly Lazy<SafeLibraryHandle> dllHandle = new Lazy<SafeLibraryHandle>(LoadDll);
internal static void EnsureDllIsLoaded()
{
if (!dllHandle.IsValueCreated)
{
SafeLibraryHandle value = dllHandle.Value;
}
}
private static SafeLibraryHandle LoadDll()
{
string text = (ProcessorType.Is32Bit() ? "x86" : "x64");
string text2 = text + "/AutoHotkey.dll";
if (File.Exists(text2))
{
return SafeLibraryHandle.LoadLibrary(text2);
}
return ExtractAndLoadEmbededResource(text2);
}
private static SafeLibraryHandle ExtractAndLoadEmbededResource(string relativePath)
{
Assembly assembly = typeof(AutoHotkeyEngine).Assembly;
string text = EmbededResourceHelper.FindByName(assembly, relativePath);
if (text != null)
{
string tempFolderPath = GetTempFolderPath();
string text2 = Path.Combine(tempFolderPath, relativePath);
EmbededResourceHelper.ExtractToFile(assembly, text, text2);
return SafeLibraryHandle.LoadLibrary(text2);
}
return null;
}
private static string GetTempFolderPath()
{
string tempPath = Path.GetTempPath();
string path = "AutoHotkey.Interop";
string path2 = typeof(AutoHotkeyEngine).Assembly.GetName().Version.ToString();
return Path.Combine(tempPath, path, path2);
}
}
internal static class EmbededResourceHelper
{
public static string FindByName(Assembly assembly, string path)
{
path = Regex.Replace(path, "[/\\\\]", ".");
string value = (path.StartsWith(".") ? path : ("." + path));
string[] manifestResourceNames = assembly.GetManifestResourceNames();
string[] array = manifestResourceNames;
foreach (string text in array)
{
if (text.EndsWith(value, StringComparison.InvariantCultureIgnoreCase))
{
return text;
}
if (text.Equals(path, StringComparison.InvariantCultureIgnoreCase))
{
return text;
}
}
return null;
}
public static void ExtractToFile(Assembly assembly, string embededResourceName, string outputFilePath)
{
if (File.Exists(outputFilePath))
{
return;
}
string text = FindByName(assembly, embededResourceName);
if (text == null)
{
throw new FileNotFoundException($"Cannot find resource name of '{embededResourceName}' in assembly '{assembly.GetName().Name}'", embededResourceName);
}
EnsureDirectoryExistsForFile(outputFilePath);
using Stream stream = assembly.GetManifestResourceStream(text);
using FileStream destination = File.Open(outputFilePath, FileMode.Create);
stream.CopyTo(destination);
stream.Flush();
}
public static string ExtractToText(Assembly assembly, string embededResourceName)
{
string text = FindByName(assembly, embededResourceName);
if (text == null)
{
throw new FileNotFoundException($"Cannot find resource name of '{embededResourceName}' in assembly '{assembly.GetName().Name}'", embededResourceName);
}
using Stream stream = assembly.GetManifestResourceStream(text);
using StreamReader streamReader = new StreamReader(stream);
return streamReader.ReadToEnd();
}
private static void EnsureDirectoryExistsForFile(string targetFileName)
{
string fullPath = Path.GetFullPath(targetFileName);
string directoryName = Path.GetDirectoryName(fullPath);
if (!Directory.Exists(directoryName))
{
Directory.CreateDirectory(directoryName);
}
}
}
internal static class ProcessorType
{
public static bool Is64Bit()
{
return IntPtr.Size == 8;
}
public static bool Is32Bit()
{
return IntPtr.Size == 4;
}
}
}
namespace AutoHotkey.Interop.Pipes
{
internal class MessageHandlerPipeServer : NamedPipeServer
{
private Func<string, string> messageHandler = null;
public MessageHandlerPipeServer(string pipeName, Func<string, string> messageHandler)
: base(pipeName)
{
if (pipeName == null)
{
throw new ArgumentNullException("pipeName");
}
if (messageHandler == null)
{
throw new ArgumentNullException("messageHandler");
}
this.messageHandler = messageHandler;
}
protected override string HandleClientMessage(string clientMessage)
{
return messageHandler(clientMessage);
}
}
internal abstract class NamedPipeServer
{
private readonly string pipeName;
private volatile NamedPipeServerStream serverStream;
public bool IsClientConnected
{
get
{
if (serverStream == null)
{
return false;
}
return serverStream.IsConnected;
}
}
public NamedPipeServer(string pipeName)
{
this.pipeName = pipeName;
}
public void Start()
{
serverStream = MakeNamedPipeServerStream(pipeName);
serverStream.BeginWaitForConnection(DoConnectionLoop, null);
}
private async void DoConnectionLoop(IAsyncResult result)
{
if (!result.IsCompleted || serverStream == null)
{
return;
}
try
{
serverStream.EndWaitForConnection(result);
}
catch (IOException)
{
RebuildNamedPipe();
return;
}
catch (ObjectDisposedException)
{
RebuildNamedPipe();
return;
}
catch (OperationCanceledException)
{
RebuildNamedPipe();
return;
}
while (IsClientConnected && serverStream != null)
{
string clientMessage;
try
{
clientMessage = await ReadClientMessage(serverStream);
}
catch (IOException)
{
RebuildNamedPipe();
return;
}
catch (ObjectDisposedException)
{
RebuildNamedPipe();
return;
}
catch (OperationCanceledException)
{
RebuildNamedPipe();
return;
}
string serverResponce = HandleClientMessage(clientMessage);
if (serverStream == null)
{
break;
}
try
{
await SendResponceToClient(serverStream, serverResponce);
}
catch (IOException)
{
RebuildNamedPipe();
return;
}
catch (ObjectDisposedException)
{
RebuildNamedPipe();
return;
}
catch (OperationCanceledException)
{
RebuildNamedPipe();
return;
}
}
if (serverStream != null)
{
serverStream.BeginWaitForConnection(DoConnectionLoop, null);
}
}
private void RebuildNamedPipe()
{
Shutdown();
serverStream = MakeNamedPipeServerStream(pipeName);
serverStream.BeginWaitForConnection(DoConnectionLoop, null);
}
protected abstract string HandleClientMessage(string clientMessage);
private static async Task SendResponceToClient(NamedPipeServerStream stream, string serverResponce)
{
byte[] responceData = Encoding.Unicode.GetBytes(serverResponce);
await stream.WriteAsync(responceData, 0, responceData.Length);
await stream.FlushAsync();
stream.WaitForPipeDrain();
}
private static async Task<string> ReadClientMessage(NamedPipeServerStream stream)
{
byte[] buffer = new byte[65535];
int read = await stream.ReadAsync(buffer, 0, buffer.Length);
return Encoding.Unicode.GetString(buffer, 0, read);
}
public void Shutdown()
{
if (serverStream != null)
{
try
{
serverStream.Close();
}
catch
{
}
try
{
serverStream.Dispose();
}
catch
{
}
serverStream = null;
}
}
private NamedPipeServerStream MakeNamedPipeServerStream(string pipeName)
{
return new NamedPipeServerStream(pipeName, PipeDirection.InOut, -1, PipeTransmissionMode.Byte, PipeOptions.Asynchronous);
}
}
public static class PipesModuleLoader
{
public static object lockObj = new object();
internal static MessageHandlerPipeServer Server { get; private set; }
public static void LoadPipesModule(Func<string, string> messageHandler)
{
lock (lockObj)
{
string pipename = GeneratePipeName();
InitPipeServer(pipename, messageHandler);
InitPipeClient(pipename);
}
}
private static void InitPipeClient(string pipename)
{
if (!AutoHotkeyEngine.Instance.FunctionExists("pipeclient_getversion"))
{
string scriptText = EmbededResourceHelper.ExtractToText(typeof(PipesModuleLoader).Assembly, "Pipes/pipeclient.ahk");
AutoHotkeyEngine.Instance.LoadScript(scriptText);
}
else
{
AutoHotkeyEngine.Instance.LoadScript("A__PIPECLIENT.close()");
}
AutoHotkeyEngine.Instance.LoadScript($"A__PIPECLIENT := new pipeclient({AhkEscape.Quote(pipename)})");
}
private static void InitPipeServer(string pipename, Func<string, string> messageHandler)
{
if (Server != null)
{
Server.Shutdown();
}
Server = new MessageHandlerPipeServer(pipename, messageHandler);
Server.Start();
}
private static string GeneratePipeName()
{
return "AHK-PIPE-" + Guid.NewGuid().ToString().Replace("-", "");
}
}
}