1. 内存操作
    
    /// 
    /// Memory.dll class. Full documentation at
    /// 
    public class Mem
    {
        #region DllImports
        [DllImport("kernel32.dll")]
        public static extern IntPtr OpenProcess(
            UInt32 dwDesiredAccess,
            bool bInheritHandle,
            Int32 dwProcessId
            );

#if WINXP
#else
[DllImport("kernel32.dll", EntryPoint = "VirtualQueryEx")]
public static extern UIntPtr Native_VirtualQueryEx(IntPtr hProcess, UIntPtr lpAddress,
out MEMORY_BASIC_INFORMATION32 lpBuffer, UIntPtr dwLength);

    [DllImport("kernel32.dll", EntryPoint = "VirtualQueryEx")]
    public static extern UIntPtr Native_VirtualQueryEx(IntPtr hProcess, UIntPtr lpAddress,
        out MEMORY_BASIC_INFORMATION64 lpBuffer, UIntPtr dwLength);

    [DllImport("kernel32.dll")]
    static extern uint GetLastError();

    public UIntPtr VirtualQueryEx(IntPtr hProcess, UIntPtr lpAddress,
        out MEMORY_BASIC_INFORMATION lpBuffer)
    {
        UIntPtr retVal;

        // TODO: Need to change this to only check once.
        if (Is64Bit || IntPtr.Size == 8)
        {
            // 64 bit
            MEMORY_BASIC_INFORMATION64 tmp64 = new MEMORY_BASIC_INFORMATION64();
            retVal = Native_VirtualQueryEx(hProcess, lpAddress, out tmp64, new UIntPtr((uint)Marshal.SizeOf(tmp64)));

            lpBuffer.BaseAddress = tmp64.BaseAddress;
            lpBuffer.AllocationBase = tmp64.AllocationBase;
            lpBuffer.AllocationProtect = tmp64.AllocationProtect;
            lpBuffer.RegionSize = (long)tmp64.RegionSize;
            lpBuffer.State = tmp64.State;
            lpBuffer.Protect = tmp64.Protect;
            lpBuffer.Type = tmp64.Type;

            return retVal;
        }

        MEMORY_BASIC_INFORMATION32 tmp32 = new MEMORY_BASIC_INFORMATION32();

        retVal = Native_VirtualQueryEx(hProcess, lpAddress, out tmp32, new UIntPtr((uint)Marshal.SizeOf(tmp32)));

        lpBuffer.BaseAddress = tmp32.BaseAddress;
        lpBuffer.AllocationBase = tmp32.AllocationBase;
        lpBuffer.AllocationProtect = tmp32.AllocationProtect;
        lpBuffer.RegionSize = tmp32.RegionSize;
        lpBuffer.State = tmp32.State;
        lpBuffer.Protect = tmp32.Protect;
        lpBuffer.Type = tmp32.Type;

        return retVal;
    }

    [DllImport("kernel32.dll")]
    static extern void GetSystemInfo(out SYSTEM_INFO lpSystemInfo);

#endif

    [DllImport("kernel32.dll")]
    static extern IntPtr OpenThread(ThreadAccess dwDesiredAccess, bool bInheritHandle, uint dwThreadId);
    [DllImport("kernel32.dll")]
    static extern uint SuspendThread(IntPtr hThread);
    [DllImport("kernel32.dll")]
    static extern int ResumeThread(IntPtr hThread);

    [DllImport("dbghelp.dll")]
    static extern bool MiniDumpWriteDump(
        IntPtr hProcess,
        Int32 ProcessId,
        IntPtr hFile,
        MINIDUMP_TYPE DumpType,
        IntPtr ExceptionParam,
        IntPtr UserStreamParam,
        IntPtr CallackParam);

    [DllImport("user32.dll", SetLastError = true)]
    static extern int GetWindowLong(IntPtr hWnd, int nIndex);

    [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = false)]
    public static extern IntPtr SendMessage(IntPtr hWnd, uint Msg, IntPtr w, IntPtr l);

    [DllImport("kernel32.dll")]
    static extern bool WriteProcessMemory(
        IntPtr hProcess,
        UIntPtr lpBaseAddress,
        string lpBuffer,
        UIntPtr nSize,
        out IntPtr lpNumberOfBytesWritten
    );

    [DllImport("kernel32.dll")]
    static extern int GetProcessId(IntPtr handle);

    [DllImport("kernel32.dll", CharSet = CharSet.Unicode)]
    static extern uint GetPrivateProfileString(
       string lpAppName,
       string lpKeyName,
       string lpDefault,
       StringBuilder lpReturnedString,
       uint nSize,
       string lpFileName);

    [DllImport("kernel32.dll", SetLastError = true, ExactSpelling = true)]
    static extern bool VirtualFreeEx(
        IntPtr hProcess,
        UIntPtr lpAddress,
        UIntPtr dwSize,
        uint dwFreeType
        );

    [DllImport("kernel32.dll")]
    private static extern bool ReadProcessMemory(IntPtr hProcess, UIntPtr lpBaseAddress, [Out] byte[] lpBuffer, UIntPtr nSize, IntPtr lpNumberOfBytesRead);

    [DllImport("kernel32.dll")]
    private static extern bool ReadProcessMemory(IntPtr hProcess, UIntPtr lpBaseAddress, [Out] byte[] lpBuffer, UIntPtr nSize, out ulong lpNumberOfBytesRead);

    [DllImport("kernel32.dll")]
    private static extern bool ReadProcessMemory(IntPtr hProcess, UIntPtr lpBaseAddress, [Out] IntPtr lpBuffer, UIntPtr nSize, out ulong lpNumberOfBytesRead);

    [DllImport("kernel32.dll", SetLastError = true, ExactSpelling = true)]
    static extern UIntPtr VirtualAllocEx(
        IntPtr hProcess,
        UIntPtr lpAddress,
        uint dwSize,
        uint flAllocationType,
        uint flProtect
    );

    [DllImport("kernel32.dll", CharSet = CharSet.Ansi, ExactSpelling = true)]
    public static extern UIntPtr GetProcAddress(
        IntPtr hModule,
        string procName
    );

    [DllImport("kernel32.dll", EntryPoint = "CloseHandle")]
    private static extern bool _CloseHandle(IntPtr hObject);

    [DllImport("kernel32.dll")]
    public static extern Int32 CloseHandle(
    IntPtr hObject
    );

    [DllImport("kernel32.dll", CharSet = CharSet.Auto)]
    public static extern IntPtr GetModuleHandle(
        string lpModuleName
    );

    [DllImport("kernel32", SetLastError = true, ExactSpelling = true)]
    internal static extern Int32 WaitForSingleObject(
        IntPtr handle,
        Int32 milliseconds
    );

    [DllImport("kernel32.dll")]
    private static extern bool WriteProcessMemory(IntPtr hProcess, UIntPtr lpBaseAddress, byte[] lpBuffer, UIntPtr nSize, IntPtr lpNumberOfBytesWritten);

    // Added to avoid casting to UIntPtr
    [DllImport("kernel32.dll")]
    private static extern bool WriteProcessMemory(IntPtr hProcess, UIntPtr lpBaseAddress, byte[] lpBuffer, UIntPtr nSize, out IntPtr lpNumberOfBytesWritten);

    [DllImport("kernel32")]
    public static extern IntPtr CreateRemoteThread(
      IntPtr hProcess,
      IntPtr lpThreadAttributes,
      uint dwStackSize,
      UIntPtr lpStartAddress, // raw Pointer into remote process  
      UIntPtr lpParameter,
      uint dwCreationFlags,
      out IntPtr lpThreadId
    );

    [DllImport("kernel32")]
    public static extern bool IsWow64Process(IntPtr hProcess, out bool lpSystemInfo);

    [DllImport("user32.dll")]
    static extern bool SetForegroundWindow(IntPtr hWnd);

    // privileges
    const int PROCESS_CREATE_THREAD = 0x0002;
    const int PROCESS_QUERY_INFORMATION = 0x0400;
    const int PROCESS_VM_OPERATION = 0x0008;
    const int PROCESS_VM_WRITE = 0x0020;
    const int PROCESS_VM_READ = 0x0010;

    // used for memory allocation
    const uint MEM_FREE = 0x10000;
    const uint MEM_COMMIT = 0x00001000;
    const uint MEM_RESERVE = 0x00002000;

    private const uint PAGE_READONLY = 0x02;
    const uint PAGE_READWRITE = 0x04;
    const uint PAGE_WRITECOPY = 0x08;
    private const uint PAGE_EXECUTE_READWRITE = 0x40;
    private const uint PAGE_EXECUTE_WRITECOPY = 0x80;
    private const uint PAGE_EXECUTE = 0x10;
    private const uint PAGE_EXECUTE_READ = 0x20;

    private const uint PAGE_GUARD = 0x100;
    private const uint PAGE_NOACCESS = 0x01;

    private uint MEM_PRIVATE = 0x20000;
    private uint MEM_IMAGE = 0x1000000;

    #endregion

    /// 
    /// The process handle that was opened. (Use OpenProcess function to populate this variable)
    /// 
    public IntPtr pHandle;
    Dictionary FreezeTokenSrcs = new Dictionary();
    public Process theProc = null;

    internal enum MINIDUMP_TYPE
    {
        MiniDumpNormal = 0x00000000,
        MiniDumpWithDataSegs = 0x00000001,
        MiniDumpWithFullMemory = 0x00000002,
        MiniDumpWithHandleData = 0x00000004,
        MiniDumpFilterMemory = 0x00000008,
        MiniDumpScanMemory = 0x00000010,
        MiniDumpWithUnloadedModules = 0x00000020,
        MiniDumpWithIndirectlyReferencedMemory = 0x00000040,
        MiniDumpFilterModulePaths = 0x00000080,
        MiniDumpWithProcessThreadData = 0x00000100,
        MiniDumpWithPrivateReadWriteMemory = 0x00000200,
        MiniDumpWithoutOptionalData = 0x00000400,
        MiniDumpWithFullMemoryInfo = 0x00000800,
        MiniDumpWithThreadInfo = 0x00001000,
        MiniDumpWithCodeSegs = 0x00002000
    }

    bool IsDigitsOnly(string str)
    {
        foreach (char c in str)
        {
            if (c < '0' || c > '9')
                return false;
        }
        return true;
    }

    /// 
    /// Freeze a value to an address.
    /// 
    /// Your address
    /// byte, 2bytes, bytes, float, int, string, double or long.
    /// Value to freeze
    /// ini file to read address from (OPTIONAL)
    public void FreezeValue(string address, string type, string value, string file = "")
    {
        CancellationTokenSource cts = new CancellationTokenSource();

        if (FreezeTokenSrcs.ContainsKey(address))
        {
            Debug.WriteLine("Changing Freezing Address " + address + " Value " + value);
            try
            {
                FreezeTokenSrcs[address].Cancel();
                FreezeTokenSrcs.Remove(address);
            }
            catch
            {
                Debug.WriteLine("ERROR: Avoided a crash. Address " + address + " was not frozen.");
            }
        }
        else 
            Debug.WriteLine("Adding Freezing Address " + address + " Value " + value);

        FreezeTokenSrcs.Add(address, cts);

        Task.Factory.StartNew(() =>
        {
            while (!cts.Token.IsCancellationRequested)
            {
                WriteMemory(address, type, value, file);
                Thread.Sleep(25);
            }
        },
        cts.Token);
    }

    /// 
    /// Unfreeze a frozen value at an address
    /// 
    /// address where frozen value is stored
    public void UnfreezeValue(string address)
    {
        Debug.WriteLine("Un-Freezing Address " + address);
        try
        {
            FreezeTokenSrcs[address].Cancel();
            FreezeTokenSrcs.Remove(address);
        }
        catch
        {
            Debug.WriteLine("ERROR: Address " + address + " was not frozen.");
        }
    }

    /// 
    /// Open the PC game process with all security and access rights.
    /// 
    /// Use process name or process ID here.
    /// 
    public bool OpenProcess(int pid)
    {
        try
        {
            if (theProc != null && theProc.Id == pid)
                return true;

            if (pid <= 0)
            {
                Debug.WriteLine("ERROR: OpenProcess given proc ID 0.");
                return false;
            }

            theProc = Process.GetProcessById(pid);

            if (theProc != null && !theProc.Responding)
            {
                Debug.WriteLine("ERROR: OpenProcess: Process is not responding or null.");
                return false;
            }

            pHandle = OpenProcess(0x1F0FFF, true, pid);
            Process.EnterDebugMode();

            if (pHandle == IntPtr.Zero)
            {
                var eCode = Marshal.GetLastWin32Error();
            }

            mainModule = theProc.MainModule;

            GetModules();

            // Lets set the process to 64bit or not here (cuts down on api calls)
            Is64Bit = Environment.Is64BitOperatingSystem && (IsWow64Process(pHandle, out bool retVal) && !retVal);

            Debug.WriteLine("Program is operating at Administrative level. Process #" + theProc + " is open and modules are stored.");

            return true;
        }
        catch {
            Debug.WriteLine("ERROR: OpenProcess has crashed. Are you trying to hack a x64 game? https://github.com/erfg12/memory.dll/wiki/64bit-Games");
            return false;
        }
    }

    /// 
    /// Open the PC game process with all security and access rights.
    /// 
    /// Use process name or process ID here.
    /// 
    public bool OpenProcess(string proc)
    {
        return OpenProcess(GetProcIdFromName(proc));
    }

    /// 
    /// Check if opened process is 64bit. Used primarily for getCode().
    /// 
    /// True if 64bit false if 32bit.
    private bool _is64Bit;
    public bool Is64Bit
    {
        get { return _is64Bit; }
        private set { _is64Bit = value; }
    }

    /// 
    /// Builds the process modules dictionary (names with addresses).
    /// 
    public void GetModules()
    {
        if (theProc == null)
            return;

        modules.Clear();
        foreach (ProcessModule Module in theProc.Modules)
        {
            if (!string.IsNullOrEmpty(Module.ModuleName) && !modules.ContainsKey(Module.ModuleName))
                modules.Add(Module.ModuleName, Module.BaseAddress);
        }
    }

    public void SetFocus()
    {
        //int style = GetWindowLong(procs.MainWindowHandle, -16);
        //if ((style & 0x20000000) == 0x20000000) //minimized
        //    SendMessage(procs.Handle, 0x0112, (IntPtr)0xF120, IntPtr.Zero);
        SetForegroundWindow(theProc.MainWindowHandle);
    }

    /// 
    /// Get the process ID number by process name.
    /// 
    /// Example: "eqgame". Use task manager to find the name. Do not include .exe
    /// 
    public int GetProcIdFromName(string name) //new 1.0.2 function
    {
        Process[] processlist = Process.GetProcesses();

        if (name.ToLower().Contains(".exe"))
            name = name.Replace(".exe", "");
        if (name.ToLower().Contains(".bin")) // test
            name = name.Replace(".bin", "");

        foreach (Process theprocess in processlist)
        {
            if (theprocess.ProcessName.Equals(name, StringComparison.CurrentCultureIgnoreCase)) //find (name).exe in the process list (use task manager to find the name)
                return theprocess.Id;
        }

        return 0; //if we fail to find it
    }

    /// 
    /// Get code from ini file.
    /// 
    /// label for address or code
    /// path and name of ini file
    /// 
    public string LoadCode(string name, string file)
    {
        StringBuilder returnCode = new StringBuilder(1024);
        uint read_ini_result;

        if (file != "")
            read_ini_result = GetPrivateProfileString("codes", name, "", returnCode, (uint)returnCode.Capacity, file);
        else
            returnCode.Append(name);

        return returnCode.ToString();
    }

    private int LoadIntCode(string name, string path)
    {
        try
        {
            int intValue = Convert.ToInt32(LoadCode(name, path), 16);
            if (intValue >= 0)
                return intValue;
            else
                return 0;
        } catch
        {
            Debug.WriteLine("ERROR: LoadIntCode function crashed!");
            return 0;
        }
    }

    /// 
    /// Dictionary with our opened process module names with addresses.
    /// 
    public Dictionary modules = new Dictionary();

    /// 
    /// Make a named pipe (if not already made) and call to a remote function.
    /// 
    /// remote function to call
    /// name of the thread
    public void ThreadStartClient(string func, string name)
    {
        //ManualResetEvent SyncClientServer = (ManualResetEvent)obj;
        using (NamedPipeClientStream pipeStream = new NamedPipeClientStream(name))
        {
            if (!pipeStream.IsConnected)
                pipeStream.Connect();

            //MessageBox.Show("[Client] Pipe connection established");
            using (StreamWriter sw = new StreamWriter(pipeStream))
            {
                if (!sw.AutoFlush)
                    sw.AutoFlush = true;
                sw.WriteLine(func);
            }
        }
    }

    private ProcessModule mainModule;

    /// 
    /// Cut a string that goes on for too long or one that is possibly merged with another string.
    /// 
    /// The string you want to cut.
    /// 
    public string CutString(string str)
    {
        StringBuilder sb = new StringBuilder();
        foreach (char c in str)
        {
            if (c >= ' ' && c <= '~')
                sb.Append(c);
            else
                break;
        }
        return sb.ToString();
    }

    /// 
    /// Clean up a string that has bad characters in it.
    /// 
    /// The string you want to sanitize.
    /// 
    public string SanitizeString(string str)
    {
        StringBuilder sb = new StringBuilder();
        foreach (char c in str)
        {
            if (c >= ' ' && c <= '~')
                sb.Append(c);
        }
        return sb.ToString();
    }

    #region readMemory
    /// 
    /// Reads up to `length ` bytes from an address.
    /// 
    /// address, module + pointer + offset, module + offset OR label in .ini file.
    /// The maximum bytes to read.
    /// path and name of ini file.
    /// The bytes read or null
    public byte[] ReadBytes(string code, long length, string file = "")
    {
        byte[] memory = new byte[length];
        UIntPtr theCode = GetCode(code, file);

        if (!ReadProcessMemory(pHandle, theCode, memory, (UIntPtr)length, IntPtr.Zero))
            return null;

        return memory;
    }

    /// 
    /// Read a float value from an address.
    /// 
    /// address, module + pointer + offset, module + offset OR label in .ini file.
    /// path and name of ini file. (OPTIONAL)
    /// Round the value to 2 decimal places
    /// 
    public float ReadFloat(string code, string file = "", bool round = true)
    {
        byte[] memory = new byte[4];

        UIntPtr theCode;
        theCode = GetCode(code, file);
        try
        {
            if (ReadProcessMemory(pHandle, theCode, memory, (UIntPtr)4, IntPtr.Zero))
            {
                float address = BitConverter.ToSingle(memory, 0);
                float returnValue = (float)address;
                if (round)
                    returnValue = (float)Math.Round(address, 2);
                return returnValue;
            }
            else
                return 0;
        }
        catch
        {
            return 0;
        }
    }

    /// 
    /// Read a string value from an address.
    /// 
    /// address, module + pointer + offset, module + offset OR label in .ini file.
    /// path and name of ini file. (OPTIONAL)
    /// length of bytes to read (OPTIONAL)
    /// terminate string at null char
    /// 
    public string ReadString(string code, string file = "", int length = 32, bool zeroTerminated = true)
    {
        byte[] memoryNormal = new byte[length];
        UIntPtr theCode;
        theCode = GetCode(code, file);
        if (ReadProcessMemory(pHandle, theCode, memoryNormal, (UIntPtr)length, IntPtr.Zero))
            return (zeroTerminated) ? Encoding.Unicode.GetString(memoryNormal).Split('\0')[0] : Encoding.Unicode.GetString(memoryNormal);
        else
            return "";
    }

    /// 
    /// Read a double value
    /// 
    /// address, module + pointer + offset, module + offset OR label in .ini file.
    /// path and name of ini file. (OPTIONAL)
    /// Round the value to 2 decimal places
    /// 
    public double ReadDouble(string code, string file = "", bool round = true)
    {
        byte[] memory = new byte[8];

        UIntPtr theCode;
        theCode = GetCode(code, file);
        try
        {
            if (ReadProcessMemory(pHandle, theCode, memory, (UIntPtr)8, IntPtr.Zero))
            {
                double address = BitConverter.ToDouble(memory, 0);
                double returnValue = (double)address;
                if (round)
                    returnValue = (double)Math.Round(address, 2);
                return returnValue;
            }
            else
                return 0;
        }
        catch
        {
            return 0;
        }
    }

    public int ReadUIntPtr(UIntPtr code)
    {
        byte[] memory = new byte[4];
        if (ReadProcessMemory(pHandle, code, memory, (UIntPtr)4, IntPtr.Zero))
            return BitConverter.ToInt32(memory, 0);
        else
            return 0;
    }

    /// 
    /// Read an integer from an address.
    /// 
    /// address, module + pointer + offset, module + offset OR label in .ini file.
    /// path and name of ini file. (OPTIONAL)
    /// 
    public int ReadInt(string code, string file = "")
    {
        byte[] memory = new byte[4];
        UIntPtr theCode;
        theCode = GetCode(code, file);
        if (ReadProcessMemory(pHandle, theCode, memory, (UIntPtr)4, IntPtr.Zero))
            return BitConverter.ToInt32(memory, 0);
        else
            return 0;
    }

    /// 
    /// Read a long value from an address.
    /// 
    /// address, module + pointer + offset, module + offset OR label in .ini file.
    /// path and name of ini file. (OPTIONAL)
    /// 
    public long ReadLong(string code, string file = "")
    {
        byte[] memory = new byte[16];
        UIntPtr theCode;

        theCode = GetCode(code, file);

        if (ReadProcessMemory(pHandle, theCode, memory, (UIntPtr)16, IntPtr.Zero))
            return BitConverter.ToInt64(memory, 0);
        else
            return 0;
    }

    /// 
    /// Read a UInt value from address.
    /// 
    /// address, module + pointer + offset, module + offset OR label in .ini file.
    /// path and name of ini file. (OPTIONAL)
    /// 
    public UInt64 ReadUInt(string code, string file = "")
    {
        byte[] memory = new byte[4];
        UIntPtr theCode;
        theCode = GetCode(code, file);

        if (ReadProcessMemory(pHandle, theCode, memory, (UIntPtr)4, IntPtr.Zero))
            return BitConverter.ToUInt64(memory, 0);
        else
            return 0;
    }

    /// 
    /// Reads a 2 byte value from an address and moves the address.
    /// 
    /// address, module + pointer + offset, module + offset OR label in .ini file.
    /// Quantity to move.
    /// path and name of ini file (OPTIONAL)
    /// 
    public int Read2ByteMove(string code, int moveQty, string file = "")
    {
        byte[] memory = new byte[4];
        UIntPtr theCode;
        theCode = GetCode(code, file);

        UIntPtr newCode = UIntPtr.Add(theCode, moveQty);

        if (ReadProcessMemory(pHandle, newCode, memory, (UIntPtr)2, IntPtr.Zero))
            return BitConverter.ToInt32(memory, 0);
        else
            return 0;
    }

    /// 
    /// Reads an integer value from address and moves the address.
    /// 
    /// address, module + pointer + offset, module + offset OR label in .ini file.
    /// Quantity to move.
    /// path and name of ini file (OPTIONAL)
    /// 
    public int ReadIntMove(string code, int moveQty, string file = "")
    {
        byte[] memory = new byte[4];
        UIntPtr theCode;
        theCode = GetCode(code, file);

        UIntPtr newCode = UIntPtr.Add(theCode, moveQty);

        if (ReadProcessMemory(pHandle, newCode, memory, (UIntPtr)4, IntPtr.Zero))
            return BitConverter.ToInt32(memory, 0);
        else
            return 0;
    }

    /// 
    /// Get UInt and move to another address by moveQty. Use in a for loop.
    /// 
    /// address, module + pointer + offset, module + offset OR label in .ini file.
    /// Quantity to move.
    /// path and name of ini file (OPTIONAL)
    /// 
    public ulong ReadUIntMove(string code, int moveQty, string file = "")
    {
        byte[] memory = new byte[8];
        UIntPtr theCode;
        theCode = GetCode(code, file, 8);

        UIntPtr newCode = UIntPtr.Add(theCode, moveQty);

        if (ReadProcessMemory(pHandle, newCode, memory, (UIntPtr)8, IntPtr.Zero))
            return BitConverter.ToUInt64(memory, 0);
        else
            return 0;
    }

    /// 
    /// Read a 2 byte value from an address. Returns an integer.
    /// 
    /// address, module + pointer + offset, module + offset OR label in .ini file.
    /// path and file name to ini file. (OPTIONAL)
    /// 
    public int Read2Byte(string code, string file = "")
    {
        byte[] memoryTiny = new byte[4];

        UIntPtr theCode;
        theCode = GetCode(code, file);

        if (ReadProcessMemory(pHandle, theCode, memoryTiny, (UIntPtr)2, IntPtr.Zero))
            return BitConverter.ToInt32(memoryTiny, 0);
        else
            return 0;
    }

    /// 
    /// Read 1 byte from address.
    /// 
    /// address, module + pointer + offset, module + offset OR label in .ini file.
    /// path and file name of ini file. (OPTIONAL)
    /// 
    public int ReadByte(string code, string file = "")
    {
        byte[] memoryTiny = new byte[1];

        UIntPtr theCode = GetCode(code, file);

        if (ReadProcessMemory(pHandle, theCode, memoryTiny, (UIntPtr) 1, IntPtr.Zero))
            return memoryTiny[0];

        return 0;
    }

    /// 
    /// Reads a byte from memory and splits it into bits
    /// 
    /// address, module + pointer + offset, module + offset OR label in .ini file.
    /// path and file name of ini file. (OPTIONAL)
    /// Array of 8 booleans representing each bit of the byte read
    public bool[] ReadBits(string code, string file = "")
    {
        byte[] buf = new byte[1];

        UIntPtr theCode = GetCode(code, file);

        bool[] ret = new bool[8];

        if (!ReadProcessMemory(pHandle, theCode, buf, (UIntPtr) 1, IntPtr.Zero))
            return ret;

        if (!BitConverter.IsLittleEndian)
            throw new Exception("Should be little endian");

        for (var i = 0; i < 8; i++)
            ret[i] = Convert.ToBoolean(buf[0] & (1 << i));

        return ret;

    }

    public int ReadPByte(UIntPtr address, string code, string file = "")
    {
        byte[] memory = new byte[4];
        if (ReadProcessMemory(pHandle, address + LoadIntCode(code, file), memory, (UIntPtr)1, IntPtr.Zero))
            return BitConverter.ToInt32(memory, 0);
        else
            return 0;
    }

    public float ReadPFloat(UIntPtr address, string code, string file = "")
    {
        byte[] memory = new byte[4];
        if (ReadProcessMemory(pHandle, address + LoadIntCode(code, file), memory, (UIntPtr)4, IntPtr.Zero))
        {
            float spawn = BitConverter.ToSingle(memory, 0);
            return (float)Math.Round(spawn, 2);
        }
        else
            return 0;
    }

    public int ReadPInt(UIntPtr address, string code, string file = "")
    {
        byte[] memory = new byte[4];
        if (ReadProcessMemory(pHandle, address + LoadIntCode(code, file), memory, (UIntPtr)4, IntPtr.Zero))
            return BitConverter.ToInt32(memory, 0);
        else
            return 0;
    }

    public string ReadPString(UIntPtr address, string code, string file = "")
    {
        byte[] memoryNormal = new byte[32];
        if (ReadProcessMemory(pHandle, address + LoadIntCode(code, file), memoryNormal, (UIntPtr)32, IntPtr.Zero))
            return CutString(System.Text.Encoding.ASCII.GetString(memoryNormal));
        else
            return "";
    }
    #endregion

    #region writeMemory
    ///
    ///Write to memory address. See https://github.com/erfg12/memory.dll/wiki/writeMemory() for more information.
    ///
    ///address, module + pointer + offset, module + offset OR label in .ini file.
    ///byte, 2bytes, bytes, float, int, string, double or long.
    ///value to write to address.
    ///path and name of .ini file (OPTIONAL)
    ///System.Text.Encoding.Unicode (DEFAULT). Other options: ascii, unicode, utf32, utf7
    public bool WriteMemory(string code, string type, string write, string file = "", System.Text.Encoding stringEncoding = null)
    {
        byte[] memory = new byte[4];
        int size = 4;

        UIntPtr theCode;
        theCode = GetCode(code, file);

        if (type.ToLower() == "float")
        {
            memory = BitConverter.GetBytes(Convert.ToSingle(write));
            size = 4;
        }
        else if (type.ToLower() == "int")
        {
            memory = BitConverter.GetBytes(Convert.ToInt32(write));
            size = 4;
        }
        else if (type.ToLower() == "byte")
        {
            memory = new byte[1];
            memory[0] = Convert.ToByte(write, 16);
            size = 1;
        }
        else if (type.ToLower() == "2bytes")
        {
            memory = new byte[2];
            memory[0] = (byte)(Convert.ToInt32(write) % 256);
            memory[1] = (byte)(Convert.ToInt32(write) / 256);
            size = 2;
        }
        else if (type.ToLower() == "bytes")
        {
            if (write.Contains(",") || write.Contains(" ")) //check if it's a proper array
            {
                string[] stringBytes;
                if (write.Contains(","))
                    stringBytes = write.Split(',');
                else
                    stringBytes = write.Split(' ');
                //Debug.WriteLine("write:" + write + " stringBytes:" + stringBytes);

                int c = stringBytes.Count();
                memory = new byte[c];
                for (int i = 0; i < c; i++)
                {
                    memory[i] = Convert.ToByte(stringBytes[i], 16);
                }
                size = stringBytes.Count();
            }
            else //wasnt array, only 1 byte
            {
                memory = new byte[1];
                memory[0] = Convert.ToByte(write, 16);
                size = 1;
            }
        }
        else if (type.ToLower() == "double")
        {
            memory = BitConverter.GetBytes(Convert.ToDouble(write));
            size = 8;
        }
        else if (type.ToLower() == "long")
        {
            memory = BitConverter.GetBytes(Convert.ToInt64(write));
            size = 8;
        }
        else if (type.ToLower() == "string")
        {
            if (stringEncoding == null)
                memory = System.Text.Encoding.Unicode.GetBytes(write);
            else
                memory = stringEncoding.GetBytes(write);
            size = memory.Length;
        }
        //Debug.Write("DEBUG: Writing bytes [TYPE:" + type + " ADDR:" + theCode + "] " + String.Join(",", memory) + Environment.NewLine);
        return WriteProcessMemory(pHandle, theCode, memory, (UIntPtr)size, IntPtr.Zero);
    }

    /// 
    /// Write to address and move by moveQty. Good for byte arrays. See https://github.com/erfg12/memory.dll/wiki/Writing-a-Byte-Array for more information.
    /// 
    ///address, module + pointer + offset, module + offset OR label in .ini file.
    ///byte, bytes, float, int, string or long.
    /// byte to write
    /// quantity to move
    /// path and name of .ini file (OPTIONAL)
    /// 
    public bool WriteMove(string code, string type, string write, int moveQty, string file = "")
    {
        byte[] memory = new byte[4];
        int size = 4;

        UIntPtr theCode;
        theCode = GetCode(code, file);

        if (type == "float")
        {
            memory = new byte[write.Length];
            memory = BitConverter.GetBytes(Convert.ToSingle(write));
            size = write.Length;
        }
        else if (type == "int")
        {
            memory = BitConverter.GetBytes(Convert.ToInt32(write));
            size = 4;
        }
        else if (type == "double")
        {
            memory = BitConverter.GetBytes(Convert.ToDouble(write));
            size = 8;
        }
        else if (type == "long")
        {
            memory = BitConverter.GetBytes(Convert.ToInt64(write));
            size = 8;
        }
        else if (type == "byte")
        {
            memory = new byte[1];
            memory[0] = Convert.ToByte(write, 16);
            size = 1;
        }
        else if (type == "string")
        {
            memory = new byte[write.Length];
            memory = System.Text.Encoding.Unicode.GetBytes(write);
            size = write.Length;
        }

        UIntPtr newCode = UIntPtr.Add(theCode, moveQty);

        Debug.Write("DEBUG: Writing bytes [TYPE:" + type + " ADDR:[O]" + theCode + " [N]" + newCode + " MQTY:" + moveQty + "] " + String.Join(",", memory) + Environment.NewLine);
        Thread.Sleep(1000);
        return WriteProcessMemory(pHandle, newCode, memory, (UIntPtr)size, IntPtr.Zero);
    }

    /// 
    /// Write byte array to addresses.
    /// 
    /// address to write to
    /// byte array to write
    /// path and name of ini file. (OPTIONAL)
    public void WriteBytes(string code, byte[] write, string file = "")
    {
        UIntPtr theCode;
        theCode = GetCode(code, file);
        WriteProcessMemory(pHandle, theCode, write, (UIntPtr)write.Length, IntPtr.Zero);
    }

    /// 
    /// Takes an array of 8 booleans and writes to a single byte
    /// 
    /// address to write to
    /// Array of 8 booleans to write
    /// path and name of ini file. (OPTIONAL)
    public void WriteBits(string code, bool[] bits, string file = "")
    {
        if(bits.Length != 8)
            throw new ArgumentException("Not enough bits for a whole byte", nameof(bits));

        byte[] buf = new byte[1];

        UIntPtr theCode = GetCode(code, file);

        for (var i = 0; i < 8; i++)
        {
            if (bits[i])
                buf[0] |= (byte)(1 << i);
        }

        WriteProcessMemory(pHandle, theCode, buf, (UIntPtr) 1, IntPtr.Zero);
    }

    /// 
    /// Write byte array to address
    /// 
    /// Address to write to
    /// Byte array to write to
    public void WriteBytes(UIntPtr address, byte[] write)
    {
        WriteProcessMemory(pHandle, address, write, (UIntPtr)write.Length, out IntPtr bytesRead);
    }

    #endregion

    /// 
    /// Convert code from string to real address. If path is not blank, will pull from ini file.
    /// 
    /// label in ini file or code
    /// path to ini file (OPTIONAL)
    /// size of address (default is 8)
    /// 
    public UIntPtr GetCode(string name, string path = "", int size = 8)
    {
        string theCode = "";
        if (Is64Bit)
        {
            //Debug.WriteLine("Changing to 64bit code...");
            if (size == 8) size = 16; //change to 64bit
            return Get64BitCode(name, path, size); //jump over to 64bit code grab
        }

        if (path != "")
            theCode = LoadCode(name, path);
        else
            theCode = name;

        if (theCode == "")
        {
            //Debug.WriteLine("ERROR: LoadCode returned blank. NAME:" + name + " PATH:" + path);
            return UIntPtr.Zero;
        }
        else
        {
            //Debug.WriteLine("Found code=" + theCode + " NAME:" + name + " PATH:" + path);
        }

        // remove spaces
        if (theCode.Contains(" "))
            theCode.Replace(" ", String.Empty);

        if (!theCode.Contains("+") && !theCode.Contains(",")) return new UIntPtr(Convert.ToUInt32(theCode, 16));

        string newOffsets = theCode;

        if (theCode.Contains("+"))
            newOffsets = theCode.Substring(theCode.IndexOf('+') + 1);

        byte[] memoryAddress = new byte[size];

        if (newOffsets.Contains(','))
        {
            List offsetsList = new List();

            string[] newerOffsets = newOffsets.Split(',');
            foreach (string oldOffsets in newerOffsets)
            {
                string test = oldOffsets;
                if (oldOffsets.Contains("0x")) test = oldOffsets.Replace("0x","");
                int preParse = 0;
                if (!oldOffsets.Contains("-"))
                    preParse = Int32.Parse(test, NumberStyles.AllowHexSpecifier);
                else
                {
                    test = test.Replace("-", "");
                    preParse = Int32.Parse(test, NumberStyles.AllowHexSpecifier);
                    preParse = preParse * -1;
                }
                offsetsList.Add(preParse);
            }
            int[] offsets = offsetsList.ToArray();

            if (theCode.Contains("base") || theCode.Contains("main"))
                ReadProcessMemory(pHandle, (UIntPtr)((int)mainModule.BaseAddress + offsets[0]), memoryAddress, (UIntPtr)size, IntPtr.Zero);
            else if (!theCode.Contains("base") && !theCode.Contains("main") && theCode.Contains("+"))
            {
                string[] moduleName = theCode.Split('+');
                IntPtr altModule = IntPtr.Zero;
                if (!moduleName[0].ToLower().Contains(".dll") && !moduleName[0].ToLower().Contains(".exe") && !moduleName[0].ToLower().Contains(".bin"))
                {
                    string theAddr = moduleName[0];
                    if (theAddr.Contains("0x")) theAddr = theAddr.Replace("0x", "");
                    altModule = (IntPtr)Int32.Parse(theAddr, NumberStyles.HexNumber);
                }
                else
                {
                    try
                    {
                        altModule = modules[moduleName[0]];
                    }
                    catch
                    {
                        Debug.WriteLine("Module " + moduleName[0] + " was not found in module list!");
                        Debug.WriteLine("Modules: " + string.Join(",", modules));
                    }
                }
                ReadProcessMemory(pHandle, (UIntPtr)((int)altModule + offsets[0]), memoryAddress, (UIntPtr)size, IntPtr.Zero);
            }
            else
                ReadProcessMemory(pHandle, (UIntPtr)(offsets[0]), memoryAddress, (UIntPtr)size, IntPtr.Zero);

            uint num1 = BitConverter.ToUInt32(memoryAddress, 0); //ToUInt64 causes arithmetic overflow.

            UIntPtr base1 = (UIntPtr)0;

            for (int i = 1; i < offsets.Length; i++)
            {
                base1 = new UIntPtr(Convert.ToUInt32(num1 + offsets[i]));
                ReadProcessMemory(pHandle, base1, memoryAddress, (UIntPtr)size, IntPtr.Zero);
                num1 = BitConverter.ToUInt32(memoryAddress, 0); //ToUInt64 causes arithmetic overflow.
            }
            return base1;
        }
        else // no offsets
        {
            int trueCode = Convert.ToInt32(newOffsets, 16);
            IntPtr altModule = IntPtr.Zero;
            //Debug.WriteLine("newOffsets=" + newOffsets);
            if (theCode.ToLower().Contains("base") || theCode.ToLower().Contains("main"))
                altModule = mainModule.BaseAddress;
            else if (!theCode.ToLower().Contains("base") && !theCode.ToLower().Contains("main") && theCode.Contains("+"))
            {
                string[] moduleName = theCode.Split('+');
                if (!moduleName[0].ToLower().Contains(".dll") && !moduleName[0].ToLower().Contains(".exe") && !moduleName[0].ToLower().Contains(".bin"))
                {
                    string theAddr = moduleName[0];
                    if (theAddr.Contains("0x")) theAddr = theAddr.Replace("0x", "");
                    altModule = (IntPtr)Int32.Parse(theAddr, NumberStyles.HexNumber);
                }
                else
                {
                    try
                    {
                        altModule = modules[moduleName[0]];
                    }
                    catch
                    {
                        Debug.WriteLine("Module " + moduleName[0] + " was not found in module list!");
                        Debug.WriteLine("Modules: " + string.Join(",", modules));
                    }
                }
            }
            else
                altModule = modules[theCode.Split('+')[0]];
            return (UIntPtr)((int)altModule + trueCode);
        }
    }

    /// 
    /// Convert code from string to real address. If path is not blank, will pull from ini file.
    /// 
    /// label in ini file OR code
    /// path to ini file (OPTIONAL)
    /// size of address (default is 16)
    /// 
    public UIntPtr Get64BitCode(string name, string path = "", int size = 16)
    {
        string theCode = "";
        if (path != "")
            theCode = LoadCode(name, path);
        else
            theCode = name;

        if (theCode == "")
            return UIntPtr.Zero;

        // remove spaces
        if (theCode.Contains(" "))
            theCode.Replace(" ", String.Empty);

        string newOffsets = theCode;
        if (theCode.Contains("+"))
            newOffsets = theCode.Substring(theCode.IndexOf('+') + 1);

        byte[] memoryAddress = new byte[size];

        if (!theCode.Contains("+") && !theCode.Contains(",")) return new UIntPtr(Convert.ToUInt64(theCode, 16));

        if (newOffsets.Contains(','))
        {
            List offsetsList = new List();

            string[] newerOffsets = newOffsets.Split(',');
            foreach (string oldOffsets in newerOffsets)
            {
                string test = oldOffsets;
                if (oldOffsets.Contains("0x")) test = oldOffsets.Replace("0x", "");
                Int64 preParse = 0;
                if (!oldOffsets.Contains("-"))
                    preParse = Int64.Parse(test, NumberStyles.AllowHexSpecifier);
                else
                {
                    test = test.Replace("-", "");
                    preParse = Int64.Parse(test, NumberStyles.AllowHexSpecifier);
                    preParse = preParse * -1;
                }
                offsetsList.Add(preParse);
            }
            Int64[] offsets = offsetsList.ToArray();

            if (theCode.Contains("base") || theCode.Contains("main"))
                ReadProcessMemory(pHandle, (UIntPtr)((Int64)mainModule.BaseAddress + offsets[0]), memoryAddress, (UIntPtr)size, IntPtr.Zero);
            else if (!theCode.Contains("base") && !theCode.Contains("main") && theCode.Contains("+"))
            {
                string[] moduleName = theCode.Split('+');
                IntPtr altModule = IntPtr.Zero;
                if (!moduleName[0].ToLower().Contains(".dll") && !moduleName[0].ToLower().Contains(".exe") && !moduleName[0].ToLower().Contains(".bin"))
                    altModule = (IntPtr)Int64.Parse(moduleName[0], System.Globalization.NumberStyles.HexNumber);
                else
                {
                    try
                    {
                        altModule = modules[moduleName[0]];
                    }
                    catch
                    {
                        Debug.WriteLine("Module " + moduleName[0] + " was not found in module list!");
                        Debug.WriteLine("Modules: " + string.Join(",", modules));
                    }
                }
                ReadProcessMemory(pHandle, (UIntPtr)((Int64)altModule + offsets[0]), memoryAddress, (UIntPtr)size, IntPtr.Zero);
            }
            else // no offsets
                ReadProcessMemory(pHandle, (UIntPtr)(offsets[0]), memoryAddress, (UIntPtr)size, IntPtr.Zero);

            Int64 num1 = BitConverter.ToInt64(memoryAddress, 0);

            UIntPtr base1 = (UIntPtr)0;

            for (int i = 1; i < offsets.Length; i++)
            {
                base1 = new UIntPtr(Convert.ToUInt64(num1 + offsets[i]));
                ReadProcessMemory(pHandle, base1, memoryAddress, (UIntPtr)size, IntPtr.Zero);
                num1 = BitConverter.ToInt64(memoryAddress, 0);
            }
            return base1;
        }
        else
        {
            Int64 trueCode = Convert.ToInt64(newOffsets, 16);
            IntPtr altModule = IntPtr.Zero;
            if (theCode.Contains("base") || theCode.Contains("main"))
                altModule = mainModule.BaseAddress;
            else if (!theCode.Contains("base") && !theCode.Contains("main") && theCode.Contains("+"))
            {
                string[] moduleName = theCode.Split('+');
                if (!moduleName[0].ToLower().Contains(".dll") && !moduleName[0].ToLower().Contains(".exe") && !moduleName[0].ToLower().Contains(".bin"))
                {
                    string theAddr = moduleName[0];
                    if (theAddr.Contains("0x")) theAddr = theAddr.Replace("0x", "");
                    altModule = (IntPtr)Int64.Parse(theAddr, NumberStyles.HexNumber);
                }
                else
                {
                    try
                    {
                        altModule = modules[moduleName[0]];
                    }
                    catch
                    {
                        Debug.WriteLine("Module " + moduleName[0] + " was not found in module list!");
                        Debug.WriteLine("Modules: " + string.Join(",", modules));
                    }
                }
            }
            else
                altModule = modules[theCode.Split('+')[0]];
            return (UIntPtr)((Int64)altModule + trueCode);
        }
    }

    /// 
    /// Close the process when finished.
    /// 
    public void CloseProcess()
    {
        if (pHandle == null)
            return;

        CloseHandle(pHandle);
        theProc = null;
    }

    /// 
    /// Inject a DLL file.
    /// 
    /// path and name of DLL file.
    public void InjectDll(String strDllName)
    {
        IntPtr bytesout;

        foreach (ProcessModule pm in theProc.Modules)
        {
            if (pm.ModuleName.StartsWith("inject", StringComparison.InvariantCultureIgnoreCase))
                return;
        }

        if (!theProc.Responding)
            return;

        int lenWrite = strDllName.Length + 1;
        UIntPtr allocMem = VirtualAllocEx(pHandle, (UIntPtr)null, (uint)lenWrite, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);

        WriteProcessMemory(pHandle, allocMem, strDllName, (UIntPtr)lenWrite, out bytesout);
        UIntPtr injector = GetProcAddress(GetModuleHandle("kernel32.dll"), "LoadLibraryA");

        if (injector == null)
            return;

        IntPtr hThread = CreateRemoteThread(pHandle, (IntPtr)null, 0, injector, allocMem, 0, out bytesout);
        if (hThread == null)
            return;

        int Result = WaitForSingleObject(hThread, 10 * 1000);
        if (Result == 0x00000080L || Result == 0x00000102L)
        {
            if (hThread != null)
                CloseHandle(hThread);
            return;
        }
        VirtualFreeEx(pHandle, allocMem, (UIntPtr)0, 0x8000);

        if (hThread != null)
            CloseHandle(hThread);

        return;
    }

#if WINXP
#else
///


/// Creates a code cave to write custom opcodes in target process
///

/// Address to create the trampoline
/// The opcodes to write in the code cave
/// The number of bytes being replaced
/// size of the allocated region
/// ini file to look in
/// Please ensure that you use the proper replaceCount
/// if you replace halfway in an instruction you may cause bad things

/// UIntPtr to created code cave for use for later deallocation
public UIntPtr CreateCodeCave(string code, byte[] newBytes, int replaceCount, int size = 0x1000, string file = "")
{
if (replaceCount < 5)
return UIntPtr.Zero; // returning UIntPtr.Zero instead of throwing an exception
// to better match existing code

        UIntPtr theCode;
        theCode = GetCode(code, file);
        UIntPtr address = theCode;

        // if x64 we need to try to allocate near the address so we dont run into the +-2GB limit of the 0xE9 jmp

        UIntPtr caveAddress = UIntPtr.Zero;
        UIntPtr prefered = address;

        for(var i = 0; i < 10 && caveAddress == UIntPtr.Zero; i++)
        {
            caveAddress = VirtualAllocEx(pHandle, FindFreeBlockForRegion(prefered, (uint)size),
                                         (uint)size, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);

            if (caveAddress == UIntPtr.Zero)
                prefered = UIntPtr.Add(prefered, 0x10000);
        }

        // Failed to allocate memory around the address we wanted let windows handle it and hope for the best?
        if (caveAddress == UIntPtr.Zero)
            caveAddress = VirtualAllocEx(pHandle, UIntPtr.Zero, (uint)size, MEM_COMMIT | MEM_RESERVE,
                                         PAGE_EXECUTE_READWRITE);

        int nopsNeeded = replaceCount > 5 ? replaceCount - 5 : 0;

        // (to - from - 5)
        int offset = (int)((long)caveAddress - (long)address - 5);

        byte[] jmpBytes = new byte[5 + nopsNeeded];
        jmpBytes[0] = 0xE9;
        BitConverter.GetBytes(offset).CopyTo(jmpBytes, 1);

        for(var i = 5; i < jmpBytes.Length; i++)
        {
            jmpBytes[i] = 0x90;
        }
        WriteBytes(address, jmpBytes);

        byte[] caveBytes = new byte[5 + newBytes.Length];
        offset = (int)(((long)address + jmpBytes.Length) - ((long)caveAddress + newBytes.Length) - 5);

        newBytes.CopyTo(caveBytes, 0);
        caveBytes[newBytes.Length] = 0xE9;
        BitConverter.GetBytes(offset).CopyTo(caveBytes, newBytes.Length + 1);

        WriteBytes(caveAddress, caveBytes);

        return caveAddress;
    }

    private UIntPtr FindFreeBlockForRegion(UIntPtr baseAddress, uint size)
    {
        UIntPtr minAddress = UIntPtr.Subtract(baseAddress, 0x70000000);
        UIntPtr maxAddress = UIntPtr.Add(baseAddress, 0x70000000);

        UIntPtr ret = UIntPtr.Zero;
        UIntPtr tmpAddress = UIntPtr.Zero;

        GetSystemInfo(out SYSTEM_INFO si);

        if (Is64Bit)
        {
            if ((long)minAddress > (long)si.maximumApplicationAddress ||
                (long)minAddress < (long)si.minimumApplicationAddress)
                minAddress = si.minimumApplicationAddress;

            if ((long)maxAddress < (long)si.minimumApplicationAddress ||
                (long)maxAddress > (long)si.maximumApplicationAddress)
                maxAddress = si.maximumApplicationAddress;
        }
        else
        {
            minAddress = si.minimumApplicationAddress;
            maxAddress = si.maximumApplicationAddress;
        }

        MEMORY_BASIC_INFORMATION mbi;

        UIntPtr current = minAddress;
        UIntPtr previous = current;

        while (VirtualQueryEx(pHandle, current, out mbi).ToUInt64() != 0)
        {
            if ((long)mbi.BaseAddress > (long)maxAddress)
                return UIntPtr.Zero;  // No memory found, let windows handle

            if (mbi.State == MEM_FREE && mbi.RegionSize > size)
            {
                if ((long)mbi.BaseAddress % si.allocationGranularity > 0)
                {
                    // The whole size can not be used
                    tmpAddress = mbi.BaseAddress;
                    int offset = (int)(si.allocationGranularity -
                                       ((long)tmpAddress % si.allocationGranularity));

                    // Check if there is enough left
                    if((mbi.RegionSize - offset) >= size)
                    {
                        // yup there is enough
                        tmpAddress = UIntPtr.Add(tmpAddress, offset);

                        if((long)tmpAddress < (long)baseAddress)
                        {
                            tmpAddress = UIntPtr.Add(tmpAddress, (int)(mbi.RegionSize - offset - size));

                            if ((long)tmpAddress > (long)baseAddress)
                                tmpAddress = baseAddress;

                            // decrease tmpAddress until its alligned properly
                            tmpAddress = UIntPtr.Subtract(tmpAddress, (int)((long)tmpAddress % si.allocationGranularity));
                        }

                        // if the difference is closer then use that
                        if (Math.Abs((long)tmpAddress - (long)baseAddress) < Math.Abs((long)ret - (long)baseAddress))
                            ret = tmpAddress;
                    }
                }
                else
                {
                    tmpAddress = mbi.BaseAddress;

                    if((long)tmpAddress < (long)baseAddress) // try to get it the cloest possible 
                                                             // (so to the end of the region - size and
                                                             // aligned by system allocation granularity)
                    {
                        tmpAddress = UIntPtr.Add(tmpAddress, (int)(mbi.RegionSize - size));

                        if ((long)tmpAddress > (long)baseAddress)
                            tmpAddress = baseAddress;

                        // decrease until aligned properly
                        tmpAddress =
                            UIntPtr.Subtract(tmpAddress, (int)((long)tmpAddress % si.allocationGranularity));
                    }

                    if (Math.Abs((long)tmpAddress - (long)baseAddress) < Math.Abs((long)ret - (long)baseAddress))
                        ret = tmpAddress;
                }
            }

            if (mbi.RegionSize % si.allocationGranularity > 0)
                mbi.RegionSize += si.allocationGranularity - (mbi.RegionSize % si.allocationGranularity);

            previous = current;
            current = UIntPtr.Add(mbi.BaseAddress, (int)mbi.RegionSize);

            if ((long)current > (long)maxAddress)
                return ret;

            if ((long)previous > (long)current)
                return ret; // Overflow
        }

        return ret;
    }

#endif

    [Flags]
    public enum ThreadAccess : int
    {
        TERMINATE = (0x0001),
        SUSPEND_RESUME = (0x0002),
        GET_CONTEXT = (0x0008),
        SET_CONTEXT = (0x0010),
        SET_INFORMATION = (0x0020),
        QUERY_INFORMATION = (0x0040),
        SET_THREAD_TOKEN = (0x0080),
        IMPERSONATE = (0x0100),
        DIRECT_IMPERSONATION = (0x0200)
    }

    public static void SuspendProcess(int pid)
    {
        var process = Process.GetProcessById(pid);

        if (process.ProcessName == string.Empty)
            return;

        foreach (ProcessThread pT in process.Threads)
        {
            IntPtr pOpenThread = OpenThread(ThreadAccess.SUSPEND_RESUME, false, (uint)pT.Id);
            if (pOpenThread == IntPtr.Zero)
                continue;

            SuspendThread(pOpenThread);
            CloseHandle(pOpenThread);
        }
    }

    public static void ResumeProcess(int pid)
    {
        var process = Process.GetProcessById(pid);
        if (process.ProcessName == string.Empty)
            return;

        foreach (ProcessThread pT in process.Threads)
        {
            IntPtr pOpenThread = OpenThread(ThreadAccess.SUSPEND_RESUME, false, (uint)pT.Id);
            if (pOpenThread == IntPtr.Zero)
                continue;

            var suspendCount = 0;
            do
            {
                suspendCount = ResumeThread(pOpenThread);
            } while (suspendCount > 0);
            CloseHandle(pOpenThread);
        }
    }

#if WINXP
#else
async Task PutTaskDelay(int delay)
{
await Task.Delay(delay);
}
#endif

    void AppendAllBytes(string path, byte[] bytes)
    {
        using (var stream = new FileStream(path, FileMode.Append))
        {
            stream.Write(bytes, 0, bytes.Length);
        }
    }

    public byte[] FileToBytes(string path, bool dontDelete = false) {
        byte[] newArray = File.ReadAllBytes(path);
        if (!dontDelete)
            File.Delete(path);
        return newArray;
    }

    public string MSize()
    {
        if (Is64Bit)
            return ("x16");
        else
            return ("x8");
    }

    /// 
    /// Convert a byte array to hex values in a string.
    /// 
    /// your byte array to convert
    /// 
    public static string ByteArrayToHexString(byte[] ba)
    {
        StringBuilder hex = new StringBuilder(ba.Length * 2);
        int i = 1;
        foreach (byte b in ba)
        {
            if (i == 16)
            {
                hex.AppendFormat("{0:x2}{1}", b, Environment.NewLine);
                i = 0;
            }
            else
                hex.AppendFormat("{0:x2} ", b);
            i++;
        }
        return hex.ToString().ToUpper();
    }

    public static string ByteArrayToString(byte[] ba)
    {
        StringBuilder hex = new StringBuilder(ba.Length * 2);
        foreach (byte b in ba)
        {
            hex.AppendFormat("{0:x2} ", b);
        }
        return hex.ToString().TrimEnd();
    }

#if WINXP
#else

    public struct SYSTEM_INFO
    {
        public ushort processorArchitecture;
        ushort reserved;
        public uint pageSize;
        public UIntPtr minimumApplicationAddress;
        public UIntPtr maximumApplicationAddress;
        public IntPtr activeProcessorMask;
        public uint numberOfProcessors;
        public uint processorType;
        public uint allocationGranularity;
        public ushort processorLevel;
        public ushort processorRevision;
    }

    public struct MEMORY_BASIC_INFORMATION32
    {
        public UIntPtr BaseAddress;
        public UIntPtr AllocationBase;
        public uint AllocationProtect;
        public uint RegionSize;
        public uint State;
        public uint Protect;
        public uint Type;
    }

    public struct MEMORY_BASIC_INFORMATION64
    {
        public UIntPtr BaseAddress;
        public UIntPtr AllocationBase;
        public uint AllocationProtect;
        public uint __alignment1;
        public ulong RegionSize;
        public uint State;
        public uint Protect;
        public uint Type;
        public uint __alignment2;
    }

    public struct MEMORY_BASIC_INFORMATION
    {
        public UIntPtr BaseAddress;
        public UIntPtr AllocationBase;
        public uint AllocationProtect;
        public long RegionSize;
        public uint State;
        public uint Protect;
        public uint Type;
    }

    public ulong GetMinAddress()
    {
        SYSTEM_INFO SI;
        GetSystemInfo(out SI);
        return (ulong)SI.minimumApplicationAddress;
    }
    /// 
    /// 获取引用类型的内存地址方法
    /// 
    /// 
    /// 
    public static string GetMemory(object o) 
    {
        GCHandle h = GCHandle.Alloc(o, GCHandleType.Pinned);
        IntPtr addr = h.AddrOfPinnedObject();
        return "0x" + addr.ToString("X");
    }
    /// 
    /// Dump memory page by page to a dump.dmp file. Can be used with Cheat Engine.
    /// 
    public bool DumpMemory(string file = "dump.dmp")
    {
        Debug.Write("[DEBUG] memory dump starting... (" + DateTime.Now.ToString("h:mm:ss tt") + ")" + Environment.NewLine);
        SYSTEM_INFO sys_info = new SYSTEM_INFO();
        GetSystemInfo(out sys_info);

        UIntPtr proc_min_address = sys_info.minimumApplicationAddress;
        UIntPtr proc_max_address = sys_info.maximumApplicationAddress;

        // saving the values as long ints so I won't have to do a lot of casts later
        Int64 proc_min_address_l = (Int64)proc_min_address; //(Int64)procs.MainModule.BaseAddress;
        Int64 proc_max_address_l = (Int64)theProc.VirtualMemorySize64 + proc_min_address_l;

        //int arrLength = 0;
        if (File.Exists(file))
            File.Delete(file);

        MEMORY_BASIC_INFORMATION memInfo = new MEMORY_BASIC_INFORMATION();
        while (proc_min_address_l < proc_max_address_l)
        {
            VirtualQueryEx(pHandle, proc_min_address, out memInfo);
            byte[] buffer = new byte[(Int64)memInfo.RegionSize];
            UIntPtr test = (UIntPtr)((Int64)memInfo.RegionSize);
            UIntPtr test2 = (UIntPtr)((Int64)memInfo.BaseAddress);

            ReadProcessMemory(pHandle, test2, buffer, test, IntPtr.Zero);

            AppendAllBytes(file, buffer); //due to memory limits, we have to dump it then store it in an array.
            //arrLength += buffer.Length;

            proc_min_address_l += (Int64)memInfo.RegionSize;
            proc_min_address = new UIntPtr((ulong)proc_min_address_l);
        }

        Debug.Write("[DEBUG] memory dump completed. Saving dump file to " + file + ". (" + DateTime.Now.ToString("h:mm:ss tt") + ")" + Environment.NewLine);
        return true;
    }

    /// 
    /// Array of byte scan.
    /// 
    /// array of bytes to search for, OR your ini code label.
    /// Include writable addresses in scan
    /// Include executable addresses in scan
    /// ini file (OPTIONAL)
    /// IEnumerable of all addresses found.
    public Task> AoBScan(string search, bool writable = false, bool executable = true, string file = "")
    {
        return AoBScan(0, long.MaxValue, search, writable, executable, file);
    }

    /// 
    /// Array of byte scan.
    /// 
    /// array of bytes to search for, OR your ini code label.
    /// Include readable addresses in scan
    /// Include writable addresses in scan
    /// Include executable addresses in scan
    /// ini file (OPTIONAL)
    /// IEnumerable of all addresses found.
    public Task> AoBScan(string search, bool readable, bool writable, bool executable, string file = "")
    {
        return AoBScan(0, long.MaxValue, search, readable, writable, executable, file);
    }

    /// 
    /// Array of Byte scan.
    /// 
    /// Your starting address.
    /// ending address
    /// array of bytes to search for, OR your ini code label.
    /// ini file (OPTIONAL)
    /// Include writable addresses in scan
    /// Include executable addresses in scan
    /// IEnumerable of all addresses found.
    public Task> AoBScan(long start, long end, string search, bool writable = false, bool executable = true, string file = "")
    {
        // Not including read only memory was scan behavior prior.
        return AoBScan(start, end, search, false, writable, executable, file);
    }

    /// 
    /// Array of Byte scan.
    /// 
    /// Your starting address.
    /// ending address
    /// array of bytes to search for, OR your ini code label.
    /// ini file (OPTIONAL)
    /// Include readable addresses in scan
    /// Include writable addresses in scan
    /// Include executable addresses in scan
    /// IEnumerable of all addresses found.
    public Task> AoBScan(long start, long end, string search, bool readable, bool writable, bool executable, string file = "")
    {
        return Task.Run(() =>
        {
            var memRegionList = new List();

            string memCode = LoadCode(search, file);

            string[] stringByteArray = memCode.Split(' ');

            byte[] aobPattern = new byte[stringByteArray.Length];
            byte[] mask = new byte[stringByteArray.Length];

            for (var i = 0; i < stringByteArray.Length; i++)
            {
                string ba = stringByteArray[i];

                if (ba == "??" || (ba.Length == 1 && ba == "?"))
                {
                    mask[i] = 0x00;
                    stringByteArray[i] = "0x00";
                }
                else if (Char.IsLetterOrDigit(ba[0]) && ba[1] == '?')
                {
                    mask[i] = 0xF0;
                    stringByteArray[i] = ba[0] + "0";
                }
                else if (Char.IsLetterOrDigit(ba[1]) && ba[0] == '?')
                {
                    mask[i] = 0x0F;
                    stringByteArray[i] = "0" + ba[1];
                }
                else
                    mask[i] = 0xFF;
            }

            for (int i = 0; i < stringByteArray.Length; i++)
                aobPattern[i] = (byte)(Convert.ToByte(stringByteArray[i], 16) & mask[i]);

            SYSTEM_INFO sys_info = new SYSTEM_INFO();
            GetSystemInfo(out sys_info);

            UIntPtr proc_min_address = sys_info.minimumApplicationAddress;
            UIntPtr proc_max_address = sys_info.maximumApplicationAddress;

            if (start < (long)proc_min_address.ToUInt64())
                start = (long)proc_min_address.ToUInt64();

            if (end > (long)proc_max_address.ToUInt64())
                end = (long)proc_max_address.ToUInt64();

            Debug.WriteLine("[DEBUG] memory scan starting... (start:0x" + start.ToString(MSize()) + " end:0x" + end.ToString(MSize()) + " time:" + DateTime.Now.ToString("h:mm:ss tt") + ")");
            UIntPtr currentBaseAddress = new UIntPtr((ulong)start);

            MEMORY_BASIC_INFORMATION memInfo = new MEMORY_BASIC_INFORMATION();

            //Debug.WriteLine("[DEBUG] start:0x" + start.ToString("X8") + " curBase:0x" + currentBaseAddress.ToUInt64().ToString("X8") + " end:0x" + end.ToString("X8") + " size:0x" + memInfo.RegionSize.ToString("X8") + " vAloc:" + VirtualQueryEx(pHandle, currentBaseAddress, out memInfo).ToUInt64().ToString());

            while (VirtualQueryEx(pHandle, currentBaseAddress, out memInfo).ToUInt64() != 0 &&
                   currentBaseAddress.ToUInt64() < (ulong)end &&
                   currentBaseAddress.ToUInt64() + (ulong)memInfo.RegionSize >
                   currentBaseAddress.ToUInt64())
            {
                bool isValid = memInfo.State == MEM_COMMIT;
                isValid &= memInfo.BaseAddress.ToUInt64() < (ulong)proc_max_address.ToUInt64();
                isValid &= ((memInfo.Protect & PAGE_GUARD) == 0);
                isValid &= ((memInfo.Protect & PAGE_NOACCESS) == 0);
                isValid &= (memInfo.Type == MEM_PRIVATE) || (memInfo.Type == MEM_IMAGE);

                if (isValid)
                {
                    bool isReadable = (memInfo.Protect & PAGE_READONLY) > 0;

                    bool isWritable = ((memInfo.Protect & PAGE_READWRITE) > 0) ||
                                      ((memInfo.Protect & PAGE_WRITECOPY) > 0) ||
                                      ((memInfo.Protect & PAGE_EXECUTE_READWRITE) > 0) ||
                                      ((memInfo.Protect & PAGE_EXECUTE_WRITECOPY) > 0);

                    bool isExecutable = ((memInfo.Protect & PAGE_EXECUTE) > 0) ||
                                        ((memInfo.Protect & PAGE_EXECUTE_READ) > 0) ||
                                        ((memInfo.Protect & PAGE_EXECUTE_READWRITE) > 0) ||
                                        ((memInfo.Protect & PAGE_EXECUTE_WRITECOPY) > 0);

                    isReadable &= readable;
                    isWritable &= writable;
                    isExecutable &= executable;

                    isValid &= isReadable || isWritable || isExecutable;
                }

                if (!isValid)
                {
                    currentBaseAddress = new UIntPtr(memInfo.BaseAddress.ToUInt64() + (ulong)memInfo.RegionSize);
                    continue;
                }

                MemoryRegionResult memRegion = new MemoryRegionResult
                {
                    CurrentBaseAddress = currentBaseAddress,
                    RegionSize = memInfo.RegionSize,
                    RegionBase = memInfo.BaseAddress
                };

                currentBaseAddress = new UIntPtr(memInfo.BaseAddress.ToUInt64() + (ulong)memInfo.RegionSize);

                //Console.WriteLine("SCAN start:" + memRegion.RegionBase.ToString() + " end:" + currentBaseAddress.ToString());

                if (memRegionList.Count > 0)
                {
                    var previousRegion = memRegionList[memRegionList.Count - 1];

                    if ((long)previousRegion.RegionBase + previousRegion.RegionSize == (long)memInfo.BaseAddress)
                    {
                        memRegionList[memRegionList.Count - 1] = new MemoryRegionResult
                        {
                            CurrentBaseAddress = previousRegion.CurrentBaseAddress,
                            RegionBase = previousRegion.RegionBase,
                            RegionSize = previousRegion.RegionSize + memInfo.RegionSize
                        };

                        continue;
                    }
                }

                memRegionList.Add(memRegion);
            }

            ConcurrentBag bagResult = new ConcurrentBag();

            Parallel.ForEach(memRegionList,
                             (item, parallelLoopState, index) =>
                             {
                                 long[] compareResults = CompareScan(item, aobPattern, mask);

                                 foreach (long result in compareResults)
                                     bagResult.Add(result);
                             });

            Debug.WriteLine("[DEBUG] memory scan completed. (time:" + DateTime.Now.ToString("h:mm:ss tt") + ")");

            return bagResult.ToList().OrderBy(c => c).AsEnumerable();
        });
    }

    /// 
    /// Array of bytes scan
    /// 
    /// Starting address or ini label
    /// ending address
    /// array of bytes to search for or your ini code label
    /// ini file
    /// First address found
    public async Task AoBScan(string code, long end, string search, string file ="")
    {
        long start = (long)GetCode(code, file).ToUInt64();

        return  (await AoBScan(start, end, search, true, true, true, file)).FirstOrDefault();
    }

    private long[] CompareScan(MemoryRegionResult item, byte[] aobPattern, byte[] mask)
    {
        if (mask.Length != aobPattern.Length)
            throw new ArgumentException($"{nameof(aobPattern)}.Length != {nameof(mask)}.Length");

        IntPtr buffer = Marshal.AllocHGlobal((int)item.RegionSize);

        ReadProcessMemory(pHandle, item.CurrentBaseAddress, buffer, (UIntPtr)item.RegionSize, out ulong bytesRead);

        int result = 0 - aobPattern.Length;
        List ret = new List();
        unsafe
        {
            do
            {

                result = FindPattern((byte*)buffer.ToPointer(), (int)bytesRead, aobPattern, mask, result + aobPattern.Length);

                if (result >= 0)
                    ret.Add((long) item.CurrentBaseAddress + result);

            } while (result != -1);
        }

        Marshal.FreeHGlobal(buffer);

        return ret.ToArray();
    }

    private int FindPattern(byte[] body, byte[] pattern, byte[] masks, int start = 0)
    {
        int foundIndex = -1;

        if (body.Length <= 0 || pattern.Length <= 0 || start > body.Length - pattern.Length ||
            pattern.Length > body.Length) return foundIndex;

        for (int index = start; index <= body.Length - pattern.Length; index++)
        {
            if (((body[index] & masks[0]) == (pattern[0] & masks[0])))
            {
                var match = true;
                for (int index2 = 1; index2 <= pattern.Length - 1; index2++)
                {
                    if ((body[index + index2] & masks[index2]) == (pattern[index2] & masks[index2])) continue;
                    match = false;
                    break;

                }

                if (!match) continue;

                foundIndex = index;
                break;
            }
        }

        return foundIndex;
    }

    private unsafe int FindPattern(byte* body, int bodyLength, byte[] pattern, byte[] masks, int start = 0)
    {
        int foundIndex = -1;

        if (bodyLength <= 0 || pattern.Length <= 0 || start > bodyLength - pattern.Length ||
            pattern.Length > bodyLength) return foundIndex;

        for (int index = start; index <= bodyLength - pattern.Length; index++)
        {
            if (((body[index] & masks[0]) == (pattern[0] & masks[0])))
            {
                var match = true;
                for (int index2 = 1; index2 <= pattern.Length - 1; index2++)
                {
                    if ((body[index + index2] & masks[index2]) == (pattern[index2] & masks[index2])) continue;
                    match = false;
                    break;

                }

                if (!match) continue;

                foundIndex = index;
                break;
            }
        }

        return foundIndex;
    }

#endif
}


2. 鼠标操作

public static partial class WindowMouse
{
//当前光标的句柄
[DllImport("User32.dll", EntryPoint = "GetCursor")]
private static extern IntPtr GetCursor();

    //按键,鼠标移动和鼠标点击。
    [DllImport("User32.dll", EntryPoint = "SendInput", SetLastError = true)]
    private static extern int _SendInput(int nInputs, MKH_Input[] pInputs, int cbSize);

    //获取鼠标的位置
    [DllImport("User32", EntryPoint = "GetCursorPos", SetLastError = true)]
    public extern static bool GetCursorPos(out M_CursorPos lpPoint);

    //设置鼠标的位置
    [DllImport("User32", EntryPoint = "SetCursorPos", SetLastError = true)]
    public extern static bool SetCursorPOS(int x, int y);

    //销毁一个光标并释放它占用的任何内存,不要使用该函数去消毁一个共享光标
    [DllImport("User32", EntryPoint = "DestroyCUrsor", SetLastError = true)]
    public extern static bool DestroyCUrsor(IntPtr hCursor);

    //显示或隐藏光标。

    [DllImport("User32.dll", EntryPoint = "ShowCursor")]
    public extern static int ShowCursor(bool bShow);
}
3  窗体操作

public partial class WindowForm
{
internal delegate bool EnumChildWindowsCallBack(IntPtr hwnd, int lParam);

    //关闭窗体
    [DllImport("User32.dll", EntryPoint = "CloseWindow", SetLastError = true)]
    internal static extern bool _CloseWindow(IntPtr hWnd);

    //获得给定窗口的可视状态
    [DllImport("User32.dll", EntryPoint = "IsWindowVisible")]
    internal static extern bool _IsWindowVisible(IntPtr hWnd);

    //移动窗体
    [DllImport("User32.dll", EntryPoint = "MoveWindow", SetLastError = true)]
    internal static extern bool _MoveWindow(IntPtr hWnd, int left, int top, int nWidth, int nHeight, bool BRePaint);

    //激活前台窗体
    [DllImport("User32.dll", EntryPoint = "SetForegroundWindow")]
    internal static extern bool _SetForegroundWindow(IntPtr hwnd);

    //设置指定窗口的显示状态。
    [DllImport("User32.dll", EntryPoint = "ShowWindow")]
    internal static extern bool _ShowWindow(IntPtr hWnd, int nCmdShow);

    //指出一个被属窗口,可见窗口,顶级弹出窗口,或层叠窗日是否在屏幕上存在。这个函数搜索整个屏幕,而不仅仅搜索应用程序的客户区。
    [DllImport("User32.dll", EntryPoint = "AnyPopup")]
    internal static extern bool AnyPopup();

    [DllImport("user32.dll")]
    internal static extern int EnumChildWindows(IntPtr hWndParent, EnumChildWindowsCallBack lpfn, int lParam);

    //取窗口句柄 FindWindow 不查询子窗体 不区分大小写
    [DllImport("User32.dll", EntryPoint = "FindWindow", SetLastError = true)]
    internal extern static IntPtr FindWindow(string lpClassName, string lpWindowName);

    //检索其类名和窗口名称与指定字符串匹配的窗口的句柄。该函数搜索子窗口
    [DllImport("User32.dll", EntryPoint = "FindWindowEx", SetLastError = true)]
    internal static extern IntPtr FindWindowEx(IntPtr hwndParent, IntPtr hwndChildAfter, string lpClassName, string lpWindowName);

    //检索指定窗口所属的类的名称。
    [DllImport("User32.dll", EntryPoint = "GetClassName", SetLastError = true)]
    internal static extern IntPtr GetClassName(IntPtr hwnd, string lpClassName, int length);

    //返回桌面窗口的句柄
    [DllImport("User32.dll", EntryPoint = "GetDesktopWindow")]
    internal static extern IntPtr GetDesktopWindow();

    //前台窗体
    [DllImport("User32.dll", EntryPoint = "GetForegroundWindow")]
    internal static extern IntPtr GetForegroundWindow();

    [DllImport("User32.dll", EntryPoint = "GetNextWindow", SetLastError = true)]
    internal static extern IntPtr GetNextWindow(IntPtr hwnd, int wcmd);

    [DllImport("User32.dll", EntryPoint = "GetParent", SetLastError = true)]
    internal static extern IntPtr GetParent(IntPtr hwnd);

    //获取窗体中 z序子窗体顶部
    [DllImport("User32.dll", EntryPoint = "GetTopWindow", SetLastError = true)]
    internal static extern IntPtr GetTopWindow(IntPtr hwnd);

    //指定窗口的显示状态和还原的,最小化的和最大化的位置。
    [DllImport("User32.dll", EntryPoint = "GetWindowPlacement", SetLastError = true)]
    internal static extern bool GetWindowPlacement(IntPtr hwnd, out W_PLACEMENT wp);

    //该函数返回指定窗口的边框矩形的尺寸。该尺寸以相对于屏幕坐标左上角的屏幕坐标给出
    [DllImport("user32.dll", EntryPoint = "GetWindowRect", SetLastError = true)]
    internal static extern bool GetWindowRect(IntPtr hwnd, out W_RECT lpRect);

    //指定窗口的标题条文本
    [DllImport("User32.dll", EntryPoint = "GetWindowText", SetLastError = true, CharSet = CharSet.Unicode)]
    internal static extern bool GetWindowText(IntPtr hwnd, byte[] data, int length);

    //获取标题条文本长度
    [DllImport("User32.dll", EntryPoint = "GetWindowTextLength", SetLastError = true)]
    internal static extern int GetWindowTextLength(IntPtr hWnd);

    //判断是否是子窗口
    [DllImport("User32.dll", EntryPoint = "IsChild")]
    internal static extern bool IsChild(IntPtr hWndParant, IntPtr hWnd);

    //窗体最小化
    [DllImport("User32.dll", EntryPoint = "IsIconic")]
    internal static extern bool IsIconic(IntPtr hWnd);

    //判断是否是一个窗体
    [DllImport("User32.dll", EntryPoint = "IsWindow")]
    internal static extern bool isWindow(IntPtr hWnd);

    //指定的窗口是否是本地Unicode窗口。
    [DllImport("User32.dll", EntryPoint = "IsWindowUnicode")]
    internal static extern bool IsWindowUnicode(IntPtr hWnd);

    [DllImport("User32.dll", EntryPoint = "IsZoomed")]
    internal static extern bool IsZoomed(IntPtr hWnd);

    //最小化(标志性)窗口恢复到其先前的大小和位置;然后激活窗口。
    [DllImport("User32.dll", EntryPoint = "OpenIcon", SetLastError = true)]
    internal static extern bool OpenIcon(IntPtr hWnd);

    //指定窗口的显示状态和还原的,最小化的和最大化的位置。
    [DllImport("User32.dll", EntryPoint = "SetWindowPlacement", SetLastError = true)]
    internal static extern bool SetWindowPlacement(IntPtr hwnd, out W_PLACEMENT wp);

    [DllImport("user32.dll")]
    internal static extern IntPtr SetWindowPos(IntPtr hWnd, int hWndInsertAfter, int x, int y, int cX, int cY, int wFlags);

    //设置由不同线程产生的窗口的显示状态。 避免在等待一个挂起的应用程序完成处理show-window事件时也被挂起。

    [DllImport("User32.dll", EntryPoint = "ShowWindowAsync")]
    internal static extern bool ShowWindowAsync(IntPtr hWnd, int nCmdShow);

    //从指定的坐标获取窗体
    [DllImport("User32.dll", EntryPoint = "WindowFromPoint")]
    internal static extern IntPtr WindowFromPoint(Point point);
    //定的消息发送到窗口或窗口
    [DllImport("User32.dll", EntryPoint = "SendMessage",SetLastError =true)]
    internal static extern int _SendMessage(int hWnd,  int Msg, int wParam,  int lParam  );
}


参考 https://docs.microsoft.com/zh-cn/windows/win32/apiindex/windows-api-list
demo https://aescr.coding.net/p/window/git