Начнём по порядку.
1)Класс с подключением библиотек win32
Код:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;
using System.Diagnostics;
namespace Win32Lib
{
public static class Win32
{
public const int WM_SETCURSOR = 0x020;
public const int WM_COMMAND = 0x111;
public const int WM_KEYDOWN = 0x100;
public const int WM_KEYUP = 0x101;
public const int WM_CHAR = 0x102;
public const int WM_MOUSEMOVE = 0x200;
public const int WM_LBUTTONDOWN = 0x201;
public const int WM_LBUTTONUP = 0x202;
public const int WM_LBUTTONDBLCLK = 0x203;
public const int GWL_EXSTYLE = -20;
public const int WS_EX_TOOLWINDOW = 0x00000080;
public const int WS_EX_APPWINDOW = 0x00040000;
public delegate bool EnumThreadDelegate(IntPtr hWnd, IntPtr lParam);
[DllImport("User32.dll")]
public static extern IntPtr FindWindow(string strClassName, string strWindowName);
[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]
public static extern IntPtr FindWindowEx(int parentHandle, IntPtr childAfter, string lclassName, string windowTitle);
[DllImport("user32.dll")]
public static extern bool PostMessage(IntPtr hWnd, uint Msg, int wParam, int lParam);
[DllImport("User32.dll")]
public static extern Int32 SendMessage(IntPtr hWnd, uint Msg, int wParam, Int64 lParam);
[DllImport("user32.dll")]
public static extern int SetWindowLong(IntPtr window, int index, int
value);
[DllImport("user32.dll")]
public static extern int GetWindowLong(IntPtr window, int index);
[DllImport("user32.dll")]
public static extern byte VkKeyScan(char ch);
[DllImport("user32.dll", SetLastError = true)]
public static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint lpdwProcessId);
[DllImport("user32.dll")]
public static extern bool ShowWindow(IntPtr hwnd, int nCmdShow);
[DllImport("kernel32.dll")]
public static extern int OpenProcess(ProcessAccessFlags dwDesiredAccess, bool bInheritHandle, int dwProcessId);
[DllImport("kernel32.dll")]
public static extern bool WriteProcessMemory(int hProcess, uint lpBaseAddress, byte[] buffer, int size, int lpNumberOfBytesWritten);
[DllImport("kernel32.dll")]
public static extern bool ReadProcessMemory(int hProcess, uint lpBaseAddress, byte[] buffer, int size, int lpNumberOfBytesRead);
[DllImport("kernel32.dll")]
public static extern Int32 CloseHandle(int hProcess);
[DllImport("user32.dll")]
public static extern bool EnumThreadWindows(int dwThreadId, EnumThreadDelegate lpfn, IntPtr lParam);
public enum ProcessAccessFlags : uint
{
All = 0x001F0FFF,
Terminate = 0x00000001,
CreateThread = 0x00000002,
VMOperation = 0x00000008,
VMRead = 0x00000010,
VMWrite = 0x00000020,
DupHandle = 0x00000040,
SetInformation = 0x00000200,
QueryInformation = 0x00000400,
Synchronize = 0x00100000
}
}
public static class ShowMode
{
public const int SW_FORCEMINIMIZE = 11;
public const int SW_HIDE = 0;
public const int SW_MAXIMIZE = 3;
public const int SW_MINIMIZE = 6;
public const int SW_RESTORE = 9;
public const int SW_SHOW = 5;
public const int SW_SHOWDEFAULT = 10;
public const int SW_SHOWMAXIMIZED = 3;
public const int SW_SHOWMINIMIZED = 2;
public const int SW_SHOWMINNOACTIVE = 7;
public const int SW_SHOWNA = 8;
public const int SW_SHOWNOACTIVATE = 4;
public const int SW_SHOWNORMAL = 1;
}
}
2.Класс, который я использую для чтения из памяти
Код:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Win32Lib;
using System.ComponentModel;
namespace MyMagic
{
class WhiteMagic
{
int handler;
public WhiteMagic(int processId)
{
handler = Win32.OpenProcess(Win32.ProcessAccessFlags.All, false, processId);
}
public uint GetAddressByOffsets(uint[] offsets)
{
uint ret=offsets[0];
for(int i=1;i<offsets.Length;i++)
{
ret = ReadUint(ret);
ret += offsets[i];
}
return ret;
}
public uint ReadUint(uint offset)
{
byte[] buff = new byte[4];
Win32.ReadProcessMemory(handler, offset, buff, 4, 0);
uint ret=BitConverter.ToUInt32(buff, 0);
return ret;
}
public void Close()
{
Win32.CloseHandle(handler);
}
}
}
P.S.Пробовал открывать с правами только на чтение - не помогает.
Функция GetAddressByOffsets использую для высчитывания адресов флагов и параметров при старте бота один раз.
3.Пример обращения (привожу не весь код - там 500 с лишним строк - только то что выполняется в таймерах)
Код:
private void checkOffsets(object sender, ElapsedEventArgs e)
{
if(gather_existance.Interval>1000)
{
gather_existance.Interval = rnd.Next(80, 120);
}
if(tera!=null && checkGatherFlag() && currentState==gatherStates.LOOKING_FOR_RESOURCE)
{
setStatus(gatherStates.WAITING_FOR_GATHER);
log_form.logText("Ресурс найден");
//this.Invoke((MethodInvoker)delegate() { log.AppendText("Find ore/plant" + Environment.NewLine); });
gather_existance.Enabled = false;
if (tera.ReadUint(ClientAddresses.IS_GATHERING) == 0)
{
send_one();
}
check_start_gather.Interval = rnd.Next(30,70);
check_start_gather.Elapsed += checkStartGather;
check_start_gather.Enabled = true;
}
}
private Boolean checkGatherFlag()
{
//Проверка флага наличия в таргете руды. Оффсетов было много и с разными значениями. Для этого при 0 - нет в таргете руды, при 1 - руда трава и т.п.
if (tera!=null && tera.ReadUint(ClientAddresses.TARGET_GATHER_FLAG) == 1)
{
return true;
}
else
{
return false;
}
}
private void checkFinishGather(object sender, ElapsedEventArgs e)
{
Win32.ShowWindow(handler, ShowMode.SW_SHOWNOACTIVATE);
if (tera != null && tera.ReadUint(ClientAddresses.IS_GATHERING) == 0 && currentState==gatherStates.GATHERING)
{
log_form.logText("Сбор завершён");
//this.Invoke((MethodInvoker)delegate() { log.AppendText("Finish gathering" + Environment.NewLine); });
finish_gathering.Enabled = false;
gather_existance.Enabled = true;
check_start_gather.Enabled = false;
setStatus(gatherStates.LOOKING_FOR_RESOURCE);
uint wp = tera.ReadUint(ClientAddresses.WORK_POINTS);
redrawProgressBar((int) wp);
if (wp < 10 && stopZeroWork.Checked)
{
this.Invoke((MethodInvoker)delegate() {
button1_Click(null, null);
});
log_form.logText("Закончились очки работы");
}
}
}
private void checkStartGather(object sender, ElapsedEventArgs e)
{
if (tera != null && tera.ReadUint(ClientAddresses.IS_GATHERING) == 0 && tera.ReadUint(ClientAddresses.TARGET_GATHER_FLAG) == 1 && currentState == gatherStates.WAITING_FOR_GATHER)
{
send_one();
}
else if(tera!=null && tera.ReadUint(ClientAddresses.TARGET_GATHER_FLAG)==0 && currentState==gatherStates.WAITING_FOR_GATHER)
{
TimeSpan delta = DateTime.Now - last_change_status;
if (delta.TotalMilliseconds > 3000)
{
log_form.logText("Кто-то скрысил ресурс");
//this.Invoke((MethodInvoker)delegate() { log.AppendText("Someone start gather (not me)" + Environment.NewLine); });
}
else
{
gather_existance.Interval = 20000;
//log_form.logText("tst");
//this.Invoke((MethodInvoker)delegate() { log.Lines = log.Lines.Reverse().Skip(2).Reverse().ToArray(); log.AppendText(Environment.NewLine); });
}
check_start_gather.Enabled = false;
gather_existance.Enabled = true;
setStatus(gatherStates.LOOKING_FOR_RESOURCE);
}
else if(tera!=null && tera.ReadUint(ClientAddresses.TARGET_GATHER_FLAG)==1 && tera.ReadUint(ClientAddresses.IS_GATHERING) == 1 && currentState==gatherStates.WAITING_FOR_GATHER)
{
setStatus(gatherStates.GATHERING);
log_form.logText("Начат сбор");
//this.Invoke((MethodInvoker)delegate() { log.AppendText("Start gathering" + Environment.NewLine); });
check_start_gather.Enabled = false;
finish_gathering.Interval = rnd.Next(30,70);
finish_gathering.Elapsed += checkFinishGather;
finish_gathering.Enabled = true;
}
}
Комментарии по этому куску.
checkOffsets - выполняется по таймеру раз в 50-100мс. Проверяет наличие в таргете ресурса. То что для установки использую рандом - вчера тестировал - думал, что возможна какая-то кореляция между таймерами и поэтому всё подвисает.
Как только боту в таргет попадает ресурс он останавливает этот таймер и вызывает таймер проверки начала сбора т.е. я шлю нажатие кнопки сбора до тех пор пока не появится флаг того, что я собираю ресурс.(checkStartGather)
После того как я получил подтверждение старта сбора (флаги сбора и нахождение в таргете ресурса) таймер проверки начала сбора останавливается и запускается таймер проверки окончания сбора.
(Про интервал в 20000 после окончания сбора руда несколько секунд ещё таргете. По этому моменту я выставляю таймер на проверку сбора в 20с(вчера тестил с этой штукой, но бот всё равно зависал так что это не принципиально)
Таймер окончания сбора ожидает, когда флаг сбора станет равным нулю и запускает таймер появления ресурса