1 (изменено: Alectric, 2020-07-06 13:14:55)

Тема: AHK: COM Port terminal

Программа для работы с COM-портом.
Возможности:
- автоматический поиск порта;
- чтение данных с порта;
- всевозможные типы представления данных (бинарные данные, символ, шестнадцатеричное, десятичное, 1 2 4 8 байта, значение с точкой);
- фильтрация данных (правой кнопкой по таблице);
- построение графика по выбранному типу данных онлайн (правой кнопкой мыши на нужное число в таблице, программа ориентируется по смещению от первого принятого байта пакета, данные не отрисовываются если контрольная сумма не совпадает (если выбрана контрольная сумма));
- отправка данных в порт;
- добавление контрольной суммы к данным (1 байт CRC, 2 байта CRC, Modbus RTU);
- расшифровка модбас сообщений (при условии что данные приняты корректно);
- построение графиков на основе данных из порта онлайн;
- до 12-ти графиков с возможностью отключать ненужные;
- свободный формат разделителя (например: "123,456,789;" или "123I456I789]" - 1 точка 3х графиков);
- настройки нигде не сохраняются.

Возможно когда-нибудь добавятся функции:
- сохранять графики в файл в виде данных либо в виде изображения.

Недостатки:
- нет возможности распаралелить обработку данных и обработку порта
из-за этого нет возможности корректно обрабатывать большие пакеты данных и данные идущие с большой частотой;
- возможны проблемы с масштабированием окна.

Тема для обсуждения.

Version:="2.2"
#SingleInstance,Force

#NoEnv
;ListLines,Off
#KeyHistory,0
AutoTrim,Off
CoordMode,Mouse,Screen
CoordMode,ToolTip
CoordMode,Pixel
CoordMode,Caret,Screen
DetectHiddenWindows,On
setformat,float,0.3
Menu,Tray,UseErrorLevel
SendMode,Input
SetBatchLines,-1
SetControlDelay,0
SetKeyDelay,0
SetTitleMatchMode,2
SetWinDelay,20
SetWorkingDir,%A_ScriptDir%
StringCaseSense,Off
Menu,Tray,NoIcon
ComObjError(false)
OnExit,EXIT
Gdip_Initialised:=Gdip_Startup()
Meta:="\.*?+[{|()^$"

OnMessage(WM_MOVING:=0x0216, "WM_MOVING")
;OnMessage(WM_MOUSEMOVE:=0x200, "WM_MOUSEMOVE")
;IDC_SIZENS := DllCall("User32.dll\LoadCursor", "Ptr", NULL, "Int", 32645, "UPtr")
IDC_SIZEWE := DllCall("User32.dll\LoadCursor", "Ptr", NULL, "Int", 32644, "UPtr")
IDC_SIZEALL := DllCall("User32.dll\LoadCursor", "Ptr", NULL, "Int", 32646, "UPtr")

global COMFail

RS232_Bytes_Reqest=16
Rownums=10000
if (a_osversion~="10")
  add_size=65
else
  add_size=55
E_Text_h=560
MoveOut=800
Packages_Recieved:=0

Gdip_Points_Num:=2000
Gdip_W:=358
Gdip_H:=358
Gdip_M:=Gdip_H//2
Gdip_Midle:=Gdip_H//2
Gdip_ZoomX:=1
Gdip_ZoomY:=1
Gdip_X_Offset:=0 ;Gdip_Y_Offset:=Gdip_M
ColourPen1:=0x0000ff
ColourPen2:=0xff0000
ColourPen3:=0x00ff00
ColourPen4:=0x00ffff
ColourPen5:=0xff00ff
ColourPen6:=0xcccc00
ColourPen7:=0x808080
ColourPen8:=0x0000c0
ColourPen9:=0xc00000
ColourPen10:=0x00c000
ColourPen11:=0x0000ff
ColourPen12:=0xff0000
Monitor_value:={}
MaxNumOfGraf:=12
loop,% MaxNumOfGraf
  Monitor_value%a_index%:={}
Monitor_value_T:={}
GrafSmooth:=1
AutoMinMax:=1
AutoEnding:=1

ICON_CRC_ERROR:=132
ICON_EMPTY:=0
ICON_ERROR:=78
ICON_INFO:=222
ICON_REGISTER:=13 ;268
ICON_START:=138
ICON_V:=145
ICON_X:=272

RTUFunc1:="Read Coil Status (Чтение DO)"
RTUFunc2:="Read Input Status (Чтение DI)"
RTUFunc3:="Read Holding Reg. (Чтение AO)"
RTUFunc4:="Read Input Register (Чтение AI)"
RTUFunc5:="Force Single Coil (Запись DO)"
RTUFunc6:="Preset Single Reg. (Запись AO)"
RTUFunc15:="Force Mult. Coils (Запись неск. DO)"
RTUFunc16:="Force Mult. Reg. (Запись неск. AO)"
RTU_Command_List:= RTUFunc1 "|" RTUFunc2 "|" RTUFunc3 "|" RTUFunc4 "|" RTUFunc5 "|" RTUFunc6 "|" RTUFunc15 "|" RTUFunc16 ;

RTUF1:="DO"
RTUF2:="DI"
RTUF3:="AO"
RTUF4:="AI"
RTUF5:="1 DO"
RTUF6:="1 AO"
RTUF15:="# DO"
RTUF16:="# AO"

RTUErr1:="У слейва нет такой команды."
RTUErr2:="Регистр недоступен."
RTUErr3:="Недопустимая величина значения."
RTUErr4:="Ошибка во время выполнения действия."
RTUErr5:="Обрабатываю запрос, ждите..."
RTUErr6:="Обрабатываю команду, ждите..."
RTUErr7:="Не могу выполнить программную функцию. Запросите диагностические данные."
RTUErr8:="Ошибка паритета. Повторите запрос или обратитесь в сервис."
RTUErr10:="Шлюз не правильно настроен или перегружен."
RTUErr11:="Слейв устройства нет в сети или от него нет ответа."

menu,ListRightClick,add,Очистить,update
menu,ListRightClick,add,Фильтровать данные,DataFilter
menu,ListRightClick,add
menu,ListRightClick,add,Копировать,ListCopySelected
menu,ListRightClick,add,Отрисовать график,GrafDrawFromSelected
menu,ListRightClick,disable,Отрисовать график ;

GuiCreate:
Gui,1:+hwndhMainWin ; -DPIScale ; +alwaysontop
Gui,1:+MinimizeBox +maximizebox +Resize ; +E0x80000
Gui,1:Margin,10,10
Gui,1:font,s8,Tahoma
Gui,1:Add,StatusBar,+hwndhStatusBar,Готов

; Настройки COM-порта ;
Gui,1:Add,GroupBox,xm ym w210 h175,Настройки COM-порта ;
Gui,1:Add,Radio,Checked vChoiceCOM xp+10 yp+20 w130,USB-COM устройство ;
Gui,1:Add,Radio,+hwndhCOMNo xp y+10 w106,COM-порт №:
Gui,1:Add,Radio,+hwndhCOMName xp y+3 w106,Искать по имени:
Gui,1:Add,Edit,xp+110 yp-20 w50 vManualCOM gChoseCOMNo Number Limit3,1
Gui,1:Add,UpDown,Range1-30
Gui,1:Add,Edit,xp y+3 w80 vManualCOMByName gChoseCOMName,CH340
Gui,1:add,Button,xp-110 y+10 w90 gAddSettings,Дополнительно ;
Gui,1:add,Button,x+10 yp w90 gAceptSettings,Применить ;
Gui,1:add,text,xp-98 y+6 w187 h40 v_text_ gDiskonnectCOM

; Отправить данные в порт ;
Gui,1:Add,GroupBox,xm y+15 w210 h484,Отправить данные в порт ;
Gui,1:Add,Edit,xp+10 yp+22 w190 h300 vDataToSend gDataToSendCheck +hwndE_1,
Gui,1:add,text,+hwndhE_RTU_Addres_1 xm+69 yp,Адрес DEC: ;Адрес DEC:
Gui,1:add,text,+hwndhE_RTU_Addres_2 xp+35 y+8,HEX:
Gui,1:add,Edit,+hwndhE_RTU_Addres_3 xm+130 yp-24 w70 gChangeAddres Number Limit3 vRTU_SlaveAdress,
Gui,1:Add,UpDown,+hwndhE_RTU_Addres_4 Range0-255 Wrap,1
Gui,1:add,Edit,+hwndhE_RTU_Addres_5 xp y+1 w70 gChangeAddres vRTU_SlaveAdressHEX,1

Gui,1:add,text,+hwndhE_RTU_Command_1 xm+10 y+5,Команда: ;Команда:
Gui,1:add,DropDownList,+hwndhE_RTU_Command_2 xm+10 y+5 w190 Choose3 vRTU_Command gSubmit AltSubmit,% RTU_Command_List
Gui,1:add,text,+hwndhE_RTU_StartReg_1 xm+19 y+10,Начальный рег. DEC: ;№ регистра DEC:
Gui,1:add,text,+hwndhE_RTU_StartReg_2 xp+85 y+8,HEX:
Gui,1:add,Edit,+hwndhE_RTU_StartReg_3 xm+130 yp-24 w70 gChangeStartReg Number vRTU_StartReg,
Gui,1:Add,UpDown,+hwndhE_RTU_StartReg_4 Range0-65535 Wrap 0x80,0
Gui,1:add,Edit,+hwndhE_RTU_StartReg_5 xp y+1 w70 gChangeStartReg vRTU_StartRegHEX,0
Gui,1:add,text,+hwndhE_RTU_RegNum_1 xm+10 y+10,Кол-во регистров DEC: ;Кол-во регистров DEC:
Gui,1:add,text,+hwndhE_RTU_RegNum_2 xp+94 y+8,HEX:
Gui,1:add,Edit,+hwndhE_RTU_RegNum_3 xm+130 yp-24 w70 gChangeRegNum Number vRTU_RegNum,
Gui,1:Add,UpDown,+hwndhE_RTU_RegNum_4 Range1-100 Wrap 0x80,1
Gui,1:add,Edit,+hwndhE_RTU_RegNum_5 xp y+1 w70 gChangeRegNum vRTU_RegNumHEX,1
Gui,1:add,text,+hwndhE_RTU_Val_1 xm+10 y+5,Значение: ;Значение:
Gui,1:add,checkbox,+hwndhE_RTU_Val_2 xm+10 y+85 vBeliveMe,Данным верить ;Данным верить:

; GuiControl,Move,% E_1,y470 h75
;loop,5
;  guicontrol,enable,% hE_RTU_RegNum_%a_index%
;loop,2
;  GuiControl,disable,% hE_RTU_Val_%a_index%
;GuiControl,disable,% E_1
 loop,2
   GuiControl,hide,% hE_RTU_Command_%a_index%
 loop,5
   GuiControl,hide,% hE_RTU_StartReg_%a_index%
 loop,5
   GuiControl,hide,% hE_RTU_Addres_%a_index%
 loop,5
   GuiControl,hide,% hE_RTU_RegNum_%a_index%
 loop,3
   GuiControl,hide,% hE_RTU_Val_%a_index%

Gui,1:add,Radio,xm+10 ym+510 gConvertToSYM vChoiceFormat,SYM
Gui,1:add,Radio,x+5 yp gConvertToDEC,DEC
Gui,1:add,Radio,x+5 yp gConvertToHEX Checked,HEX
Gui,1:add,Radio,x+5 yp gConvertToBIN,BIN
PreviousConversion:="hex"
Gui,1:Add,GroupBox,+hwndhEDEC1 xm+10 y+2 w190 h55,DEC ;
Gui,1:add,Radio,+hwndhEDEC2 xm+20 yp+15 gChooseDEC vChoiceDEC,Unsigned
Gui,1:add,Radio,+hwndhEDEC3 x+5 yp gChooseDEC,Signed
Gui,1:add,Radio,+hwndhEDEC4 x+5 yp gChooseDEC,Float
Gui,1:add,Radio,+hwndhEDEC5 xm+20 y+5 gChooseDECSize Group vChoiceDECSize,1
Gui,1:add,Radio,+hwndhEDEC6 x+5 yp gChooseDECSize,2
Gui,1:add,Radio,+hwndhEDEC7 x+5 yp gChooseDECSize,4
Gui,1:add,Radio,+hwndhEDEC8 x+5 yp gChooseDECSize,8   Bytes
 loop,8
   GuiControl,hide,% hEDEC%a_index%

Gui,1:add,text,xm+10 ym+580,Добавить контрольную сумму: ; CheckSumm ;
Gui,1:add,DropDownList,xp y+5 w190 +hwndhCS vAddCS gSubmit Choose1 AltSubmit,Нет|CRC8|CRC16|Modbus RTU ;
Gui,1:add,Button,xp y+10 w120 gButtonSendData,Отправить ;
Gui,1:add,Button,x+ yp w69 gSendDataRepitedly +hwndhSendDataRepitedly,Циклично ;

; Буфер приёма COM-порта ;
Gui,1:Add,GroupBox,xm+220 ym w720 h662 +hwndhGroupBufer,Буфер приёма COM-порта ;
Gui,1:add,text,xp+10 yp+20  w128,Ожидаемое число байт: ;
Gui,1:add,edit,x+5 yp-3 w70 Number Limit3 vRS232_Bytes_Reqest gSubmit2,% RS232_Bytes_Reqest
Gui,1:add,UpDown,Range1-128 gSubmit,% RS232_Bytes_Reqest
Gui,1:Add,checkbox,+hwndhAutoByRe x+5 yp+3 checked vAuto_Bytes_Reqest gSubmit2,Автоматически ;
GuiControl,hide,% hAutoByRe
Gui,1:add,checkbox,xp-209 y+9 vFollowSelect gSubmit,Сохранять выделение таблицы (мониторить значение) ;
Gui,1:add,ListView,xp y+7 w320 h%E_Text_h% +LV0x10000 v_List_ gListGetSelected AltSubmit grid Count%Rownums% NoSortHdr ReadOnly +hwndE_LV,#|№ байта|№ байта или описание|BIN|SYM|HEX|DEC|WORD|INT|DWORD|DINT|Float|Double
LV_SetImageList(ImageListID:=IL_Create(300))
loop,300
  IL_Add(ImageListID,"shell32.dll",a_index)

; Разделитель 1
Gui,1:Add,Progress,x+0 yp w20 h%E_Text_h% vProgress cNavy backgroundSilver Vertical +hwndhProgress

Gui,1:add,checkbox,x+0 ym+20 +hwndE_111 vEN_Kurvendrucker gKurvendrucker,Включить графопостроитель ;
Gui,1:add,checkbox,xp y+10 vAddCRLF gSubmit Checked,Добавить перевод строки после каждого сообщения ;
Gui,1:font,s12,Courier New
tmp1:=E_Text_h-36
Gui,1:add,edit,xp y+5 w360 h%tmp1% HScroll VScroll ReadOnly Multi v_InText_ hwndE_Text
; Разделитель 2
Gui,1:Add,Progress,xp y+0 h20 w360 vProgress2 cSilver backgroundNavy +hwndhProgress2,100
Gui,1:add,edit,% "xp+2 yp+22 w" Gdip_W-4 " h" 12 " +hwndhGrafPos"
;GuiControl,Hide,% hGrafPos

Gui,1:font,s8,Tahoma
Gui,1:add,button,% "xp-340 y+10 w490 gUpdate +hwndhClButton",Очистить ;
Gui,1:add,button,x+10 yp w200 gToTheEnd +hwndhTtEButton,Продолжить прокрутку ;

Gui,1:add,checkbox,xm+230 yp gSubmit2 vGrafPause +hwndhECB0,Остановить прокрутку (F1)

Gui,1:add,text,xm+218 y+1 w1 +hwndhE0, ;
Gui,1:font,s10 w1000,Tahoma
loop,% MaxNumOfGraf
  Gui,1:add,checkbox,% "x+5 yp w50 gSubmit2 vEnableGraf" a_index " checked c" ColourPen%a_index% " +hwndhECB" a_index,% "[" a_index "]"

loop,% MaxNumOfGraf+1
{
  i:=a_index-1
  GuiControl,hide,% hECB%i%
}
GuiControl,hide,% hE0
Gui,1:font,s8,Tahoma
Gui,1:Show,,COM Terminal v%Version%
Gui,1:+MinSize

E%E_LV%=
(
В столбце #: V - Контрольная сумма совпала`; X - не совпала
)

E%E_LV2%=
(
Варианты представления выделенных байтов.
)

E%hProgress%=
(
Шкала заполнения таблицы. При переполнении таблица и текстовое поле очистятся.
)

E%hProgress2%=
(
Позиция в графике. Правый клик - сменить позицию.
)

E%E_1%=
(

)

E%E_111%=
(
Kurvendrucker по немецки.
)

E%hECB12%=
(
Правый клик - отключить лишние графики. Правый клик на отключенный - включить все.
)
E%hECB1%:=E%hECB2%:=E%hECB3%:=E%hECB4%:=E%hECB5%:=E%hECB6%:=E%hECB7%:=E%hECB8%:=E%hECB9%:=E%hECB10%:=E%hECB11%:=E%hECB12%
VarSetCapacity(E_Text_start, 4), VarSetCapacity(E_Text_end, 4)

Gdip_Initialise:
if Gdip_Initialised
{
  E%E_LV2%.=" Нажмите на нужный вариант для отрисовки графика."
  gui,3:+owner1
  Gui,3:+hwndhGraf -Caption +E0x80000 +LastFound +ToolWindow ;+AlwaysOnTop WS_EX_LAYERED := 0x80000
  Gui,3:Show,x0 y0 w1 h1
;  SetParent(hGraf,hMainWin,,0,0)

  Gdip_FontFamilyCreate(Gdip_Font)

  Gdip_hbm2 := CreateDIBSection(A_ScreenWidth,A_ScreenHeight)
  Main_Holst := CreateCompatibleDC()
  Gdip_obm2 := SelectObject(Main_Holst,Gdip_hbm2)
  Main_Holst_G := Gdip_GraphicsFromHDC(Main_Holst)
  Gdip_SetSmoothingMode(Main_Holst_G,4)

  Gdip_Font:="Lucida Console"
  Gdip_Font_Size:="s15"
  Gdip_Font_W:=10
  Gdip_Font_H:=15
  Gdip_Font_Selected_Colour:="ff000000"

  Gdip_WhiteBrush:= Gdip_BrushCreateSolid(0xffffffff) ; белая кисть для заливки
  Gdip_WhiteBrush2:= Gdip_BrushCreateSolid(0xd0ffffff) ; белая кисть для заливки
  Gdip_BlackBrush:= Gdip_BrushCreateSolid(0xff000000) ; черная кисть для заливки
  Gdip_WhitePen := Gdip_CreatePen(0xffffffff,1) ; карандаш для стирания
  Gdip_GrayPen := Gdip_CreatePen(0xff808080,1) ; карандаш для оси
  Gdip_GrayPen2 := Gdip_CreatePen(0xffe0e0e0,1) ; карандаш для оси
  Gdip_GrayPen3 := Gdip_CreatePen(0x11000000,1) ; карандаш для оси
  Gdip_BluePen := Gdip_CreatePen(0xff0000ff,1) ; карандашы для графика
  Gdip_BlackPen := Gdip_CreatePen(0xff000000,2) ; карандашы для графика
  Line_w:=2

  loop,% MaxNumOfGraf
  {
    Gdip_Pen%a_index%:=Gdip_CreatePen(0xff000000 | ColourPen%a_index%,Line_w)
    Gdip_Brush%a_index%:= Gdip_BrushCreateSolid(0x40000000 | ColourPen%a_index%) ; белая кисть для заливки
  }

  Gdip_FillRectangle(Main_Holst_G,Gdip_WhiteBrush,0,0,Gdip_Points_Num,Gdip_H)
}

Gui,1:ListView,% E_LV
LV_ModifyCol(1,65) ;#
LV_ModifyCol(2,0) ;№ байта
LV_ModifyCol(3,45) ;№ байта или описание
LV_ModifyCol(4,60) ;BIN
LV_ModifyCol(5,20) ;SYM
LV_ModifyCol(6,37) ;HEX
LV_ModifyCol(7,32) ;DEC
LV_ModifyCol(8,50) ;WORD
LV_ModifyCol(9,50) ;INT
LV_ModifyCol(10,20) ;DWORD
LV_ModifyCol(11,20) ;DINT
LV_ModifyCol(12,80) ;Float
LV_ModifyCol(13,40) ;Double

Column_HEX:=6
Column_DEC:=7
Column_WORD:=8
Column_INT:=9
Column_DWORD:=10
Column_DINT:=11
Column_Float:=12
Column_Double:=13

;RS232_Baud:=9600
;RS232_Parity:="N"
;RS232_Data:=8
;RS232_Stop:=1

Gui,2:-sysmenu +owner1
Gui,2:+hwndhAddWin ; -DPIScale ; +alwaysontop
;Gui,2:Color,dfdfdf
Gui,2:Margin,10,10
Gui,2:font,s8
Gui,2:add,text,xm ym w120,Скорость передачи: ;
Gui,2:Add,DropDownList,x+3 yp-3 w195 Choose14 vRS232_Baud_Choice,50|75|110|150|300|600|1200|1800|2000|2400|3600|4800|7200|9600|14400|19200|28800|38400|57600|115200|250000|300000|375000|500000|750000|1500000
Gui,2:add,text,xm y+10 w120,Контрольные биты: ;
Gui,2:Add,DropDownList,x+3 yp-3 w195 Choose1 vRS232_Parity_Choice,N (None) - без проверки|E (EVEN) - проверка на четность|O (Odd) - проверка на нечетность|M (MARk) - бит всегда 1|S (SPACE) - бит всегда 0
Gui,2:add,text,xm y+10 w120,Число бит на символ: ;
Gui,2:Add,DropDownList,x+3 yp-3 w195 Choose4 vRS232_Data_Choice,5|6|7|8
Gui,2:add,text,xm y+10 w120,Число стоповых бит: ;
Gui,2:Add,DropDownList,x+3 yp-3 w195 Choose1 vRS232_Stop_Choice,1|2 (1.5 для 5 бит на символ)
Gui,2:add,Button,xm y+10 w80 gDefaultSettings default,Поумолчанию ;
Gui,2:add,Button,x+65 yp w80 gEndAddSettings default,Применить ;
Gui,2:add,Button,x+10 yp w80 gCancelAddSettings,Отмена ;
gosub,EndAddSettings

Gui,4:-sysmenu +owner1
Gui,4:+hwndhFilterWin -DPIScale ; +alwaysontop
Gui,4:Margin,10,10
Gui,4:font,s8
Gui,4:add,checkbox,xm ym gChangeFilter_Addres vFilter_Addres_EN,Фильтровать пакеты по "Адресу"/Byte_№1 ;
Gui,4:add,Edit, xm+255 yp-3 w70 gChangeFilter_Addres Number Limit3 vFilter_Addres,
Gui,4:Add,UpDown, Range1-255 Wrap 0x80,1
Gui,4:add,Edit, xp y+1 w70 gChangeFilter_Addres vFilter_AddresHEX,1
Gui,4:add,checkbox,xm y+20 gChangeFilter_Command vFilter_Command_EN,Фильтровать пакеты по "Команде"/Byte_№2 ;
Gui,4:add,Edit,+hwndhFiltE1 xm+255 yp-3 w70 gChangeFilter_Command Number Limit3 vFilter_Command,
Gui,4:Add,UpDown,+hwndhFiltE2 Range1-255 Wrap 0x80,1
Gui,4:add,Edit,+hwndhFiltE3 xp y+1 w70 gChangeFilter_Command vFilter_CommandHEX,1
Gui,4:Add,DropDownList,+hwndhFiltE4 xp-200 yp w195 gChangeFilter_Command vFilter_CommandRTU Choose3 AltSubmit,% RTU_Command_List
Gui,4:Add,Radio,+hwndhFiltE5 xm y+20 Checked gChangeFilter_Addres vFilter_Message_EN,Принимать все ;
Gui,4:Add,Radio,+hwndhFiltE6 xp y+5 gChangeFilter_Addres,Не принимать запросы мастера ;
Gui,4:Add,Radio,+hwndhFiltE7 xp y+5 gChangeFilter_Addres,Не принимать ответы слейва ;
Gui,4:Add,checkbox,+hwndhFiltE8 xp y+15 gChangeFilter_Addres vFilter_CRC_EN,Не принимать ошибки контрольной суммы ;
Gui,4:add,Button,xm+255 y+20 w70 gDataFilterOk,Ok ;

;GuiControl,Show,% hFiltE4
;GuiControl,Show,% hFiltE5
;GuiControl,Show,% hFiltE6
;GuiControl,Show,% hFiltE7
;loop,3
;  GuiControl,disable,% hFiltE%a_index%
GuiControl,hide,% hFiltE4
GuiControl,hide,% hFiltE5
GuiControl,hide,% hFiltE6
GuiControl,hide,% hFiltE7
loop,3
  GuiControl,enable,% hFiltE%a_index%
GuiControl,hide,% hFiltE8

LOOP:
loop
{
  sleep,10
  gosub,WM_MOUSEMOVE
  Loop_Time:=oldLoop_Time!="" ? a_tickcount-oldLoop_Time : 0
  oldLoop_Time:=a_tickcount
  if (Diskonnect)
    sleep,100
  else if (!RS232_FileHandle or COMFail)
  {
    COMFail:=0
    RS232_FileHandle:=""
    dots.=">"
    if (strlen(dots)>20)
      dots:=""
    if (!COMPort and ChoiceCOM)
      GuiControl,,_text_,% COMPort "`nCOM-порт не обнаружен`nПоиск " dots
    else
    {
      GuiControl,,_text_,% COMPort " занят другой программой`nВыберите другой порт вручную."
      ComPortList:=SeachCOM("",1)
      GuiControl,,% E_Text,% COMPort " занят другой программой`nВыберите другой порт вручную.`n`nДоступные в системе порты:`n" ComPortList
    }
    if (ChoiceCOM=1)
      COMPort:=SeachCOM("USB")
    else if (ChoiceCOM=2)
      COMPort:="COM" ManualCOM
    else if (ChoiceCOM=3)
      COMPort:=SeachCOM(ManualCOMByName)
    sleep,100
    if (COMPort!="")
      RS232_FileHandle:=RS232_Get_FileHandle(COMPort,RS232_Baud,RS232_Parity,RS232_Data,RS232_Stop)
;RS232_Get_FileHandle(RS232_Port="COM1",RS232_Baud=2400,RS232_Parity="E",RS232_Data=8,RS232_Stop=2)
  }
  else
  {
    if doonce
      doonce:=""
    if (!doonce and !Diskonnect)
    {
      dots:=""
      doonce:=1
      GuiControl,,_text_,% "Подключен " COMPort ".`n`n`t`tОтключиться"
    }

; отправляем данные в буфер порта
    if (SendData or SendDataRepitedly)
      gosub,SendData
; читаем данные с буфера порта
    varsetcapacity(In_Data,RS232_Bytes_Reqest)
    RS232_Read(RS232_FileHandle,RS232_Bytes_Reqest,RS232_Bytes_Received,In_Data)
    if (Reset_Bytes_Reqest and RS232_Bytes_Received)
    {
      Reset_Bytes_Reqest:=0
      gosub,submit2
    }
; если данные были
    if (RS232_Bytes_Received>0)
    {
      Packages_Recieved++
      if EN_Kurvendrucker
        Monitorintext.=strget(&In_Data,RS232_Bytes_Received,"UTF-8")
      Received_Addres:=NumGet(In_Data,0,"uchar")
      Received_Command:=NumGet(In_Data,1,"uchar")
; определить корректность контрольной суммы
      CheckSummIsCorrect:=0
      if (AddCS=4)
      {
        tmp1:=NumGet(In_Data,RS232_Bytes_Received-AddCSLenght,"ushort")
        tmp2:=CRC_RTU(In_Data,RS232_Bytes_Received-AddCSLenght)
        CheckSummIsCorrect:=(tmp1==tmp2)
      }
      else if (AddCS>1)
      {
        tmp1:=NumGet(In_Data,RS232_Bytes_Received-AddCSLenght,(AddCS>2)?"ushort":"Uchar")
        tmp2:=(AddCS>2) ? CRC16(In_Data,RS232_Bytes_Received-AddCSLenght) : CRC16(In_Data,RS232_Bytes_Received-AddCSLenght)&0xff
        CheckSummIsCorrect:=(tmp1==tmp2)
      }
; определить тип пакета Modbus
      if (AddCS=4)
      {
        Received_Message_Type:=""
        if  CheckSummIsCorrect
        {
          if (Received_Command>0 and Received_Command<5)
          {
            tmp1:=NumGet(In_Data,2,"Uchar")
            if (RS232_Bytes_Received=tmp1+5)
              Received_Message_Type:="Ответ"
            else
              Received_Message_Type:="Запрос"
          }
          else if (Received_Command>4 and Received_Command<7)
            Received_Message_Type:="Запрос/Ответ"
          else if (Received_Command=0x0f or Received_Command=0x10)
          {
            tmp1:=NumGet(In_Data,6,"Uchar")
            if (RS232_Bytes_Received=tmp1+9)
              Received_Message_Type:="Запрос"
            else
              Received_Message_Type:="Ответ"
          }
        }
      }
; отфильтровать пакеты
      Filtered:=0
      if (Filter_Addres_EN or Filter_Command_EN or Filter_Message_EN>1 or Filter_CRC_EN)
      {
        if (Filter_Addres_EN and Received_Addres!=Filter_Addres)
          Filtered:=1
        if (Filter_Command_EN and Received_Command!=Filter_Command)
          Filtered:=1
        if (Filter_Message_EN=2 and Received_Message_Type="Запрос")
          Filtered:=1
        if (Filter_Message_EN=3 and Received_Message_Type="Ответ")
          Filtered:=1
        if (AddCS>1 and Filter_CRC_EN and !CheckSummIsCorrect)
          Filtered:=1
      }

; разобраться с принятыми данными
      if !Filtered
      {
        GuiControl,-Redraw,% E_LV
        Add_rn:=0
        gosub,ParseReceivedBytes
        if !EN_Kurvendrucker
          LV_Add("Icon" 0)
        GuiControl,+Redraw,% E_LV
      }
      else if !EN_Kurvendrucker
      {
        if (AddCS>1)
        {
          if CheckSummIsCorrect
            LV_Icon:=ICON_V
          else
            LV_Icon:=ICON_X
        }
        else
          LV_Icon:=ICON_EMPTY
        LV_Add("Icon" LV_Icon,AddCS>1?"CRC":"","---","Адрес",Received_Addres,format("0x{:02x}",Received_Addres),"Команда",Received_Command,format("0x{:02x}",Received_Command),"---","---","---","---","---","---")
      }

      GuiControl,,Progress,% rows/Rownums*100
      if (rows>Rownums)
        gosub,Update
      if (!Add_rn and tmp_intext)
      {
        InText:=tmp_intext . (AddCRLF ? "`r`n" : "")
        Add_rn:=1
        tmp_intext=
        gosub,showtext
      }
      if (!FollowSelect and !LV_GetCount("S"))
      {
        LV_Modify(LV_GetCount(),"Vis")
        sleep,10
      }
      else if (FollowSelect and CountSelected:=LV_GetCount("S"))
      {
        startpos:=LV_GetCount()-OldGetCount+LV_GetNext()
        LV_Modify(0,"-Select")
        loop,% CountSelected
          LV_Modify(startpos+a_index-1,"Select")
        LV_Modify(startpos+CountSelected-1,"Vis Focus")
        sleep,10
        gosub,ListGetSelected
      }
      else if (FollowSelect and !LV_GetCount("S"))
      {
        LV_Modify(LV_GetCount(),"Vis")
        sleep,10
      }
      if (MonitorVar and !Filtered)
      {
        varsetcapacity(Monitorvalue_tmp,Slave_Len)
        SwapData(In_Data,Slave_Start_Byte,Slave_Len,Monitorvalue_tmp)
        Monitorvalue:=numget(Monitorvalue_tmp,0,Slave_Data_Type)
        if GrafPause
          Gdip_X_Offset+=1
        else
          Gdip_Marker_mx-=Gdip_ZoomX
        Monitor_value.InsertAt(1,Monitorvalue)
        Monitor_value_T.InsertAt(1,a_hour ":" a_min "." a_sec "." a_MSec)
        gosub,GrafDraw
      }
      OldGetCount:=LV_GetCount()
      if (MouseAbovehGraf and (MonitorVar or EN_Kurvendrucker))
        SB_SetText("Дополнительное меню правой кнопкой мыши. Масштаб = " round(Gdip_ZoomX,1) ". Максимальное значение = " hi_lim ". Время отрисовки кадра = " DrawTime "ms. Чило точек = " Monitor_value_T.maxindex() ".")

      if EN_Kurvendrucker
      {
        if AutoEnding
        {
          if ((EndSymbol="" or TryFind>10) and strlen(Monitorintext)>100)
          {
            findMetaSbl(Monitorintext,SplSymbol,EndSymbol)
            TryFind:=0
          }
          else
            TryFind++
        }
  ;tooltip,% TryFind "`n""" SplSymbol """`n""" EndSymbol """"

        if (EndSymbol!="")
        while(EndPos:=instr(Monitorintext,EndSymbol))
        {
          RS232_Bytes_Reqest:=EndPos*2
          GuiControl,,RS232_Bytes_Reqest,% RS232_Bytes_Reqest
          Monitoringtext:=substr(Monitorintext,1,EndPos-1)
          StringTrimLeft,Monitorintext,Monitorintext,% EndPos
          if (SplSymbol==EndSymbol)
          {
            loop,parse,Monitoringtext,% EndSymbol
            {
              if (a_loopfield!="")
              {
                tmp1:=a_loopfield
                if instr(tmp1,"`r")
                  StringTrimRight,tmp1,tmp1,1
                Monitor_value1.InsertAt(1,tmp1)
                Monitor_value_T.InsertAt(1,a_hour ":" a_min "." a_sec "." a_MSec)
                if GrafPause
                  Gdip_X_Offset+=1
                else
                  Gdip_Marker_mx-=Gdip_ZoomX
              }
            }
            NumOfGraf:=1
          }
          else
          {
            if (Monitoringtext!="")
            {
              NumOfGraf:=0
              loop,parse,Monitoringtext,% SplSymbol
              {
                if (a_loopfield!="")
                {
                  tmp1:=a_loopfield
                  if instr(tmp1,"`r")
                    StringTrimRight,tmp1,tmp1,1
                  Monitor_value%a_index%.InsertAt(1,tmp1)
                  NumOfGraf++
                }
              }
              Monitor_value_T.InsertAt(1,a_hour ":" a_min "." a_sec "." a_MSec)
              if GrafPause
                Gdip_X_Offset+=1
              else
                Gdip_Marker_mx-=Gdip_ZoomX
            }
          }
        }
        gosub,GrafDraw
      }
    }
  }
}
return

ParseReceivedBytes:
  regcount:=0
  loop,% RS232_Bytes_Received
  {
    LV_Icon:=ICON_EMPTY
    LV_Hash=
    if (a_index=1)
    {
      LV_Icon:=ICON_START
      if !Received_Message_Type
        LV_Hash:="Start " Packages_Recieved
      else
        LV_Hash:=Received_Message_Type " " Packages_Recieved
    }
    rows++
    LV_NumByte:=a_index-1
    LV_DEC:=numget(In_Data,a_index-1,"uchar")
    LV_HEX:=format("0x{:02x}",LV_DEC)
    LV_Sym:=Chr(LV_HEX)
    LV_Bin:=DECtoBIN(LV_HEX)
    loop,% 8-strlen(LV_Bin)
      LV_Bin:="0" . LV_Bin
    tmp:=(LV_DEC&(1<<7)) ? "." : ""
    LV_7Seg:=Arr7SEGtoASCII[(LV_DEC&~(1<<7))] . tmp

    LV_WORD:=LV_INT:=LV_DWORD:=LV_DINT:=LV_Float:=LV_Double:=""
    if (RS232_Bytes_Received-a_index+2>2)
    {
      varsetcapacity(LV_INT_tmp,2)
      SwapData(In_Data,a_index-1,2,LV_INT_tmp)
      LV_WORD:=numget(LV_INT_tmp,0,"ushort")
      LV_INT:=numget(LV_INT_tmp,0,"short")
      AddAdd(LV_INT)
    }
    if (RS232_Bytes_Received-a_index+2>4)
    {
      varsetcapacity(LV_DINT_tmp,4)
      SwapData(In_Data,a_index-1,4,LV_DINT_tmp)
      LV_DWORD:=numget(LV_DINT_tmp,0,"uint")
      LV_DINT:=numget(LV_DINT_tmp,0,"int")
      AddAdd(LV_DINT)
      LV_Float:=numget(LV_DINT_tmp,0,"float")
      AddAdd(LV_Float)
    }
    if (RS232_Bytes_Received-a_index+2>8)
    {
      varsetcapacity(LV_Double_tmp,8)
      SwapData(In_Data,a_index-1,8,LV_Double_tmp)
      LV_Double:=numget(LV_Double_tmp,0,"Double")
      AddAdd(LV_Double)
    }
    if (AddCS=4)
    {
; работаем с Modbus
;      if CheckSummIsCorrect
      {
        LV_NumByte:=""
        if (Received_Message_Type="Ответ") ; ---------------------------------------------------------
        {
          if (Received_Command>0 and Received_Command<5)
          {
            if (a_index=3)
            {
              if !RTUErrorWasOcured
                LV_NumByte:="Кол-во байт далее"
              else
                LV_NumByte:=RTUErr%LV_DEC%
              LV_Icon:=ICON_INFO
            }
            if (a_index>3 and a_index<RS232_Bytes_Received-1)
            {
              if ((Received_Command>2 and Received_Command<5) and !(a_index&1))
              {
                LV_NumByte:="+" regcount " (+0x" format("{:x}",regcount) ")"
                regcount++
                LV_Icon:=ICON_REGISTER
                LV_Hash:="Значения регистров по смещению"
              }
              if (Received_Command>0 and Received_Command<3)
              {
                LV_NumByte:=(regcount+1)*8 "-" regcount*8
                regcount++
                LV_Icon:=ICON_REGISTER
                LV_Hash:="Значения битов по смещению"
              }
            }
          }
          else if (Received_Command=0xf or Received_Command=0x10)
          {
            if (a_index=3)
            {
              LV_NumByte:="Стартовый регистр"
              LV_Icon:=ICON_INFO
              LV_tmp:=LV_WORD
            }
            if (a_index=4)
              LV_NumByte:=LV_tmp " (" format("0x{:x}",LV_tmp) ")"
            if (a_index=5)
            {
              LV_NumByte:="Кол-во запрашиваемых регистров"
              LV_Icon:=ICON_INFO
              LV_tmp:=LV_WORD
            }
            if (a_index=6)
              LV_NumByte:=LV_tmp " (" format("0x{:x}",LV_tmp) ")"
          }
        }
        else if (Received_Message_Type="Запрос") ; ---------------------------------------------------------
        {
          if (Received_Command>0 and Received_Command<5)
          {
            if (a_index=3)
            {
              LV_NumByte:="Стартовый регистр"
              LV_Icon:=ICON_INFO
              LV_tmp:=LV_WORD
            }
            if (a_index=4)
              LV_NumByte:=LV_tmp " (" format("0x{:x}",LV_tmp) ")"
            if (a_index=5)
            {
              if (Received_Command<3)
                LV_NumByte:="Кол-во запрашиваемых бит"
              if  (Received_Command>2)
                LV_NumByte:="Кол-во запрашиваемых регистров"
              LV_Icon:=ICON_INFO
              LV_tmp:=LV_WORD
            }
            if (a_index=6)
              LV_NumByte:=LV_tmp " (" format("0x{:x}",LV_tmp) ")"
          }
          else if (Received_Command=0xf or Received_Command=0x10)
          {
            if (a_index=3)
            {
              LV_NumByte:="Стартовый регистр"
              LV_Icon:=ICON_INFO
              LV_tmp:=LV_WORD
            }
            if (a_index=4)
              LV_NumByte:=LV_tmp " (" format("0x{:x}",LV_tmp) ")"
            if (a_index=5)
            {
              LV_NumByte:="Кол-во записываемых регистров"
              LV_Icon:=ICON_INFO
              LV_tmp:=LV_WORD
            }
            if (a_index=6)
              LV_NumByte:=LV_tmp " (" format("0x{:x}",LV_tmp) ")"
            if (a_index=7)
            {
              LV_NumByte:="Кол-во байт далее = " LV_DEC
              LV_Icon:=ICON_INFO
              LV_tmp:=LV_WORD
            }
            if (a_index>7 and a_index<RS232_Bytes_Received-1)
            {
              if (Received_Command=0x10 and !(a_index&1))
              {
                LV_NumByte:="+" regcount " (+0x" format("{:x}",regcount) ")"
                regcount++
                LV_Icon:=ICON_REGISTER
                LV_Hash:="Значения регистров по смещению"
              }
              if (Received_Command=0xf)
              {
                LV_NumByte:=(regcount+1)*8-1 "-" regcount*8
                regcount++
                LV_Icon:=ICON_REGISTER
                LV_Hash:="Значения битов по смещению"
              }
            }
          }
        }
        else if (Received_Message_Type="Запрос/Ответ") ; ---------------------------------------------------------
        {
          if (a_index=3)
          {
            LV_NumByte:="Регистр"
            LV_Icon:=ICON_INFO
            LV_tmp:=LV_WORD
          }
          if (a_index=4)
            LV_NumByte:=LV_tmp " (" format("0x{:x}",LV_tmp) ")"
          if (a_index=5)
          {
            LV_NumByte:="Значение"
            LV_Icon:=ICON_INFO
            if (LV_WORD=0xff00)
              LV_tmp:="ON (" format("0x{:x}",LV_WORD) ")"
            else if (LV_WORD=0x0000)
              LV_tmp:="OFF (" format("0x{:x}",LV_WORD) ")"
            else
              LV_tmp:=LV_WORD " (" format("0x{:x}",LV_WORD) ")"
          }
          if (a_index=6)
            LV_NumByte:=LV_tmp
        }
        if (a_index=1)
          LV_NumByte:="Адрес"
        if (a_index=2)
        {
          RTUErrorWasOcured:=0
          if (LV_DEC&0x80)
          {
            RTUErrorWasOcured:=1
            LV_NumByte:="Ошибка"
            LV_Icon:=ICON_ERROR
          }
          else
          {
            LV_NumByte:=RTUFunc%LV_DEC%
            LV_Hash:=RTUF%LV_DEC%
          }
        }
        if (a_index=3 and RTUErrorWasOcured)
        {
          LV_NumByte:=RTUErr%LV_DEC%
          LV_Icon:=ICON_INFO
        }
      }
;      else
;        LV_NumByte:=""
      if (a_index>RS232_Bytes_Received-2)
          LV_NumByte:=""
    }
    if (a_index>1 and AddCS>1 and !CheckSummIsCorrect)
      LV_Icon:=ICON_X
    if (AddCS>1)
    {
      if ((AddCS>2 and a_index=(RS232_Bytes_Received-1)) or (a_index=RS232_Bytes_Received))
      {
        if CheckSummIsCorrect
        {
          LV_Icon:=ICON_V
          LV_Hash:="CRC" ;"V"
          LV_NumByte:="Контрольная сумма верна"
        }
        else
        {
          LV_Icon:=ICON_CRC_ERROR
          LV_Hash:="CRC" ;"X"
          LV_NumByte:="Ошибка контрольной суммы"
        }
      }
    }
; -------------------------------------------------------------------------------------
    if !EN_Kurvendrucker
      LV_Add("Icon" LV_Icon,LV_Hash,a_index-1,LV_NumByte,LV_Bin,LV_Sym,LV_HEX,LV_DEC,LV_WORD,LV_INT,LV_DWORD,LV_DINT,LV_Float,LV_Double)
;  #|№ байта|№ байта или описание|BIN|SYM|HEX|DEC|INT|WORD|DINT|DWORD|Float|Double
; -------------------------------------------------------------------------------------
    if !(LV_HEX=0x0a or LV_HEX=0x0d)
      tmp_intext.=(LV_HEX ? LV_Sym : " ")
    if (LV_HEX=0x0a) ; and LV_HEX_old=0x0d)
    {
      InText:=tmp_intext . (instr(tmp_intext,"`r`n") ? "" : "`r`n")
      tmp_intext=
      Add_rn:=1
      gosub,showtext
    }
    LV_HEX_old:=LV_HEX
  }
  return

showtext:
  SendMessage, 0xB0, &E_Text_start, &E_Text_end, , ahk_id %E_Text%  ; EM_GETSEL
  SendMessage, 0x0E, , , , ahk_id %E_Text%    ;  WM_GETTEXTLENGTH
  textlength:=Errorlevel
  if !(NumGet(E_Text_start)=textlength)
    GuiControl, -Redraw, %E_Text%
  SendMessage, 0x00B1, textlength, textlength, , ahk_id %E_Text%    ;  EM_SETSEL
  SendMessage, 0x00C2 , TRUE, &InText,, ahk_id %E_Text%  ;	EM_REPLACESEL
  if !(NumGet(E_Text_start)=textlength)
    SendMessage, 0x0B1, NumGet(E_Text_start), NumGet(E_Text_end), , ahk_id %E_Text%  ; EM_SETSEL
  GuiControl, +Redraw, %E_Text%
  return

DiskonnectCOM:
  if !RS232_FileHandle
    return
  GuiControl,,_text_,% COMPort "`n`t`tОтключен"
  RS232_Close(RS232_FileHandle)
  Diskonnect=1
  return

Update:
  Packages_Recieved:=0
  rows=
  GuiControl,,_InText_
  GuiControl,,Progress,0
  LV_Delete()
  tmp_intext=
  return

ToTheEnd:
  SendMessage, 0x0E, , , , ahk_id %E_Text%    ;  WM_GETTEXTLENGTH
  textlength:=Errorlevel
  SendMessage, 0x00B1, textlength, textlength, , ahk_id %E_Text%    ;  EM_SETSEL
  GuiControl, +Redraw, %E_Text%
  LV_Modify(0,"-Select")
  return

AceptSettings:
  Gui,1:submit,nohide
  AddCSLenght:=(AddCS>2 ? 2:AddCS-1)
  Diskonnect=
  RS232_Close(RS232_FileHandle)
  return

Submit2:
  Gui,1:submit,nohide
  AddCSLenght:=(AddCS>2 ? 2:AddCS-1)
  return

Submit:
  Gui,1:submit,nohide
  AddCSLenght:=(AddCS>2 ? 2:AddCS-1)
  if (a_GuiControl="AddCS")
  {
    if (AddCS=4)
    {
      GuiControl,Move,% E_1,y420 h75
      loop,2
        GuiControl,Show,% hE_RTU_Command_%a_index%
      loop,5
        GuiControl,Show,% hE_RTU_StartReg_%a_index%
      loop,5
        GuiControl,Show,% hE_RTU_Addres_%a_index%
      loop,5
        GuiControl,Show,% hE_RTU_RegNum_%a_index%
      loop,2
        GuiControl,Show,% hE_RTU_Val_%a_index%
      GuiControl,Show,% hFiltE4
      GuiControl,Show,% hFiltE5
      GuiControl,Show,% hFiltE6
      GuiControl,Show,% hFiltE7
      GuiControl,Show,% hAutoByRe
      loop,3
        GuiControl,disable,% hFiltE%a_index%
      Gui,4:submit,nohide
      Filter_CommandFlag:=1
      if (Filter_CommandRTU>0 and Filter_CommandRTU<7)
        Filter_Command:=Filter_CommandRTU
      else if (Filter_CommandRTU>6)
        Filter_Command:=Filter_CommandRTU+8
      GuiControl,,% hFiltE1,% Filter_Command
    }
    else
    {
      GuiControl,enable,% E_1
      GuiControl,Move,% E_1,y210 h300
      loop,2
        GuiControl,hide,% hE_RTU_Command_%a_index%
      loop,5
        GuiControl,hide,% hE_RTU_StartReg_%a_index%
      loop,5
        GuiControl,hide,% hE_RTU_Addres_%a_index%
      loop,5
        GuiControl,hide,% hE_RTU_RegNum_%a_index%
      loop,2
        GuiControl,hide,% hE_RTU_Val_%a_index%
      GuiControl,hide,% hFiltE4
      GuiControl,hide,% hFiltE5
      GuiControl,hide,% hFiltE6
      GuiControl,hide,% hFiltE7
      GuiControl,hide,% hAutoByRe
      loop,3
        GuiControl,enable,% hFiltE%a_index%
    }
    if (AddCS>1)
      GuiControl,show,% hFiltE8
    else
      GuiControl,hide,% hFiltE8
  }
  if (AddCS=4)
  {
    if (RTU_Command>0 and RTU_Command<5)
    {
      loop,5
        guicontrol,enable,% hE_RTU_RegNum_%a_index%
      loop,2
        GuiControl,disable,% hE_RTU_Val_%a_index%
      GuiControl,disable,% E_1
    }
    if (RTU_Command=5 or RTU_Command=6)
    {
      if SendDataRepitedly
        gosub,SendDataRepitedly
      loop,5
        guicontrol,disable,% hE_RTU_RegNum_%a_index%
      loop,2
        GuiControl,enable,% hE_RTU_Val_%a_index%
      GuiControl,enable,% E_1
    }
    if (RTU_Command=7 or RTU_Command=8)
    {
      if SendDataRepitedly
        gosub,SendDataRepitedly
      loop,5
        guicontrol,enable,% hE_RTU_RegNum_%a_index%
      loop,2
        GuiControl,enable,% hE_RTU_Val_%a_index%
      GuiControl,enable,% E_1
    }
  }
  if (hi_lim<1)
    hi_lim:=1
  return

ChangeAddres:
  Gui,1:submit,nohide
  AddCSLenght:=(AddCS>2 ? 2:AddCS-1)
  if (a_GuiControl="RTU_SlaveAdress")
  {
    if RTU_SlaveAdressHEXFlag
    {
      RTU_SlaveAdressHEXFlag:=0
      return
    }
    RTU_SlaveAdressFlag:=1
    GuiControl,,RTU_SlaveAdressHEX,% format("{:X}",RTU_SlaveAdress)
  }
  else if (a_GuiControl="RTU_SlaveAdressHEX")
  {
    if RTU_SlaveAdressFlag
    {
      RTU_SlaveAdressFlag:=0
      return
    }
    RTU_SlaveAdressHEXFlag:=1
    tmp:="0x" RTU_SlaveAdressHEX
    GuiControl,,RTU_SlaveAdress,% tmp+0
  }
  return

ChangeStartReg:
  Gui,1:submit,nohide
  AddCSLenght:=(AddCS>2 ? 2:AddCS-1)
  if (a_GuiControl="RTU_StartReg")
  {
    if RTU_StartRegHEXFlag
    {
      RTU_StartRegHEXFlag:=0
      return
    }
    RTU_StartRegFlag:=1
    GuiControl,,RTU_StartRegHEX,% format("{:X}",RTU_StartReg)
  }
  else if (a_GuiControl="RTU_StartRegHEX")
  {
    if RTU_StartRegFlag
    {
      RTU_StartRegFlag:=0
      return
    }
    RTU_StartRegHEXFlag:=1
    tmp:="0x" RTU_StartRegHEX
    GuiControl,,RTU_StartReg,% tmp+0
  }
  return

ChangeRegNum:
  Gui,1:submit,nohide
  AddCSLenght:=(AddCS>2 ? 2:AddCS-1)
  if (a_GuiControl="RTU_RegNum")
  {
    if RTU_RegNumHEXFlag
    {
      RTU_RegNumHEXFlag:=0
      return
    }
    RTU_RegNumFlag:=1
    GuiControl,,RTU_RegNumHEX,% format("{:X}",RTU_RegNum)
  }
  else if (a_GuiControl="RTU_RegNumHEX")
  {
    if RTU_RegNumFlag
    {
      RTU_RegNumFlag:=0
      return
    }
    RTU_RegNumHEXFlag:=1
    tmp:="0x" RTU_RegNumHEX
    GuiControl,,RTU_RegNum,% tmp+0
  }
  return

ChangeFilter_Addres:
  Gui,4:submit,nohide
  if (a_GuiControl="Filter_Addres")
  {
    if Filter_AddresHEXFlag
    {
      Filter_AddresHEXFlag:=0
      return
    }
    Filter_AddresFlag:=1
    GuiControl,,Filter_AddresHEX,% format("{:X}",Filter_Addres)
  }
  else if (a_GuiControl="Filter_AddresHEX")
  {
    if Filter_AddresFlag
    {
      Filter_AddresFlag:=0
      return
    }
    Filter_AddresHEXFlag:=1
    tmp:="0x" Filter_AddresHEX
    GuiControl,,Filter_Addres,% tmp+0
  }
  return

ChangeFilter_Command:
  Gui,4:submit,nohide
  if (a_GuiControl="Filter_Command")
  {
    if (Filter_CommandHEXFlag)
    {
      Filter_CommandHEXFlag:=0
      return
    }
    Filter_CommandFlag:=1
    GuiControl,,Filter_CommandHEX,% format("{:X}",Filter_Command)
  }
  else if (a_GuiControl="Filter_CommandHEX")
  {
    if (Filter_CommandFlag)
    {
      Filter_CommandFlag:=0
      return
    }
    Filter_CommandHEXFlag:=1
    tmp:="0x" Filter_CommandHEX
    GuiControl,,Filter_Command,% tmp+0
    Filter_Command:=tmp+0
  }
  else if (a_GuiControl="Filter_CommandRTU")
  {
    Filter_CommandFlag:=1
    if (Filter_CommandRTU>0 and Filter_CommandRTU<7)
      Filter_Command:=Filter_CommandRTU
    else if (Filter_CommandRTU>6)
      Filter_Command:=Filter_CommandRTU+8
    GuiControl,,Filter_Command,% Filter_Command
  }
  return

ChoseCOMNo:
  Gui,1:submit,nohide
  AddCSLenght:=(AddCS>2 ? 2:AddCS-1)
  if (!noChoseCOMNo)
  {
    noChoseCOMNo:=1
    return
  }
  GuiControl,,% hCOMNo,1
  return

ChoseCOMName:
  Gui,1:submit,nohide
  AddCSLenght:=(AddCS>2 ? 2:AddCS-1)
  GuiControl,,% hCOMName,1
  return

AddSettings:
  Gui,2:show
  return
CancelAddSettings:
  Gui,2:hide
  Gui,1:show
  return
EndAddSettings:
  Gui,2:submit
  Gui,1:show
  stringleft,RS232_Parity_Choice,RS232_Parity_Choice,1
  RS232_Baud:=RS232_Baud_Choice
  RS232_Parity:=RS232_Parity_Choice
  RS232_Data:=RS232_Data_Choice
  stringleft,RS232_Stop_Choice,RS232_Stop_Choice,1
  RS232_Stop:=RS232_Stop_Choice
  if (RS232_Data_Choice=5 and RS232_Stop_Choice=2)
    RS232_Stop=1.5
  gosub,AceptSettings
  return
DefaultSettings:
  GuiControl,Choose,RS232_Baud_Choice,14
  GuiControl,Choose,RS232_Parity_Choice,1
  GuiControl,Choose,RS232_Data_Choice,4
  GuiControl,Choose,RS232_Stop_Choice,1
  return

ButtonSendData:
  Gui,1:submit,nohide
  AddCSLenght:=(AddCS>2 ? 2:AddCS-1)
  SendData:=1
  return

SendData:
  Gui,1:submit,nohide
  SendData:=0
  Timeout:=Ceil(((RS232_Data+4)/RS232_Baud)*1000)
  if (AddCS=4)
  {
    if (RTU_Command>0 and RTU_Command<5)
    {
      if (Auto_Bytes_Reqest and (RTU_Command=1 or RTU_Command=2))
        RS232_Bytes_Reqest:=Ceil(RTU_RegNum/8)+6
      if (Auto_Bytes_Reqest and (RTU_Command=3 or RTU_Command=4))
        RS232_Bytes_Reqest:=RTU_RegNum*2+6
      size_to_send:=6
      varsetcapacity(VarDataToSend,size_to_send+AddCSLenght)
      NumPut(RTU_SlaveAdress,VarDataToSend,0,"uchar")
      NumPut(RTU_Command,VarDataToSend,1,"uchar")
      NumPut(SwapByte(RTU_StartReg),VarDataToSend,2,"ushort")
      NumPut(SwapByte(RTU_RegNum),VarDataToSend,4,"ushort")
      NumPut(CRC_RTU(VarDataToSend,size_to_send),VarDataToSend,size_to_send,"ushort")
    }
    else if (RTU_Command=5 or RTU_Command=6)
    {
      if Auto_Bytes_Reqest
        RS232_Bytes_Reqest:=9
      size_to_send:=6
      varsetcapacity(VarDataToSend,size_to_send+AddCSLenght)
      NumPut(RTU_SlaveAdress,VarDataToSend,0,"uchar")
      NumPut(RTU_Command,VarDataToSend,1,"uchar")
      NumPut(SwapByte(RTU_StartReg),VarDataToSend,2,"ushort")

      if (ChoiceFormat=1) ;text
        size_to_send_tmp:=STRToDATA(DataToSend,VarDataToSend_tmp)
      else if (ChoiceFormat=2) ;DEC
        size_to_send_tmp:=DECToDATA(DataToSend,VarDataToSend_tmp,1,ChoiceType,ChoiceSize)
      else if (ChoiceFormat=3) ;HEX
        size_to_send_tmp:=HEXstrToDATA(DataToSend,VarDataToSend_tmp)
      else if (ChoiceFormat=4) ;BIN
        size_to_send_tmp:=BINstrToDATA(DataToSend,VarDataToSend_tmp)

      NumPut(numget(VarDataToSend_tmp,0,"Uchar"),VarDataToSend,4,"ushort")
      NumPut(numget(VarDataToSend_tmp,1,"Uchar"),VarDataToSend,5,"ushort")
      NumPut(CRC_RTU(VarDataToSend,size_to_send),VarDataToSend,size_to_send,"ushort")
    }
    else if (RTU_Command=7 or RTU_Command=8)
    {
      if Auto_Bytes_Reqest
        RS232_Bytes_Reqest:=9
      if (RTU_Command=7)
      {
        RTU_Command=15
        RTU_ByteNum:=ceil(RTU_RegNum/8)
        size_to_send:=7+ceil(RTU_RegNum/8)
      }
      if (RTU_Command=8)
      {
        RTU_Command=16
        RTU_ByteNum:=RTU_RegNum*2
        size_to_send:=7+RTU_ByteNum
      }
      varsetcapacity(VarDataToSend,size_to_send+AddCSLenght)
      varsetcapacity(VarDataToSend,size_to_send+AddCSLenght)
      NumPut(RTU_SlaveAdress,VarDataToSend,0,"uchar")
      NumPut(RTU_Command,VarDataToSend,1,"uchar")
      NumPut(SwapByte(RTU_StartReg),VarDataToSend,2,"ushort")
      NumPut(SwapByte(RTU_RegNum),VarDataToSend,4,"ushort")
      NumPut(RTU_ByteNum,VarDataToSend,6,"uchar")
      if (ChoiceFormat=1) ;text
        size_to_send_tmp:=STRToDATA(DataToSend,VarDataToSend_tmp)
      else if (ChoiceFormat=2) ;DEC
        size_to_send_tmp:=DECToDATA(DataToSend,VarDataToSend_tmp,1,ChoiceType,ChoiceSize)
      else if (ChoiceFormat=3) ;HEX
        size_to_send_tmp:=HEXstrToDATA(DataToSend,VarDataToSend_tmp)
      else if (ChoiceFormat=4) ;BIN
        size_to_send_tmp:=BINstrToDATA(DataToSend,VarDataToSend_tmp)
      loop,% RTU_ByteNum
        NumPut(numget(VarDataToSend_tmp,a_index-1,"Uchar"),VarDataToSend,a_index+6,"uchar")
      NumPut(CRC_RTU(VarDataToSend,size_to_send),VarDataToSend,size_to_send,"ushort")
    }
    if Auto_Bytes_Reqest
      Reset_Bytes_Reqest:=1
    if (RTU_Command>4 and !BeliveMe)
      return
    else if (RTU_Command>4 and BeliveMe)
      guicontrol,,BeliveMe,0
  }
  else
  {
    if (ChoiceFormat=1) ;text
      size_to_send:=STRToDATA(DataToSend,VarDataToSend)
    else if (ChoiceFormat=2) ;dec
      size_to_send:=DECToDATA(DataToSend,VarDataToSend,1,ChoiceType,ChoiceSize)
    else if (ChoiceFormat=3) ;HEX
      size_to_send:=HEXstrToDATA(DataToSend,VarDataToSend)
    else if (ChoiceFormat=4) ;BIN
      size_to_send:=BINstrToDATA(DataToSend,VarDataToSend)
    if (AddCS>0)
      NumPut(CRC16(VarDataToSend,size_to_send),VarDataToSend,size_to_send,AddCS==2?"Uchar":"ushort")
  }
  if !size_to_send
    return
  RS232_Write(RS232_FileHandle,&VarDataToSend,size_to_send+AddCSLenght)
  return

SendDataRepitedly:
  SendDataRepitedly:=!SendDataRepitedly
  if SendDataRepitedly
    GuiControl,,% hSendDataRepitedly,Стоп ;
  else
    GuiControl,,% hSendDataRepitedly,Циклично ;
  return

DataToSendCheck:
  Gui,1:submit,nohide
  AddCSLenght:=(AddCS>2 ? 2:AddCS-1)
  tmp1:=DataToSend
  if (ChoiceFormat=2)
  {
    RegExMatch(DataToSend,"[0-9 -\.]+",tmp1)
    if instr(DataToSend,".")
    {
      hEDEC4once:=0
      GuiControl,,% hEDEC4,1
      GuiControl,disable,% hEDEC5
      GuiControl,disable,% hEDEC6
;      GuiControl,,% hEDEC7,1
    }
    else if instr(DataToSend,"-")
    {
      hEDEC4once:=0
      GuiControl,,% hEDEC3,1
      GuiControl,enable,% hEDEC5
      GuiControl,enable,% hEDEC6
    }
    if (!hEDEC4once and !instr(DataToSend,"-") and !instr(DataToSend,"."))
    {
      hEDEC4once:=1
      GuiControl,,% hEDEC2,1
      GuiControl,enable,% hEDEC5
      GuiControl,enable,% hEDEC6
    }
  }
  if (ChoiceFormat=3)
    RegExMatch(DataToSend,"[A-Fa-f0-9 ]+",tmp1)
  if (ChoiceFormat=4)
    RegExMatch(DataToSend,"[0-1 ]+",tmp1)
  if (DataToSend!=tmp1)
  {
    GuiControl,,DataToSend,% tmp1
    PostMessage,0x00B1,1000,1000,,ahk_id %E_1%    ;  EM_SETSEL
    PostMessage,0xB7,,,,ahk_id %E_1%    ;  EM_SCROLLCARET
  }
  return



ConvertToSYM: ; to text
  gosub,ResetDECChoise
  Gui,1:submit,nohide
  AddCSLenght:=(AddCS>2 ? 2:AddCS-1)
  if (PreviousConversion="hex") ; hex to text
  {
    tmp_DataToSend:=""
    ClearVar(DataToSend)
    loop,% ceil(strlen(DataToSend)/2)
    {
      stringright,tmp2,DataToSend,2
      tmp_DataToSend:=chr("0x"tmp2) . tmp_DataToSend
      StringTrimRight,DataToSend,DataToSend,2
    }
  }
  else if (PreviousConversion="bin") ; bin to text
  {
    size:=BINstrToDATA(DataToSend,tmp1)
    tmp_DataToSend:=DATAtoSTR(&tmp1,0,size)
  }
  else if (PreviousConversion="dec") ; dec to text
  {
    DECToDATA(DataToSend,tmp1,1,ChoiceType,ChoiceSize)
    tmp_DataToSend:=DATAtoSTR(&tmp1,0,ChoiceSize)
  }
  GuiControl,,DataToSend,% tmp_DataToSend
  PreviousConversion:="sym"
  return

ConvertToHEX: ; to hex
  gosub,ResetDECChoise
  Gui,1:submit,nohide
  AddCSLenght:=(AddCS>2 ? 2:AddCS-1)
  if (PreviousConversion="sym") ; text to hex
  {
    tmp_DataToSend:=""
    loop,parse,DataToSend
      tmp_DataToSend.=format("{:02X}",Asc(A_LoopField)) " "
    stringtrimright,tmp_DataToSend,tmp_DataToSend,1
  }
  else if (PreviousConversion="bin") ; bin to hex
  {
    ClearVar(DataToSend)
    tmp_DataToSend:=format("{:02X}",BINtoDEC(DataToSend))
    if (strlen(tmp_DataToSend)&1)
      tmp_DataToSend:="0" . tmp_DataToSend
  }
  else if (PreviousConversion="dec") ; dec to hex
  {
    DECToDATA(DataToSend,tmp1,1,ChoiceType,ChoiceSize)
    tmp_DataToSend:=DATAtoHEX(&tmp1,0,ChoiceSize)
  }
  GuiControl,,DataToSend,% tmp_DataToSend
  PreviousConversion:="hex"
  return

ConvertToBIN: ; to bin
  gosub,ResetDECChoise
  Gui,1:submit,nohide
  AddCSLenght:=(AddCS>2 ? 2:AddCS-1)
  if (PreviousConversion="sym") ; text to bin
  {
    size:=STRToDATA(DataToSend,tmp0)
    tmp_DataToSend:=DATAtoBIN(&tmp0,0,size)
  }
  else if (PreviousConversion="hex") ; hex to bin
  {
    size:=HEXstrToDATA(DataToSend,tmp0)
    tmp_DataToSend:=DATAtoBIN(&tmp0,0,size)
  }
  else if (PreviousConversion="dec") ; dec to bin
  {
    DECToDATA(DataToSend,tmp1,1,ChoiceType,ChoiceSize)
    tmp_DataToSend:=DATAtoBIN(&tmp1,0,ChoiceSize)
  }
  GuiControl,,DataToSend,% tmp_DataToSend
  PreviousConversion:="bin"
  return

ConvertToDEC: ; to dec
  loop,8
    GuiControl,show,% hEDEC%a_index%
  Gui,1:submit,nohide
  AddCSLenght:=(AddCS>2 ? 2:AddCS-1)
  old_DataToSend:=DataToSend
  return
ConvertToDECRun:
  if !(ChoiceDEC and ChoiceDECSize)
    return
  ChoicePType:={1:"char",2:"short",3:"int",4:"int64"}
  if (ChoiceDEC=1)
    ChoiceType:="U" ChoicePType[ChoiceDECSize]
  else if (ChoiceDEC=2)
    ChoiceType:="" ChoicePType[ChoiceDECSize]
  else if (ChoiceDEC=3 and ChoiceDECSize=3)
    ChoiceType:="float"
  else if (ChoiceDEC=3 and ChoiceDECSize=4)
    ChoiceType:="duoble"
  if (ChoiceDECSize=3)
    ChoiceSize:=4
  else if (ChoiceDECSize=4)
    ChoiceSize:=8
  else
    ChoiceSize:=ChoiceDECSize
  if (PreviousConversion="sym") ; text to dec
  {
    STRToDATA(old_DataToSend,tmp0,1)
    tmp_DataToSend:=numget(tmp0,0,ChoiceType)
  }
  else if (PreviousConversion="hex") ; hex to dec
  {
    ClearVar(old_DataToSend)
    HEXstrToDATA(old_DataToSend,tmp0,1)
    tmp_DataToSend:=numget(tmp0,0,ChoiceType)
  }
  else if (PreviousConversion="bin") ; bin to dec
  {
    BINstrToDATA(old_DataToSend,tmp0,1)
    tmp_DataToSend:=numget(tmp0,0,ChoiceType)
  }
  GuiControl,,DataToSend,% tmp_DataToSend
  PreviousConversionWasDEC:=1
  return

ChooseDEC:
  Gui,1:submit,nohide
  AddCSLenght:=(AddCS>2 ? 2:AddCS-1)
  if (ChoiceDEC=3)
  {
    if (ChoiceDECSize and ChoiceDECSize<3)
    {
      GuiControl,,% hEDEC5,0
      GuiControl,,% hEDEC6,0
      GuiControl,,% hEDEC7,1
      ChoiceDECSize:=3
    }
    GuiControl,disable,% hEDEC5
    GuiControl,disable,% hEDEC6
  }
  else
  {
    GuiControl,enable,% hEDEC5
    GuiControl,enable,% hEDEC6
  }
  gosub,ConvertToDECRun
  return

ChooseDECSize:
  Gui,1:submit,nohide
  AddCSLenght:=(AddCS>2 ? 2:AddCS-1)
  GuiControl,disable,% hEDEC%a_index%
  gosub,ConvertToDECRun
  return

ResetDECChoise:
  loop,8
    GuiControl,hide,% hEDEC%a_index%
  GuiControl,,% hEDEC2,1
  GuiControl,,% hEDEC2,0
  GuiControl,,% hEDEC5,1
  GuiControl,,% hEDEC5,0
  ChoiceDEC:=0
  ChoiceDECSize:=0
  if PreviousConversionWasDEC
  {
    PreviousConversion:="dec"
    PreviousConversionWasDEC:=0
  }
  return

DataFilter:
  Gui,4:show,,Фильтровать данные
  if (AddCS=4)
  {
    Gui,4:submit,nohide
    Filter_CommandFlag:=1
    if (Filter_CommandRTU>0 and Filter_CommandRTU<7)
      Filter_Command:=Filter_CommandRTU
    else if (Filter_CommandRTU>6)
      Filter_Command:=Filter_CommandRTU+8
    GuiControl,,% hFiltE1,% Filter_Command
  }
  return

DataFilterOk:
  Gui,4:submit
  return

ListGetSelected:
  if (A_GuiEvent="RightClick")
  {
    LV_Column:=LV_SubItemHitTest(E_LV)
    LV_Row:=LV_GetNext(RowNumber)
    if (LV_Column>5 and LV_Column<14 and !EN_Kurvendrucker)
      menu,ListRightClick,enable,Отрисовать график ;
    else
      menu,ListRightClick,disable,Отрисовать график ;
    menu,ListRightClick,show
  }
  return

ListCopySelected:
  LV_GetText(tmp1,LV_Row,LV_Column)
  clipboard:=tmp1
  return

Kurvendrucker:
  if SendDataRepitedly
  {
    GuiControl,,EN_Kurvendrucker,0
    gosub,SendDataRepitedly
    return
  }
  if MonitorVar
    MonitorVar:=0
  if (!Gdip_Initialised)
  {
    msgbox,Библиотека GDip++ не поддерживается в системе.
    return
  }
  Gui,1:submit,nohide
  AddCSLenght:=(AddCS>2 ? 2:AddCS-1)
  Monitorintext:=""
  if EN_Kurvendrucker
  {
    Graf_Start_Time:=a_tickcount
    gosub,GrafOpen
    GuiControlGet,E_pos,Pos,% E_LV
    GuiControlGet,T_pos,Pos,% E_Text
    tmp_Deltamx:=-(E_posw-10)
    if !((E_posw+tmp_Deltamx)<10 or (T_posw-tmp_Deltamx)<10)
    {
      MoveControl(E_LV,,,tmp_Deltamx,,0)
      MoveControl(hProgress,tmp_Deltamx,,,,0)
      MoveControl(hProgress2,tmp_Deltamx,,-tmp_Deltamx,,0)
      MoveControl(E_Text,tmp_Deltamx,,-tmp_Deltamx,,0)
      MoveControl(hGrafPos,tmp_Deltamx,,-tmp_Deltamx,,0)
      if (tmp_Deltamx>0)
        Gdip_X_Offset-=tmp_Deltamx*2
    }
;    GuiControl,Hide,% E_LV
;    GuiControl,Hide,% hProgress
    GuiControl,Hide,% hTtEButton
    GuiControl,Hide,% hClButton
    GuiControl,Hide,FollowSelect
    GuiControl,Choose,% hCS,1
    loop,% MaxNumOfGraf+1
    {
      i:=a_index-1
      GuiControl,Show,% hECB%i%
    }
    gosub,GrafUpdate
  }
  else
  {
;    GuiControl,show,% E_LV
;    GuiControl,show,% hProgress
    GuiControl,show,% hTtEButton
    GuiControl,show,% hClButton
    GuiControl,show,FollowSelect
    loop,% MaxNumOfGraf+1
    {
      i:=a_index-1
      GuiControl,Hide,% hECB%i%
    }
    gosub,GrafClose
    GuiControlGet,E_pos,Pos,% E_LV
    GuiControlGet,T_pos,Pos,% E_Text
    tmp_Deltamx:=300
    if !((E_posw+tmp_Deltamx)<10 or (T_posw-tmp_Deltamx)<10)
    {
      MoveControl(E_LV,,,tmp_Deltamx,,0)
      MoveControl(hProgress,tmp_Deltamx,,,,0)
      MoveControl(hProgress2,tmp_Deltamx,,-tmp_Deltamx,,0)
      MoveControl(E_Text,tmp_Deltamx,,-tmp_Deltamx,,0)
      MoveControl(hGrafPos,tmp_Deltamx,,-tmp_Deltamx,,0)
      if (tmp_Deltamx>0)
        Gdip_X_Offset-=tmp_Deltamx*2
    }
    gosub,GrafUpdate
  }
  return

GrafDrawFromSelected:
  if (!Gdip_Initialised)
  {
    msgbox,Библиотека GDip++ не поддерживается в системе.
    return
  }
  if AutoMinMax
    hi_lim:=1
  MonitorVar:=1
  gosub,GrafOpen

  Graf_Start_Time:=a_tickcount
  LV_GetText(Slave_Start_Byte,LV_Row,2)
  LV_GetText(Slave_Addres,LV_Row-Slave_Start_Byte,7)
  LV_GetText(Slave_Command,LV_Row-Slave_Start_Byte+1,7)
  LV_GetText(Slave_ByteNum,LV_Row-Slave_Start_Byte+2,7)
  Slave_ByteNum+=5
  if (LV_Column=Column_HEX)
  {
    Slave_Data_Type:="char"
    Slave_Len:=1
  }
  if (LV_Column=Column_DEC)
  {
    Slave_Data_Type:="uchar"
    Slave_Len:=1
  }
  if (LV_Column=Column_WORD)
  {
    Slave_Data_Type:="ushort"
    Slave_Len:=2
  }
  if (LV_Column=Column_INT)
  {
    Slave_Data_Type:="short"
    Slave_Len:=2
  }
  if (LV_Column=Column_DWORD)
  {
    Slave_Data_Type:="uint"
    Slave_Len:=4
  }
  if (LV_Column=Column_DINT)
  {
    Slave_Data_Type:="int"
    Slave_Len:=4
  }
  if (LV_Column=Column_Float)
  {
    Slave_Data_Type:="float"
    Slave_Len:=4
  }
  if (LV_Column=Column_Double)
  {
    Slave_Data_Type:="Double"
    Slave_Len:=8
  }
  return

GrafClose:
  if MonitorVar
    MonitorVar:=0
  GuiControlGet,E_pos,Pos,% E_Text
  GuiControlGet,T_pos,Pos,% hGrafPos
  tmpy:=T_posh-10
  MoveControl(E_Text,,,,tmpy,0)
  MoveControl(hProgress2,,tmpy,,,0)
  MoveControl(hGrafPos,,tmpy,,-tmpy,0)
  gosub,GrafRedraw
  return

GrafOpen:
  GuiControlGet,E_pos,Pos,% E_Text
  tmpy:=-E_posh+70
  MoveControl(E_Text,,,,tmpy,0)
  MoveControl(hProgress2,,tmpy,,,0)
  MoveControl(hGrafPos,,tmpy,,-tmpy,0)
  gosub,GrafCreateMenu
  gosub,GrafRedraw
  return

GrafClear:
  ArrClear(Monitor_value)
  ArrClear(Monitor_value_T)
  loop,% MaxNumOfGraf
    ArrClear(Monitor_value%a_index%)
  return

GrafCreateMenu:
  menu,GrafRightClick,DeleteAll
  menu,GrafRightClick,add,Автоматическое максимальное значение,GrafAutoMinMax
  menu,GrafRightClick,add,Задать максимальное значение вручную	F2,GrafChangeMinMax
  menu,GrafRightClick,add
  if EN_Kurvendrucker
  {
    menu,GrafRightClick,add,Автоматически определять разделитель графиков,GrafAutoEnding
    menu,GrafRightClick,add,Сбросить автоопределение разделителя	F5,GrafAutoEndingReset
    menu,GrafRightClick,add,Задать разделитель вручную	F3,GrafChangeEnding
    menu,GrafRightClick,add
    if AutoEnding
      menu,GrafRightClick,Check,Автоматически определять разделитель графиков ;
  }
  menu,GrafRightClick,add,Сглаживание "ступенек"	F4,GrafSmooth
  menu,GrafRightClick,add,Остановить прокрутку	F1,GrafPause
  menu,GrafRightClick,add
  menu,GrafRightClick,add,Очистить график,GrafClear
  menu,GrafRightClick,add
  menu,GrafRightClick,add,Увеличить	Колесико мыши или Ctrl+,ZoomIn
  menu,GrafRightClick,add,Уменьшить	Колесико мыши или Ctrl-,ZoomOut
  if MonitorVar
  {
    menu,GrafRightClick,add
    menu,GrafRightClick,add,Закрыть,GrafClose
  }
  if AutoMinMax
    menu,GrafRightClick,Check,Автоматическое максимальное значение ;
  if GrafSmooth
    menu,GrafRightClick,Check,Сглаживание "ступенек"	F4
  return

#If (WinActive("ahk_id " hMainWin) or WinActive("ahk_id " hGraf))
F4::
#if
GrafSmooth:
  if GrafSmooth:=!GrafSmooth
    menu,GrafRightClick,Check,Сглаживание "ступенек"	F4
  else
    menu,GrafRightClick,UnCheck,Сглаживание "ступенек"	F4
  if (a_tickcount-StartDrawTime>500)
    gosub,GrafRedraw
  return

GrafAutoMinMax:
  if AutoMinMax:=!AutoMinMax
  {
    hi_lim:=1
    menu,GrafRightClick,Check,Автоматическое максимальное значение ;
  }
  else
    menu,GrafRightClick,UnCheck,Автоматическое максимальное значение ;
  if (a_tickcount-StartDrawTime>500)
    gosub,GrafRedraw
  return

#If (WinActive("ahk_id " hMainWin) or WinActive("ahk_id " hGraf))
F2::
#if
GrafChangeMinMax:
  InputBox,tmp1,Задать максимальное значение вручную	F2,Введите положительное число,,240,130,,,,12,% hi_lim
  if !ErrorLevel
  {
    menu,GrafRightClick,UnCheck,Автоматическое максимальное значение ;
    AutoMinMax:=0
    hi_lim:=tmp1
  }
  if (a_tickcount-StartDrawTime>500)
    gosub,GrafRedraw
  return

GrafAutoEnding:
  if AutoEnding:=!AutoEnding
  {
    AutoEnding:=1
    EndSymbol:=""
    TryFind:=0
    menu,GrafRightClick,Check,Автоматически определять разделитель графиков ;
  }
  else
    menu,GrafRightClick,UnCheck,Автоматически определять разделитель графиков ;
  return

#If (EN_Kurvendrucker and (WinActive("ahk_id " hMainWin) or WinActive("ahk_id " hGraf)))
F5::
#if
GrafAutoEndingReset:
  EndSymbol:=""
  TryFind:=0
  return

#If (EN_Kurvendrucker and (WinActive("ahk_id " hMainWin) or WinActive("ahk_id " hGraf)))
F3::
#if
GrafChangeEnding:
  InputBox,tmp1,Задать разделитель вручную,Введите разделительный символ,,240,130,,,,12,% EndSymbol
  if !ErrorLevel
  {
    menu,GrafRightClick,UnCheck,Автоматически определять разделитель графиков ;
    AutoEnding:=0
    EndSymbol:=tmp1
  }
  return

#If (Monitorvalue or EN_Kurvendrucker) and (WinActive("ahk_id " hMainWin) or WinActive("ahk_id " hGraf))
F1::
#if
GrafPause:
  if GrafPause:=!GrafPause
  {
    GuiControl,,GrafPause,% GrafPause
    menu,GrafRightClick,Check,Остановить прокрутку	F1 ;
  }
  else
  {
    GuiControl,,GrafPause,% GrafPause
    menu,GrafRightClick,UnCheck,Остановить прокрутку	F1 ;
  }
  return

GrafDraw:
  StartDrawTime:=a_tickcount
GrafRedraw:
  if (Monitor_value_T.maxindex()<Gdip_W)
    Gdip_Points_Num:=Gdip_W
  else
    Gdip_Points_Num:=Monitor_value_T.maxindex()
  tmp0:=((Gdip_H//Gdip_ZoomY)//2)
  if (Gdip_Y_Offset<tmp0) ; верхний край
    Gdip_Y_Offset:=tmp0
  else if (Gdip_Y_Offset>Gdip_H-tmp0) ; нижний край
    Gdip_Y_Offset:=Gdip_H-tmp0
  if (Gdip_X_Offset>Gdip_Points_Num-Gdip_W/Gdip_ZoomX) ; левый край
    Gdip_X_Offset:=Gdip_Points_Num-Gdip_W/Gdip_ZoomX
  if (Gdip_X_Offset<0) ; правый край
    Gdip_X_Offset:=0
  Gdip_X_Offset:=round(Gdip_X_Offset)
  if AutoMinMax
  {
    if EN_Kurvendrucker
    {
      hi_lim:=0
      loop,% NumOfGraf
      {
        tmp1:=Ceil(ArrGetMax(Monitor_value%a_index%,Gdip_X_Offset,round(Gdip_W/Gdip_ZoomX)))
        if (hi_lim<tmp1)
          hi_lim:=tmp1
      }
    }
    else
      hi_lim:=Ceil(ArrGetMax(Monitor_value,Gdip_X_Offset,round(Gdip_W/Gdip_ZoomX)))
  }

  GuiControl,,Progress2,% round(100-Gdip_X_Offset/(Gdip_Points_Num-Gdip_W/Gdip_ZoomX)*100)
  ; очистить поле
  Gdip_FillRectangle(Main_Holst_G,Gdip_WhiteBrush,0,0,Gdip_Points_Num,Gdip_H)
  ; отрисовать сетку
  Gdip_DrawLine(Main_Holst_G,Gdip_GrayPen,0,Gdip_M
			  ,Gdip_Points_Num,Gdip_M)
  ; Отрисовать горизонтальные линии величин с текстом
  Grad:=10**NumOfSymbols:=(strlen(round(hi_lim))-1)
  GdipCoordX:=Gdip_Font_W ;Gdip_Points_Num-Gdip_Font_W*(NumOfSymbols+1)-10
  Gdip_Text_W:=Gdip_Font_W*NumOfSymbols
  loop,% round(hi_lim)//Grad
  {
    Val:=A_index*Grad
    PlussCoordY:=abs(round((Val/hi_lim)*Gdip_M)+Gdip_M-Gdip_H)
    MinusCoordY:=abs(round((-Val/hi_lim)*Gdip_M)+Gdip_M-Gdip_H)
    Gdip_FillRectangle(Main_Holst_G,Gdip_WhiteBrush2,GdipCoordX,PlussCoordY+1,Gdip_Text_W,Gdip_Font_H)
    Gdip_FillRectangle(Main_Holst_G,Gdip_WhiteBrush2,GdipCoordX,MinusCoordY-Gdip_Font_H,Gdip_Text_W,Gdip_Font_H)
    Gdip_TextToGraphics(Main_Holst_G,Val,"x" GdipCoordX " y" PlussCoordY+1 " Left cff000000 " Gdip_Font_Size,Gdip_Font)
    Gdip_TextToGraphics(Main_Holst_G,-Val,"x" GdipCoordX-10 " y" MinusCoordY-Gdip_Font_H " Left cff000000 " Gdip_Font_Size,Gdip_Font)
    Gdip_DrawLine(Main_Holst_G,Gdip_GrayPen2,0,PlussCoordY
			     ,Gdip_Points_Num,PlussCoordY)
    Gdip_DrawLine(Main_Holst_G,Gdip_GrayPen2,0,MinusCoordY
			     ,Gdip_Points_Num,MinusCoordY)
  }
; линия нуля
  Gdip_FillRectangle(Main_Holst_G,Gdip_WhiteBrush2,GdipCoordX,Gdip_M,Gdip_Font_W,Gdip_Font_H)
  Gdip_TextToGraphics(Main_Holst_G,0,"x" GdipCoordX " y" Gdip_M " Left cff000000 " Gdip_Font_Size,Gdip_Font)

; отрисовать синий график
  if MonitorVar
  {
    Gdip_Time_Stamp:=0
    Old_j:=0
    Smooth_GdipCoordX:=0
    Old_GdipCoordY:=abs(round((Monitorvalue/hi_lim)*(Gdip_M))+Gdip_M-Gdip_H) ;Gdip_M
    Old_Time:=Average_time_summ:=Average_time_Devider:=0
    Val_UnderGraf_Drawed:=0
    loop,% round(Gdip_W/Gdip_ZoomX)
    {
      i:=a_index
      j:=Monitor_value[i+Gdip_X_Offset]
      ; out=(inval/max)*(hi_lim-lo_lim)+lo_lim
      GdipCoordX:=round(Gdip_W-(i*Gdip_ZoomX))
      if (abs(j)<=abs(hi_lim))
        GdipCoordY:=abs(round((j/hi_lim)*(Gdip_M))+Gdip_M-Gdip_H)
      else if (j>0)
        GdipCoordY:=0
      else if (j<0)
        GdipCoordY:=Gdip_H
      if tmp0:=Monitor_value_T[i+Gdip_X_Offset]
      {
        RegExMatch(tmp0,"([\d]+).([\d]+).([\d]+).([\d]+)",tmp)
        Calc_Time:=(tmp1*3600+tmp2*60+tmp3)*1000+tmp4
        if Old_Time
        {
          Delta_Time:=Old_Time-Calc_Time
          Average_time_Devider++
          Average_time_summ:=Average_time_summ+Delta_Time
; отметить точки с задержкой
          if (Average_time and Delta_Time>Average_time and Old_GdipCoordX!=GdipCoordX)
            Gdip_DrawLine(Main_Holst_G,Gdip_GrayPen3,GdipCoordX+1,0
				      ,GdipCoordX+1,Gdip_H)
        }
        Old_Time:=Calc_Time
      }
      if (!GrafSmooth or (Old_j!=j))
      {
; линия синего графика
        Gdip_DrawLine(Main_Holst_G,Gdip_BluePen,round(GdipCoordX+Smooth_GdipCoordX+Gdip_ZoomX),Old_GdipCoordY,GdipCoordX,GdipCoordY)
        if (Gdip_ZoomX>7)
          Gdip_DrawEllipse(Main_Holst_G,Gdip_BluePen,GdipCoordX-1,GdipCoordY-1,2,2)
        Old_GdipCoordY:=GdipCoordY
        Old_j:=j
        Smooth_GdipCoordX:=0
      }
      else
        Smooth_GdipCoordX+=1*Gdip_ZoomX
      Gdip_Time_Stamp+=Gdip_ZoomX
      if (Gdip_Time_Stamp>10*Gdip_Font_W)
      {
        Gdip_Time_Stamp:=0
; линия и текст временой шкалы
        Gdip_DrawLine(Main_Holst_G,Gdip_GrayPen2,GdipCoordX+1,0
				  ,GdipCoordX+1,Gdip_H)
        tmp0:=Monitor_value_T[i+Gdip_X_Offset]
        if tmp0
        {
          StringTrimRight,tmp0,tmp0,4
          Gdip_FillRectangle(Main_Holst_G,Gdip_WhiteBrush2,GdipCoordX+1,Gdip_H-Gdip_Font_H
      						  ,Gdip_Font_W*(strlen(tmp0)),Gdip_Font_H)
          Gdip_TextToGraphics(Main_Holst_G,tmp0,"x" GdipCoordX+1 " y" Gdip_H-Gdip_Font_H " Left cff000000 " Gdip_Font_Size,Gdip_Font)
        }
      }
; текст значения под курсором
      if (!Val_UnderGraf_Drawed)
      if (UnderGraf_mx>=GdipCoordX and UnderGraf_mx<=GdipCoordX+10)
      if (UnderGraf_my>GdipCoordY-10 and UnderGraf_my<GdipCoordY+10)
      {
        Val_UnderGraf_Drawed:=1
        tmp1:=(GdipCoordY-Gdip_Font_H)<0?0:(GdipCoordY-Gdip_Font_H)
        Gdip_FillRectangle(Main_Holst_G,Gdip_WhiteBrush2,GdipCoordX,tmp1,Gdip_Font_W*(strlen(j)),Gdip_Font_H)
        Gdip_TextToGraphics(Main_Holst_G,j,"x" GdipCoordX " y" tmp1 " Left c" Gdip_Font_Selected_Colour " " Gdip_Font_Size,Gdip_Font)
      }
      Old_GdipCoordX:=GdipCoordX
    }
    Average_time:=Average_time_summ//Average_time_Devider*2

    if (Monitorvalue>=0)
      addY:=0
    else
      addY:=-Gdip_Font_H
    Old_GdipCoordY:=abs(round((Monitorvalue/hi_lim)*(Gdip_M))+Gdip_M-Gdip_H) ;Gdip_M
    GdipCoordX:=Gdip_W-Gdip_Font_W*(strlen(Monitorvalue))
; текст текущего значения
    Gdip_FillRectangle(Main_Holst_G,Gdip_WhiteBrush2,GdipCoordX,Old_GdipCoordY+1+addY,Gdip_Font_W*(strlen(Monitorvalue)),Gdip_Font_H)
    Gdip_TextToGraphics(Main_Holst_G,Monitorvalue,"x" GdipCoordX " y" Old_GdipCoordY+1+addY " Left cff000000 " Gdip_Font_Size,Gdip_Font)

    if (Gdip_Marker_mx>0 and Gdip_Marker_mx<=Gdip_W and Gdip_Marker_my>0 and Gdip_Marker_my<=Gdip_H)
    {
      j:=Monitor_value[round((Gdip_W-Gdip_Marker_mx)/Gdip_ZoomX+Gdip_X_Offset)]
      if (j>=0)
        addY:=0
      else
        addY:=-Gdip_Font_H
      if (abs(j)<=abs(hi_lim))
        GdipCoordY:=abs(round((j/hi_lim)*(Gdip_M))+Gdip_M-Gdip_H)
      else if (j>0)
        GdipCoordY:=0
      else if (j<0)
        GdipCoordY:=Gdip_H
; время значения выбранного мышкой
      t:=Monitor_value_T[round((Gdip_W-Gdip_Marker_mx)/Gdip_ZoomX+Gdip_X_Offset)]
      Gdip_FillRectangle(Main_Holst_G,Gdip_WhiteBrush2,Gdip_Marker_mx,1,Gdip_Font_W*(strlen(t)),Gdip_Font_H)
      Gdip_TextToGraphics(Main_Holst_G,t,"x" round(Gdip_Marker_mx) " y" 1 " Left c" Gdip_Font_Selected_Colour " " Gdip_Font_Size,Gdip_Font)
; линия и значение выбранное мышкой
      Gdip_DrawLine(Main_Holst_G,Gdip_GrayPen,Gdip_Marker_mx,0,Gdip_Marker_mx,Gdip_H)
      tmp1:=GdipCoordY+addY<Gdip_Font_H?Gdip_Font_H:GdipCoordY+addY
      Gdip_FillRectangle(Main_Holst_G,Gdip_WhiteBrush2,Gdip_Marker_mx,tmp1,Gdip_Font_W*(strlen(j)),Gdip_Font_H)
      Gdip_TextToGraphics(Main_Holst_G,j,"x" round(Gdip_Marker_mx) " y" tmp1 " Left c" Gdip_Font_Selected_Colour " " Gdip_Font_Size,Gdip_Font)
    }
  }
; отрисовать остальные графики
  if EN_Kurvendrucker
  {
    loop,% NumOfGraf
    {
      CurGN:=a_index
      if !EnableGraf%CurGN%
        continue
      Old_j:=0
      Smooth_GdipCoordX:=0
      Old_GdipCoordY:=abs(round((Monitorvalue/hi_lim)*(Gdip_M))+Gdip_M-Gdip_H) ;Gdip_M
      Val_UnderGraf_Drawed:=0
      loop,% round(Gdip_W/Gdip_ZoomX)
      {
        i:=a_index
        j:=Monitor_value%CurGN%[i+Gdip_X_Offset]
        ; out=(inval/max)*(hi_lim-lo_lim)+lo_lim
        GdipCoordX:=round(Gdip_W-(i*Gdip_ZoomX))
        if (abs(j)<=abs(hi_lim))
          GdipCoordY:=abs(round((j/hi_lim)*(Gdip_M))+Gdip_M-Gdip_H)
        else if (j>0)
          GdipCoordY:=0
        else if (j<0)
          GdipCoordY:=Gdip_H
        if (!GrafSmooth or (Old_j!=j))
        {
  ; линия остальных графиков
          Gdip_DrawLine(Main_Holst_G,Gdip_Pen%CurGN%,round(GdipCoordX+Smooth_GdipCoordX+Gdip_ZoomX),Old_GdipCoordY,GdipCoordX,GdipCoordY)
          if (Gdip_ZoomX>7)
            Gdip_DrawEllipse(Main_Holst_G,Gdip_Pen%CurGN%,GdipCoordX-2,GdipCoordY-2,4,4)
          Old_GdipCoordY:=GdipCoordY
          Old_j:=j
          Smooth_GdipCoordX:=0
        }
        else
          Smooth_GdipCoordX+=1*Gdip_ZoomX
; текст значения под курсором
        if (!Val_UnderGraf_Drawed)
        if (UnderGraf_mx>=GdipCoordX and UnderGraf_mx<=GdipCoordX+10)
        if (UnderGraf_my>GdipCoordY-10 and UnderGraf_my<GdipCoordY+10)
        {
          Val_UnderGraf_Drawed:=1
          tmp1:=(GdipCoordY-Gdip_Font_H)<0?0:(GdipCoordY-Gdip_Font_H)
          Gdip_FillRectangle(Main_Holst_G,Gdip_Brush%CurGN%,GdipCoordX,tmp1,Gdip_Font_W*(strlen(j)),Gdip_Font_H)
          Gdip_TextToGraphics(Main_Holst_G,j,"x" GdipCoordX " y" tmp1 " Left c" Gdip_Font_Selected_Colour " " Gdip_Font_Size,Gdip_Font)
        }
      }
      if (Monitorvalue>=0)
        addY:=0
      else
        addY:=-Gdip_Font_H
      j:=Monitor_value%CurGN%[Gdip_X_Offset]
      Old_GdipCoordY:=abs(round((j/hi_lim)*(Gdip_M))+Gdip_M-Gdip_H) ;Gdip_M
      GdipCoordX:=Gdip_W-Gdip_Font_W*(strlen(j))
  ; текст текущего значения
;      Gdip_FillRectangle(Main_Holst_G,Gdip_WhiteBrush2,GdipCoordX,Old_GdipCoordY+1+addY,Gdip_Font_W*(strlen(j)),Gdip_Font_H)
;      Gdip_TextToGraphics(Main_Holst_G,j,"x" GdipCoordX " y" Old_GdipCoordY+1+addY " Left cff000000 " Gdip_Font_Size,Gdip_Font)

      if (Gdip_Marker_mx>0 and Gdip_Marker_mx<=Gdip_W and Gdip_Marker_my>0 and Gdip_Marker_my<=Gdip_H)
      {
        j:=Monitor_value%CurGN%[round((Gdip_W-Gdip_Marker_mx)/Gdip_ZoomX+Gdip_X_Offset)]
        if (j>=0)
          addY:=0
        else
          addY:=-Gdip_Font_H
        if (abs(j)<=abs(hi_lim))
          GdipCoordY:=abs(round((j/hi_lim)*(Gdip_M))+Gdip_M-Gdip_H)
        else if (j>0)
          GdipCoordY:=0
        else if (j<0)
          GdipCoordY:=Gdip_H
; время значения выбранного мышкой
        t:=Monitor_value_T[round((Gdip_W-Gdip_Marker_mx)/Gdip_ZoomX+Gdip_X_Offset)]
        Gdip_FillRectangle(Main_Holst_G,Gdip_WhiteBrush2,Gdip_Marker_mx,1,Gdip_Font_W*(strlen(t)),Gdip_Font_H)
        Gdip_TextToGraphics(Main_Holst_G,t,"x" round(Gdip_Marker_mx) " y" 1 " Left c" Gdip_Font_Selected_Colour " " Gdip_Font_Size,Gdip_Font)
  ; линия и значение выбранное мышкой
        if (j!="")
          tmp1:=CurGN "=" j
        else
          tmp1:=""
        Gdip_DrawLine(Main_Holst_G,Gdip_GrayPen,Gdip_Marker_mx,0,Gdip_Marker_mx,Gdip_H)
        Gdip_FillRectangle(Main_Holst_G,Gdip_Brush%CurGN%,Gdip_Marker_mx,Gdip_Font_H*CurGN,Gdip_Font_W*(strlen(tmp1)),Gdip_Font_H)
        Gdip_TextToGraphics(Main_Holst_G,tmp1,"x" round(Gdip_Marker_mx) " y" Gdip_Font_H*CurGN " Left c" Gdip_Font_Selected_Colour " " Gdip_Font_Size,Gdip_Font)
      }
    }
      Gdip_Time_Stamp:=0
      loop,% round(Gdip_W/Gdip_ZoomX)
      {
        i:=a_index
        Gdip_Time_Stamp+=Gdip_ZoomX
        if (Gdip_Time_Stamp>10*Gdip_Font_W)
        {
          GdipCoordX:=round(Gdip_W-(i*Gdip_ZoomX))
          Gdip_Time_Stamp:=0
          Gdip_DrawLine(Main_Holst_G,Gdip_GrayPen2,GdipCoordX+1,0
  				  ,GdipCoordX+1,Gdip_H)
          tmp0:=Monitor_value_T[i+Gdip_X_Offset]
          if tmp0
          {
  ; текст временой шкалы
            StringTrimRight,tmp0,tmp0,4
            Gdip_FillRectangle(Main_Holst_G,Gdip_WhiteBrush2,GdipCoordX+1,Gdip_H-Gdip_Font_H
        						  ,Gdip_Font_W*(strlen(tmp0)),Gdip_Font_H)
            Gdip_TextToGraphics(Main_Holst_G,tmp0,"x" GdipCoordX+1 " y" Gdip_H-Gdip_Font_H " Left cff000000 " Gdip_Font_Size,Gdip_Font)
          }
        }
      }
  }

GrafUpdate:
  ; Обновить отображение
  ; Скопировать отрисованный график
  if (a_osversion~="10")
    GuiControlGet,Gdip_,Pos,% hGrafPos
  else
    wingetpos,Gdip_X,Gdip_Y,Gdip_W,Gdip_H,% "ahk_id" hGrafPos
  Gdip_M:=Gdip_H//2
  if (MonitorVar or EN_Kurvendrucker)
    UpdateLayeredWindow(hGraf,Main_Holst,Gdip_X,Gdip_Y,Gdip_W,Gdip_H+1)
  else
    UpdateLayeredWindow(hGraf,Main_Holst,Gdip_X,Gdip_Y,Gdip_W,Gdip_H+1,0)
  DrawTime:=a_tickcount-StartDrawTime
;tooltip,% DrawTime "`n" Monitor_value_T.maxindex(),500,0,9
  UnderGraf_MouseMove:=0
  return

GuiSize:
  if (!Old_GuiWidth or !Old_GuiHeight)
  {
    Old_GuiWidth:=A_GuiWidth
    Old_GuiHeight:=A_GuiHeight
  }
  if (A_EventInfo=1)
    return
  DeltaGuiWidth:=A_GuiWidth-Old_GuiWidth
  DeltaGuiHeight:=A_GuiHeight-Old_GuiHeight
  if (MonitorVar or EN_Kurvendrucker)
  {
    hGrafPos_over:=MoveControl(hGrafPos,,,DeltaGuiWidth,DeltaGuiHeight)
    E_Text_over:=MoveControl(E_Text,,,DeltaGuiWidth,((hGrafPos_over&2)?DeltaGuiHeight:0))
    hProgress2_over:=MoveControl(hProgress2,,((hGrafPos_over&2)?DeltaGuiHeight:0),DeltaGuiWidth)
  }
  else
  {
    E_Text_over:=MoveControl(E_Text,,,DeltaGuiWidth,DeltaGuiHeight)
    hProgress2_over:=MoveControl(hProgress2,,DeltaGuiHeight,DeltaGuiWidth)
    hGrafPos_over:=MoveControl(hGrafPos,,DeltaGuiHeight,DeltaGuiWidth)
  }
  MoveControl(hGroupBufer,,,DeltaGuiWidth,DeltaGuiHeight)
  MoveControl(hClButton,,DeltaGuiHeight,DeltaGuiWidth)
  MoveControl(hTtEButton,DeltaGuiWidth,DeltaGuiHeight)
  MoveControl(E_LV,,,((E_Text_over&1)?DeltaGuiWidth:0),DeltaGuiHeight)
  MoveControl(hProgress,((E_Text_over&1)?DeltaGuiWidth:0),,,DeltaGuiHeight)
  loop,% MaxNumOfGraf+1
  {
    i:=a_index-1
    MoveControl(hECB%i%,,DeltaGuiHeight)
  }
;tooltip,% "dw" DeltaGuiWidth "`ndh" DeltaGuiHeight "`nw" A_GuiWidth "`nh" A_GuiHeight
  Old_GuiWidth:=A_GuiWidth
  Old_GuiHeight:=A_GuiHeight
  return

#If (WinActive("ahk_id " hMainWin) or WinActive("ahk_id " hGraf))
~$LButton::
  mousegetpos,mx,my,mw,mc
  oldmx:=mx
  oldmy:=my
  oldmc:=mc
  oldmw:=mw
  if (mw=hGraf)
  {
    Gdip_Marker_mx:=mx-Gdip_X
    Gdip_Marker_my:=my-Gdip_Y
  }
  if (MouseAbovehMain)
  {
    ControlGet,hElement,hwnd,,%mc%,ahk_id %mw%
    if (hElement=hProgress)
      EditResize:=1
    if (hElement=hProgress2)
      EditResize:=2
    if (hElement=E_LV or hElement=E_LV2)
      ControlFocus,,ahk_id %hElement%
  }
  return

~$LButton up::
  EndResizing:
  EditResize:=0
  if (a_tickcount-StartDrawTime>500)
    gosub,GrafRedraw
  return

~$RButton::
  mousegetpos,mx,my,mw,mc
  ControlGet,hElement,hwnd,,%mc%,ahk_id %mw%
  if (mw=hGraf)
    menu,GrafRightClick,show
  if (hElement=hProgress2)
  {
    if (a_osversion~="10")
      GuiControlGet,E_pos,Pos,% hProgress2
    else
      wingetpos,E_posX,E_posY,E_posW,E_posH,% "ahk_id" hProgress2
    ClickPos:=(E_posw-(mx-E_posx))/E_posw*100
    tmp1:=round(Gdip_Points_Num-Gdip_W/Gdip_ZoomX)
    Gdip_X_Offset:=ClickPos*tmp1/100
    if (a_tickcount-StartDrawTime>500)
      gosub,GrafRedraw
  }
  loop,% MaxNumOfGraf
  {
    i:=a_index
    if (hElement=hECB%i%)
    {
      if (EnableGraf%i%)
      {
        loop,% MaxNumOfGraf
          if (a_index!=i)
            GuiControl,,EnableGraf%a_index%,0
      }
      else
      {
        loop,% MaxNumOfGraf
          GuiControl,,EnableGraf%a_index%,1
      }
      gosub,Submit2
      break
    }
  }
  return


*~$Right::
  if WinActive("Ahk_id " hGraf)
  {
    if getkeystate("Ctrl","P")
      Gdip_X_Offset-=20
    Gdip_Marker_mx+=20
    if (a_tickcount-StartDrawTime>500)
      gosub,GrafRedraw
  }
  return

*~$Left::
  if WinActive("Ahk_id " hGraf)
  {
    if getkeystate("Ctrl","P")
      Gdip_X_Offset+=5
    Gdip_Marker_mx-=5
    if (a_tickcount-StartDrawTime>500)
      gosub,GrafRedraw
  }
  return
#if

ZoomIn:
#If (WinActive("ahk_id " hMainWin) or WinActive("ahk_id " hGraf))
^sc00D::
#if (MouseAbovehGraf and (MonitorVar or EN_Kurvendrucker))
~$WheelUp::
#if
  if (Gdip_ZoomX>=1)
    Gdip_ZoomX+=1
  else
    Gdip_ZoomX+=0.1
  if (Gdip_ZoomX>10)
    Gdip_ZoomX:=10
  Gdip_ZoomY+=1
  if (Gdip_ZoomY>10)
    Gdip_ZoomY:=10
  if (a_tickcount-StartDrawTime>500)
    gosub,GrafRedraw
  return

ZoomOut:
#If (WinActive("ahk_id " hMainWin) or WinActive("ahk_id " hGraf))
^sc00C::
#if (MouseAbovehGraf and (MonitorVar or EN_Kurvendrucker))
~$WheelDown::
#if
  if (Gdip_ZoomX>1)
    Gdip_ZoomX-=1
  else
    Gdip_ZoomX-=0.1
  if (Gdip_ZoomX<0.1)
    Gdip_ZoomX:=0.1
  Gdip_ZoomY-=1
  if (Gdip_ZoomY<1)
    Gdip_ZoomY:=1
  if (a_tickcount-StartDrawTime>500)
    gosub,GrafRedraw
  return

#if
#If (WinActive("ahk_id " hMainWin) or WinActive("ahk_id " hGraf))
esc::
#if
EXIT:
guiclose:
  DllCall("SystemParametersInfo",UInt,0x57,UInt,0,UInt,0,UInt,0)
  if Gdip_Initialised
  {
    Gdip_DeletePen(Gdip_WhitePen)
    Gdip_DeletePen(Gdip_GrayPen)
    Gdip_DeletePen(Gdip_GrayPen2)
    Gdip_DeletePen(Gdip_BluePen)
    Gdip_DeletePen(Gdip_BlackPen)
    loop,% MaxNumOfGraf
      Gdip_DeletePen(Gdip_Pen%a_index%)
    Gdip_DeleteBrush(Gdip_WhiteBrush)
    Gdip_DeleteBrush(Gdip_WhiteBrush2)
    Gdip_DeleteBrush(Gdip_BlackBrush)
    SelectObject(Main_Holst,Gdip_obm2), DeleteObject(Gdip_hbm2), DeleteDC(Main_Holst), Gdip_DeleteGraphics(Main_Holst_G)
    Gdip_Shutdown(Gdip_Initialised)
  }
  RS232_Close(RS232_FileHandle)
  exitapp

; ////////// отработанные линии________ctrl+Shift+win+alt+L ____________________HOTKEYS_________________________
#If (WinActive("ahk_id " hMainWin) or WinActive("ahk_id " hGraf))
^!+#vk4c::
  ListLines
  return
#if

WM_MOVING()
{
  global
  if (MonitorVar or EN_Kurvendrucker)
    gosub,GrafUpdate
}

WM_MOUSEMOVE:
  mousegetpos,mx,my,mw,mc
  if (mx!=oldmx or my!=oldmy)
  {
    Deltamx:=mx-oldmx
    Deltamy:=my-oldmy
    wingetpos,Gdip_X_,Gdip_Y_,Gdip_W_,Gdip_H_,% "ahk_id" hGrafPos
    UnderGraf_mx:=mx-Gdip_X_
    UnderGraf_my:=my-Gdip_Y_
    UnderGraf_MouseMove:=1
    MouseAbovehGraf:=(mw=hGraf)
    MouseAbovehMain:=(mw=hMainWin)
    if (MouseAbovehMain and oldmc!=mc)
    {
      ControlGet,hElement,hwnd,,%mc%,ahk_id %mw%
      SB_SetText(E%hElement%)
    }
    if (MouseAbovehMain or MouseAbovehGraf)
    {
      ControlGet,hElement,hwnd,,%mc%,ahk_id %mw%
      if (!ChangeCursorOnce and hElement=hProgress)
      {
        ChangeCursorOnce=1
        DllCall("SetSystemCursor",ptr,IDC_SIZEWE,Int,32512)
      }
      if (!ChangeCursorOnce2 and hElement=hProgress2)
      {
        ChangeCursorOnce2=1
        DllCall("SetSystemCursor",ptr,IDC_SIZEALL,Int,32512)
      }
    }
    if (ChangeCursorOnce and (hElement!=hProgress or !MouseAbovehMain))
    {
      ChangeCursorOnce=0
      DllCall("SystemParametersInfo",UInt,0x57,UInt,0,UInt,0,UInt,0)
    }
    if (ChangeCursorOnce2 and (hElement!=hProgress2 or !MouseAbovehMain))
    {
      ChangeCursorOnce2=0
      DllCall("SystemParametersInfo",UInt,0x57,UInt,0,UInt,0,UInt,0)
    }
    if (EditResize)
    {
      GuiControlGet,E_pos,Pos,% E_LV
      GuiControlGet,T_pos,Pos,% E_Text
      if !((E_posw+Deltamx)<10 or (T_posw-Deltamx)<10)
      {
        MoveControl(E_LV,,,Deltamx,,0)
        MoveControl(hProgress,Deltamx,,,,0)
        MoveControl(hProgress2,Deltamx,,-Deltamx,,0)
        MoveControl(E_Text,Deltamx,,-Deltamx,,0)
        MoveControl(hGrafPos,Deltamx,,-Deltamx,,0)
        if (Deltamx>0)
          Gdip_X_Offset-=Deltamx*2
        gosub,GrafUpdate
      }
    }
    if (EditResize=2)
    {
      GuiControlGet,E_pos,Pos,% E_Text
      GuiControlGet,T_pos,Pos,% hGrafPos
      if !((E_posh+Deltamy)<10 or (T_posh-Deltamy)<10)
      {
        MoveControl(E_Text,,,,Deltamy,0)
        MoveControl(hProgress2,,Deltamy,,,0)
        MoveControl(hGrafPos,,Deltamy,,-Deltamy,0)
        gosub,GrafUpdate
      }
    }

    Deltamx:=(mx-oldmx)*((3-Gdip_ZoomX<1) ? 1 : 3-Gdip_ZoomX)
    Deltamy:=(my-oldmy)*((3-Gdip_ZoomY<1) ? 1 : 3-Gdip_ZoomY)
    if (MouseAbovehGraf and getkeystate("LButton","P"))
    {
      Gdip_Y_Offset-=Deltamy
      Gdip_X_Offset+=Deltamx
      Gdip_Marker_mx+=Deltamx
      if (a_tickcount-StartDrawTime>500)
        gosub,GrafRedraw
    }
    if (MouseAbovehGraf and (oldmx!=mx or oldmy!=my) and a_tickcount-StartDrawTime>500)
    {
      SB_SetText("Дополнительное меню правой кнопкой мыши. Масштаб = " round(Gdip_ZoomX,1) ". Максимальное значение = " hi_lim ". Время отрисовки кадра = " DrawTime "ms. Чило точек = " Monitor_value_T.maxindex() ".")
      gosub,GrafRedraw
    }
  }
  oldmx:=mx
  oldmy:=my
  oldmc:=mc
  oldmw:=mw
  return

GetClientPos(hwnd)
{
  VarSetCapacity(pwi, 60, 0), NumPut(60, pwi, 0, "UInt")
  DllCall("GetWindowInfo", "Ptr", hwnd, "UInt", &pwi)
  top := NumGet(pwi, 24, "Int") - NumGet(pwi, 8, "Int")
  left := NumGet(pwi, 52, "Int")
  w := NumGet(pwi, 28, "Int") - NumGet(pwi, 20, "Int")
  h := NumGet(pwi, 32, "Int") - NumGet(pwi, 24, "Int")
  return {"top":top,"left":left,"w":w,"h":h}
}

SendToTerminal(RS232_FileHandle,Addres=1,command=1,Dig0=0,Dig1=0,Dig2=0,Dig3=0,Dig4=0,Dig5=0)
{
  varsetcapacity(Out_Data,9)
  NumPut(Addres,Out_Data,0,"Uchar")
  NumPut(command,Out_Data,1,"Uchar")
  NumPut(Dig0,Out_Data,2,"Uchar")
  NumPut(Dig1,Out_Data,3,"Uchar")
  NumPut(Dig2,Out_Data,4,"Uchar")
  NumPut(Dig3,Out_Data,5,"Uchar")
  NumPut(Dig4,Out_Data,6,"Uchar")
  NumPut(Dig5,Out_Data,7,"Uchar")
  NumPut(CRC16(Out_Data,8),Out_Data,8,"Uchar")
  RS232_Write(RS232_FileHandle,&Out_Data,9)
}
CRC16(byref data,size)
{
  loop,% size
    sum+=numget(data,a_index-1,"uchar")
  return sum
}
CRC_RTU(byref data,size)
{
  y:=0xFFFF
  loop,% size
  {
    z:=y^numget(data,a_index-1,"uchar")
    loop,8
    {
      r:=z&1
      z>>=1
      if r
        z^=0xA001
    }
    y:=z
  }
  a:=z//256
  b:=mod(z,256)
  return (a<<8)|b
  ;1) y=65535;
  ;2) загрузить в переменную x первый байт из потока данных;
  ;3) вычислить переменную z = ИСКЛЮЧАЮЩЕЕ ИЛИ y c байтом потока данных x.
  ;4) сдвинуть переменную z на один бит вправо
  ;5) если сдвинутый бит равен 1 то вычислить переменную z:
  ;z= ИСКЛЮЧАЮЩЕЕ ИЛИ z с числом 0xA001
  ;6) повторять шаг 4 и шаг 5 еще 7 раз.
  ;7) y=z
  ;8) если все байты из потока данных обработаны перейти на шаг 10
  ;9) перейти на шаг 2
  ;10) вычислить первый и второй байты CRC16-кода a и b.
  ;a=z/256(нацело)
  ;b= остаток от деления z/256.
}

SwapByte(var)
{
  tmp1:=(var<<8)&0xff00
  tmp2:=(var>>8)
  return (tmp1|tmp2)
}

SeachCOM(name,list=0)
{
  Loop,HKLM,HARDWARE\DEVICEMAP\SERIALCOMM\
  {
    RegRead,COMPort
    com.=COMPort "|"
  }
  loop,parse,com,|
  {
    if !A_loopfield
      continue
    COMPort:=A_loopfield
    Loop,HKLM,SYSTEM\CurrentControlSet\Enum,1,1
    {
      if (A_LoopRegName="PortName")
      {
        RegRead,Outputvar
        if (Outputvar=COMPort)
        {
          RegRead,FriendlyName,% A_LoopRegKey,% RegExReplace(A_LoopRegSubKey, "(.*)\\Device Parameters", "$1"),FriendlyName
          if (!list and InStr(FriendlyName,name))
            return COMPort
          if list
            out.=COMPort "`t" FriendlyName "`n"
        }
      }
    }
  }
  if list
    return out
}

RS232_Get_FileHandle(RS232_Port="COM1",RS232_Baud=2400,RS232_Parity="E",RS232_Data=8,RS232_Stop=2)
{
; COMx[:][baud=b][parity=p][data=d][stop=s][to={on|off}][xon={on|off}][odsr={on|off}][octs={on|off}][dtr={on|off|hs}][rts={on|off|hs|tg}][idsr={on|off}]
  RS232_Settings=%RS232_Port%:baud=%RS232_Baud% parity=%RS232_Parity% data=%RS232_Data% stop=%RS232_Stop% to=off xon=off odsr=off octs=off dtr=Off rts=off idsr=off

  ;###### Extract/Format the RS232 COM Port Number ######
  ;7/23/08 Thanks krisky68 for finding/solving the bug in which RS232 COM Ports greater than 9 didn't work.
  StringSplit, RS232_Temp, RS232_Settings, `:
  RS232_Temp1_Len := StrLen(RS232_Temp1)  ;For COM Ports > 9 \\.\ needs to prepended to the COM Port name.
  If (RS232_Temp1_Len > 4)                ;So the valid names are
    RS232_COM = \\.\%RS232_Temp1%         ; ... COM8  COM9   \\.\COM10  \\.\COM11  \\.\COM12 and so on...
  Else
    RS232_COM = %RS232_Temp1%

  ;8/10/09 A BIG Thanks to trenton_xavier for figuring out how to make COM Ports greater than 9 work for USB-Serial Dongles.
  StringTrimLeft, RS232_Settings, RS232_Settings, RS232_Temp1_Len+1 ;Remove the COM number (+1 for the semicolon) for BuildCommDCB.
  ;MsgBox, RS232_COM=%RS232_COM% `nRS232_Settings=%RS232_Settings%

  ;###### Build RS232 COM DCB ######
  ;Creates the structure that contains the RS232 COM Port number, baud rate,...
  VarSetCapacity(DCB, 28, 0)
  BCD_Result := DllCall("BuildCommDCB"
       ,"str" , RS232_Settings ;lpDef
       ,"ptr", &DCB)        ;lpDCB
;  If (!BCD_Result)
;  {
;    MsgBox, There is a problem with Serial Port communication. `nFailed Dll BuildCommDCB, BCD_Result=%BCD_Result% `nThe Script Will Now COMFail.`n %DCB%
;    COMFail=1
;    return
;  }

  ;###### Create RS232 COM File ######
  ;Creates the RS232 COM Port File Handle
  RS232_FileHandle := DllCall("CreateFile"
       ,"Str" , RS232_COM     ;File Name
       ,"UInt", 0xC0000000   ;Desired Access
       ,"UInt", 3            ;Safe Mode
       ,"UInt", 0            ;Security Attributes
       ,"UInt", 3            ;Creation Disposition
       ,"UInt", 0            ;Flags And Attributes
       ,"ptr", 0            ;Template File
       ,"Cdecl Int")

  If (!RS232_FileHandle)
  {
;;    MsgBox, There is a problem with Serial Port communication. `nFailed Dll CreateFile, RS232_FileHandle=%RS232_FileHandle% `nThe Script Will Now COMFail.
    COMFail=1
    return
  }

  ;###### Set COM State ######
  ;Sets the RS232 COM Port number, baud rate,...
  SCS_Result := DllCall("SetCommState"
       ,"ptr", RS232_FileHandle ;File Handle
       ,"ptr", &DCB)          ;Pointer to DCB structure
  If (!SCS_Result)
  {
;;    MsgBox, There is a problem with Serial Port communication. `nFailed Dll SetCommState, SCS_Result=%SCS_Result% `nThe Script Will Now COMFail.
    RS232_Close(RS232_FileHandle)
    COMFail=1
    return
  }
  Timeout:=Ceil(((RS232_Data+4)/RS232_Baud)*1000)
;tooltip,% RS232_Baud "`n" Timeout "`n" (((RS232_Data+4)/RS232_Baud)*1000),0,500,5
  ;###### Create the SetCommTimeouts Structure ######
  ReadIntervalTimeout        := 0           ; ms, max Interval betwen characters
  ReadTotalTimeoutMultiplier := Timeout     ; ms * reqested number of bytes
  ReadTotalTimeoutConstant   := 0           ; ms
  WriteTotalTimeoutMultiplier:= Timeout     ; ms * sended number of bytes
  WriteTotalTimeoutConstant  := 0           ; ms

  VarSetCapacity(Data, 20, 0) ; 5 * sizeof(DWORD)
  NumPut(ReadIntervalTimeout,         Data,  0, "UInt")
  NumPut(ReadTotalTimeoutMultiplier,  Data,  4, "UInt")
  NumPut(ReadTotalTimeoutConstant,    Data,  8, "UInt")
  NumPut(WriteTotalTimeoutMultiplier, Data, 12, "UInt")
  NumPut(WriteTotalTimeoutConstant,   Data, 16, "UInt")

  ;###### Set the RS232 COM Timeouts ######
  SCT_result := DllCall("SetCommTimeouts"
     ,"ptr", RS232_FileHandle ;File Handle
     ,"ptr", &Data)         ;Pointer to the data structure
  If !SCT_result
  {
;;    MsgBox, There is a problem with Serial Port communication. `nFailed Dll SetCommState, SCT_result=%SCT_result% `nThe Script Will Now COMFail.
    RS232_Close(RS232_FileHandle)
    COMFail=1
    return
  }
  Return %RS232_FileHandle%
}

RS232_Close(RS232_FileHandle)
{
  return DllCall("CloseHandle","ptr",RS232_FileHandle)
}

RS232_Write(RS232_FileHandle,Data,Data_Length)
{
  if RS232_FileHandle
  WF_Result := DllCall("WriteFile"
       ,"ptr" , RS232_FileHandle ;File Handle
       ,"ptr" , Data          ;Pointer to string to send
       ,"UInt", Data_Length    ;Data Length
       ,"ptr*", Bytes_Sent     ;Returns pointer to num bytes sent
       ,"Int" , "NULL")
;  RS232_WaitCommEvent(RS232_FileHandle,0x4)
}

RS232_Read(RS232_FileHandle,Data_Length,ByRef RS232_Bytes_Received,Byref D)
{
;  SetFormat,Integer,HEX
  VarSetCapacity(Data,Data_Length,0xff)
  if RS232_FileHandle
  Read_Result := DllCall("ReadFile"
       ,"ptr" , RS232_FileHandle   ; hFile
       ,"ptr"  , &Data             ; lpBuffer
       ,"Int"  , Data_Length        ; nNumberOfBytesToRead
       ,"ptr*", RS232_Bytes_Received   ; lpNumberOfBytesReceived
       ,"Int"  , 0)               ; lpOverlapped
  If (!Read_Result)
  {
    RS232_Close(RS232_FileHandle)
    COMFail=1
    return
  }
  D:=Data
}

RS232_WaitCommEvent(RS232_FileHandle,Event=0x3ff)
{
/*
Events:
EV_BREAK:=0x0040 ;A break was detected on input.
EV_CTS:=0x0008 ;The CTS (clear-to-send) signal changed state.
EV_DSR:=0x0010 ;The DSR (data-set-ready) signal changed state.
EV_ERR:=0x0080 ;A line-status error occurred. Line-status errors are CE_FRAME, CE_OVERRUN, and CE_RXPARITY.
EV_RING:=0x0100 ;A ring indicator was detected.
EV_RLSD:=0x0020 ;The RLSD (receive-line-signal-detect) signal changed state.
EV_RXCHAR:=0x0001 ;A character was received and placed in the input buffer.
EV_RXFLAG:=0x0002 ;The event character was received and placed in the input buffer. The event character is specified in the device's DCB structure, which is applied to a serial port by using the SetCommState function.
EV_TXEMPTY:=0x0004 ;The last character in the output buffer was sent.
*/
  MaskSet := DllCall("SetCommMask"
       ,"ptr" , RS232_FileHandle ;File Handle
       ,"int" , Event)          ;Mask
  if !MaskSet
  {
    RS232_Close(RS232_FileHandle)
    COMFail=1
    return
  }
  VarSetCapacity(Data,4,0)
  if RS232_FileHandle
  Success := DllCall("WaitCommEvent"
       ,"ptr", RS232_FileHandle ;File Handle
       ,"ptr", &Data          ;Pointer to Event
       ,"ptr", 0)
  if !Success
  {
    RS232_Close(RS232_FileHandle)
    COMFail=1
    return
  }
;  tooltip,% Errorlevel "`n" a_lasterror "`n" Success "`nEvent = " format("0x{:04x}",numget(Data,0,"Int64"))
}

LV_SubitemHitTest(HLV) {
   ; To run this with AHK_Basic change all DllCall types "Ptr" to "UInt", please.
   ; HLV - ListView's HWND
   LVM_SUBITEMHITTEST := 0x1039
   VarSetCapacity(POINT, 8, 0)
   ; Get the current cursor position in screen coordinates
   DllCall("User32.dll\GetCursorPos", "Ptr", &POINT)
   ; Convert them to client coordinates related to the ListView
   DllCall("User32.dll\ScreenToClient", "Ptr", HLV, "Ptr", &POINT)
   ; Create a LVHITTESTINFO structure (see below)
   VarSetCapacity(LVHITTESTINFO, 24, 0)
   ; Store the relative mouse coordinates
   NumPut(NumGet(POINT, 0, "Int"), LVHITTESTINFO, 0, "Int")
   NumPut(NumGet(POINT, 4, "Int"), LVHITTESTINFO, 4, "Int")
   ; Send a LVM_SUBITEMHITTEST to the ListView
   SendMessage, LVM_SUBITEMHITTEST, 0, &LVHITTESTINFO, , ahk_id %HLV%
   ; If no item was found on this position, the return value is -1
   If (ErrorLevel = -1)
      Return 0
   ; Get the corresponding subitem (column)
   Subitem := NumGet(LVHITTESTINFO, 16, "Int") + 1
   Return Subitem
}

SetParent(hwnd,hNewParent,ClassNN="",x=0,y=0)
{
  if ClassNN
    ControlGet,hNewParent,Hwnd,,%ClassNN%,ahk_id %hNewParent%
  oldParent:=DllCall("SetParent","ptr",hwnd,"ptr",hNewParent)
  WinMove,ahk_id %hwnd%,,%x%,%y%
  return oldParent
}

StrCut(byref string,sym)
{
  if Pos:=instr(string,sym)
  {
    stringleft,var,string,Pos-1
    stringtrimleft,string,string,Pos
    return var
  }
}

findMetaSbl(intext,byref Splitter,byref Ending)
{
  a:={}
  loop,parse,intext
  {
    if !(a_loopfield+1 or a_loopfield="-" or a_loopfield=".")
    {
      SmblExist:=0
      for i,n in a
      {
        if (i=a_loopfield)
        {
          SmblExist:=1
          a[i]++
          break
        }
      }
      if !SmblExist
        a[a_loopfield]:=1
    }
  }
  countS:=0
  countE:=32000
  for i,n in a
  {
    if (n>countS)
    {
      Splitter:=i
      countS:=n
    }
    if (n<countE)
    {
      Ending:=i
      countE:=n
    }
  }
}

CheckMetaSym(sym)
{
  static Meta:="\`n.*?+[{|()^$"
  if instr(Meta,sym)
    loop,parse,Meta
    {
      if (a_loopfield="`n" and instr(sym,a_loopfield))
        sym:="\n"
      else
        stringreplace,sym,sym,%a_loopfield%,\%a_loopfield%,all
    }
  return sym
}

AddAdd(byref var)
{
  if (var>=0)
    var:="+"var
}

SwapData(var_in,StartByte,NumBytes,byref var_out)
{
  varsetcapacity(var_out,NumBytes)
  loop,% NumBytes
    numput(numget(var_in,StartByte+a_index-1,"uchar"),var_out,NumBytes-a_index,"uchar")
}

CopyData(var_in,StartByte,NumBytes,var_out,out_StartByte)
{
  loop,% NumBytes
    numput(numget(var_in,StartByte+a_index-1,"uchar"),var_out,out_StartByte+a_index-1,"uchar")
}

ArrGetMax(arr,start=0,weight=1)
{
  start:=round(start)
  weight:=round(weight)
  loop,% weight
  {
    if (var<abs(arr[a_index+start]))
      var:=abs(arr[a_index+start])
  }
  return var
}

ArrClear(byref arr)
{
  arr:=""
  arr:=Object()
}

MoveControl(hwnd,x=0,y=0,w=0,h=0,min=10)
{
  overf:=0
  GuiControlGet,E_pos,Pos,% hwnd
  if (E_posw+w<min)
  {
    overf|=1
    x:=w
    w:=0
  }
  if (E_posh+h<min)
  {
    overf=2
    y:=h
    h:=0
  }
  GuiControl,Move,% hwnd,% " x" E_posx+x " y" E_posy+y " w" E_posw+w " h" E_posh+h
  return overf
}

DECtoBIN(h)
{
  stringreplace,h,h,%a_space%,,all
  h:=h+0
  a:=0
  while(h)
  {
    a++
    r:= (a=8?" ":"") . (h&1) . r
    h>>=1
    if (a=8)
      a:=0
  }
  if (a!=0)
  while(a!=8)
  {
    r:= 0 . r
    a++
  }
  return r
}

BINtoDEC(b)
{
  stringreplace,b,b,%a_space%,,all
  r:=0
  loop,parse,b
    r|=(a_loopfield<<(strlen(b)-a_index))
  return r
}

DATAtoSTR(var,st,size,sw=0)
{
  loop,% size
    str.=chr(numget(var+0,(sw?(size-a_index):(st+a_index-1)),"uchar"))
  return str
}

DATAtoHEX(var,st,size,sw=0)
{
  loop,% size
    hex.=format("{:02X}",numget(var+0,(sw?(size-a_index):(st+a_index-1)),"uchar"))
  return hex
}

DATAtoBIN(var,st,size,sw=0)
{
  static bin
  bin:=""
  loop,% size
  {
    h:=numget(var+0,(sw?(size-a_index):(st+a_index-1)),"uchar")
    r:=""
    loop,8
    {
      r:= (a=8?" ":"") . (h&1) . r
      h>>=1
      if (a_index=8)
        r:=" " . r
    }
    bin.= r
  }
  return bin
}

STRToDATA(str,byref var,swap=0)
{
  size:=strlen(str)
  varsetcapacity(var,size+2)
  loop,parse,str
  {
    if !swap
      NumPut(asc(a_loopfield),var,a_index-1,"Uchar")
    else
      NumPut(asc(a_loopfield),var,size-a_index,"Uchar")
  }
  return size
}

DECToDATA(str,byref var,swap=0,dtype="Uint64",size=8)
{
  varsetcapacity(var,size+2)
  NumPut(str+0,var,0,dtype)
  if swap
  {
    varsetcapacity(tmpvar,size)
    loop,% size
      numput(numget(var,a_index-1,"uchar"),tmpvar,size-a_index,"uchar")
    loop,% size
      numput(numget(tmpvar,a_index-1,"uchar"),var,a_index-1,"uchar")
  }
  return size
}

HEXstrToDATA(str,byref var,swap=0)
{
  stringreplace,str,str,%a_space%,,all
  size:=ceil(strlen(str)/2)
  varsetcapacity(var,size+2)
  loop,% size
  {
    stringright,tmp2,str,2
    if !swap
      NumPut("0x"tmp2,var,size-a_index,"Uchar")
    else
      NumPut("0x"tmp2,var,a_index-1,"Uchar")
    StringTrimRight,str,str,2
  }
  return size
}

BINstrToDATA(str,byref var,swap=0)
{
  stringreplace,str,str,%a_space%,,all
  size:=ceil(strlen(str)/8)
  varsetcapacity(var,size+2)
  loop,% size
  {
    stringright,tmp2,str,8
    if !swap
      NumPut(BINtoDEC(tmp2),var,size-a_index,"Uchar")
    else
      NumPut(BINtoDEC(tmp2),var,a_index-1,"Uchar")
    StringTrimRight,str,str,8
  }
  return size
}

ClearVar(byref var,symbol=" ")
{
  stringreplace,var,var,%symbol%,,all
}
______________________________________________________________________________:
return










; Gdip standard library v1.45 by tic (Tariq Porter) 07/09/11
; Modifed by Rseding91 using fincs 64 bit compatible Gdip library 5/1/2013
; Supports: Basic, _L ANSi, _L Unicode x86 and _L Unicode x64
;
; Updated 2/20/2014 - fixed Gdip_CreateRegion() and Gdip_GetClipRegion() on AHK Unicode x86
; Updated 5/13/2013 - fixed Gdip_SetBitmapToClipboard() on AHK Unicode x64
;
;#####################################################################################
;#####################################################################################
; STATUS ENUMERATION
; Return values for functions specified to have status enumerated return type
;#####################################################################################
;
; Ok =						= 0
; GenericError				= 1
; InvalidParameter			= 2
; OutOfMemory				= 3
; ObjectBusy				= 4
; InsufficientBuffer		= 5
; NotImplemented			= 6
; Win32Error				= 7
; WrongState				= 8
; Aborted					= 9
; FileNotFound				= 10
; ValueOverflow				= 11
; AccessDenied				= 12
; UnknownImageFormat		= 13
; FontFamilyNotFound		= 14
; FontStyleNotFound			= 15
; NotTrueTypeFont			= 16
; UnsupportedGdiplusVersion	= 17
; GdiplusNotInitialized		= 18
; PropertyNotFound			= 19
; PropertyNotSupported		= 20
; ProfileNotFound			= 21
;
;#####################################################################################
;#####################################################################################
; FUNCTIONS
;#####################################################################################
;
; UpdateLayeredWindow(hwnd, hdc, x="", y="", w="", h="", Alpha=255)
; BitBlt(ddc, dx, dy, dw, dh, sdc, sx, sy, Raster="")
; StretchBlt(dDC, dx, dy, dw, dh, sDC, sx, sy, sw, sh, Raster="")
; SetImage(hwnd, hBitmap)
; Gdip_BitmapFromScreen(Screen=0, Raster="")
; CreateRectF(ByRef RectF, x, y, w, h)
; CreateSizeF(ByRef SizeF, w, h)
; CreateDIBSection
;
;#####################################################################################

; Function:     			UpdateLayeredWindow
; Description:  			Updates a layered window with the handle to the DC of a gdi bitmap
;
; hwnd        				Handle of the layered window to update
; hdc           			Handle to the DC of the GDI bitmap to update the window with
; Layeredx      			x position to place the window
; Layeredy      			y position to place the window
; Layeredw      			Width of the window
; Layeredh      			Height of the window
; Alpha         			Default = 255 : The transparency (0-255) to set the window transparency
;
; return      				If the function succeeds, the return value is nonzero
;
; notes						If x or y omitted, then layered window will use its current coordinates
;							If w or h omitted then current width and height will be used

UpdateLayeredWindow(hwnd, hdc, x="", y="", w="", h="", Alpha=255)
{
	Ptr := A_PtrSize ? "UPtr" : "UInt"

	if ((x != "") && (y != ""))
		VarSetCapacity(pt, 8), NumPut(x, pt, 0, "UInt"), NumPut(y, pt, 4, "UInt")

	if (w = "") ||(h = "")
		WinGetPos,,, w, h, ahk_id %hwnd%

	return DllCall("UpdateLayeredWindow"
					, Ptr, hwnd
					, Ptr, 0
					, Ptr, ((x = "") && (y = "")) ? 0 : &pt
					, "int64*", w|h<<32
					, Ptr, hdc
					, "int64*", 0
					, "uint", 0
					, "UInt*", Alpha<<16|1<<24
					, "uint", 2)
}

;#####################################################################################

; Function				BitBlt
; Description			The BitBlt function performs a bit-block transfer of the color data corresponding to a rectangle
;						of pixels from the specified source device context into a destination device context.
;
; dDC					handle to destination DC
; dx					x-coord of destination upper-left corner
; dy					y-coord of destination upper-left corner
; dw					width of the area to copy
; dh					height of the area to copy
; sDC					handle to source DC
; sx					x-coordinate of source upper-left corner
; sy					y-coordinate of source upper-left corner
; Raster				raster operation code
;
; return				If the function succeeds, the return value is nonzero
;
; notes					If no raster operation is specified, then SRCCOPY is used, which copies the source directly to the destination rectangle
;
; BLACKNESS				= 0x00000042
; NOTSRCERASE			= 0x001100A6
; NOTSRCCOPY			= 0x00330008
; SRCERASE				= 0x00440328
; DSTINVERT				= 0x00550009
; PATINVERT				= 0x005A0049
; SRCINVERT				= 0x00660046
; SRCAND				= 0x008800C6
; MERGEPAINT			= 0x00BB0226
; MERGECOPY				= 0x00C000CA
; SRCCOPY				= 0x00CC0020
; SRCPAINT				= 0x00EE0086
; PATCOPY				= 0x00F00021
; PATPAINT				= 0x00FB0A09
; WHITENESS				= 0x00FF0062
; CAPTUREBLT			= 0x40000000
; NOMIRRORBITMAP		= 0x80000000

BitBlt(ddc, dx, dy, dw, dh, sdc, sx, sy, Raster="")
{
	Ptr := A_PtrSize ? "UPtr" : "UInt"

	return DllCall("gdi32\BitBlt"
					, Ptr, dDC
					, "int", dx
					, "int", dy
					, "int", dw
					, "int", dh
					, Ptr, sDC
					, "int", sx
					, "int", sy
					, "uint", Raster ? Raster : 0x00CC0020)
}

;#####################################################################################

; Function				StretchBlt
; Description			The StretchBlt function copies a bitmap from a source rectangle into a destination rectangle,
;						stretching or compressing the bitmap to fit the dimensions of the destination rectangle, if necessary.
;						The system stretches or compresses the bitmap according to the stretching mode currently set in the destination device context.
;
; ddc					handle to destination DC
; dx					x-coord of destination upper-left corner
; dy					y-coord of destination upper-left corner
; dw					width of destination rectangle
; dh					height of destination rectangle
; sdc					handle to source DC
; sx					x-coordinate of source upper-left corner
; sy					y-coordinate of source upper-left corner
; sw					width of source rectangle
; sh					height of source rectangle
; Raster				raster operation code
;
; return				If the function succeeds, the return value is nonzero
;
; notes					If no raster operation is specified, then SRCCOPY is used. It uses the same raster operations as BitBlt

StretchBlt(ddc, dx, dy, dw, dh, sdc, sx, sy, sw, sh, Raster="")
{
	Ptr := A_PtrSize ? "UPtr" : "UInt"

	return DllCall("gdi32\StretchBlt"
					, Ptr, ddc
					, "int", dx
					, "int", dy
					, "int", dw
					, "int", dh
					, Ptr, sdc
					, "int", sx
					, "int", sy
					, "int", sw
					, "int", sh
					, "uint", Raster ? Raster : 0x00CC0020)
}

;#####################################################################################

; Function				SetStretchBltMode
; Description			The SetStretchBltMode function sets the bitmap stretching mode in the specified device context
;
; hdc					handle to the DC
; iStretchMode			The stretching mode, describing how the target will be stretched
;
; return				If the function succeeds, the return value is the previous stretching mode. If it fails it will return 0
;
; STRETCH_ANDSCANS 		= 0x01
; STRETCH_ORSCANS 		= 0x02
; STRETCH_DELETESCANS 	= 0x03
; STRETCH_HALFTONE 		= 0x04

SetStretchBltMode(hdc, iStretchMode=4)
{
	return DllCall("gdi32\SetStretchBltMode"
					, A_PtrSize ? "UPtr" : "UInt", hdc
					, "int", iStretchMode)
}

;#####################################################################################

; Function				SetImage
; Description			Associates a new image with a static control
;
; hwnd					handle of the control to update
; hBitmap				a gdi bitmap to associate the static control with
;
; return				If the function succeeds, the return value is nonzero

SetImage(hwnd, hBitmap)
{
	SendMessage, 0x172, 0x0, hBitmap,, ahk_id %hwnd%
	E := ErrorLevel
	DeleteObject(E)
	return E
}

;#####################################################################################

; Function				SetSysColorToControl
; Description			Sets a solid colour to a control
;
; hwnd					handle of the control to update
; SysColor				A system colour to set to the control
;
; return				If the function succeeds, the return value is zero
;
; notes					A control must have the 0xE style set to it so it is recognised as a bitmap
;						By default SysColor=15 is used which is COLOR_3DFACE. This is the standard background for a control
;
; COLOR_3DDKSHADOW				= 21
; COLOR_3DFACE					= 15
; COLOR_3DHIGHLIGHT				= 20
; COLOR_3DHILIGHT				= 20
; COLOR_3DLIGHT					= 22
; COLOR_3DSHADOW				= 16
; COLOR_ACTIVEBORDER			= 10
; COLOR_ACTIVECAPTION			= 2
; COLOR_APPWORKSPACE			= 12
; COLOR_BACKGROUND				= 1
; COLOR_BTNFACE					= 15
; COLOR_BTNHIGHLIGHT			= 20
; COLOR_BTNHILIGHT				= 20
; COLOR_BTNSHADOW				= 16
; COLOR_BTNTEXT					= 18
; COLOR_CAPTIONTEXT				= 9
; COLOR_DESKTOP					= 1
; COLOR_GRADIENTACTIVECAPTION	= 27
; COLOR_GRADIENTINACTIVECAPTION	= 28
; COLOR_GRAYTEXT				= 17
; COLOR_HIGHLIGHT				= 13
; COLOR_HIGHLIGHTTEXT			= 14
; COLOR_HOTLIGHT				= 26
; COLOR_INACTIVEBORDER			= 11
; COLOR_INACTIVECAPTION			= 3
; COLOR_INACTIVECAPTIONTEXT		= 19
; COLOR_INFOBK					= 24
; COLOR_INFOTEXT				= 23
; COLOR_MENU					= 4
; COLOR_MENUHILIGHT				= 29
; COLOR_MENUBAR					= 30
; COLOR_MENUTEXT				= 7
; COLOR_SCROLLBAR				= 0
; COLOR_WINDOW					= 5
; COLOR_WINDOWFRAME				= 6
; COLOR_WINDOWTEXT				= 8

SetSysColorToControl(hwnd, SysColor=15)
{
   WinGetPos,,, w, h, ahk_id %hwnd%
   bc := DllCall("GetSysColor", "Int", SysColor, "UInt")
   pBrushClear := Gdip_BrushCreateSolid(0xff000000 | (bc >> 16 | bc & 0xff00 | (bc & 0xff) << 16))
   pBitmap := Gdip_CreateBitmap(w, h), G := Gdip_GraphicsFromImage(pBitmap)
   Gdip_FillRectangle(G, pBrushClear, 0, 0, w, h)
   hBitmap := Gdip_CreateHBITMAPFromBitmap(pBitmap)
   SetImage(hwnd, hBitmap)
   Gdip_DeleteBrush(pBrushClear)
   Gdip_DeleteGraphics(G), Gdip_DisposeImage(pBitmap), DeleteObject(hBitmap)
   return 0
}

;#####################################################################################

; Function				Gdip_BitmapFromScreen
; Description			Gets a gdi+ bitmap from the screen
;
; Screen				0 = All screens
;						Any numerical value = Just that screen
;						x|y|w|h = Take specific coordinates with a width and height
; Raster				raster operation code
;
; return      			If the function succeeds, the return value is a pointer to a gdi+ bitmap
;						-1:		one or more of x,y,w,h not passed properly
;
; notes					If no raster operation is specified, then SRCCOPY is used to the returned bitmap

Gdip_BitmapFromScreen(Screen=0, Raster="")
{
	if (Screen = 0)
	{
		Sysget, x, 76
		Sysget, y, 77
		Sysget, w, 78
		Sysget, h, 79
	}
	else if (SubStr(Screen, 1, 5) = "hwnd:")
	{
		Screen := SubStr(Screen, 6)
		if !WinExist( "ahk_id " Screen)
			return -2
		WinGetPos,,, w, h, ahk_id %Screen%
		x := y := 0
		hhdc := GetDCEx(Screen, 3)
	}
	else if (Screen&1 != "")
	{
		Sysget, M, Monitor, %Screen%
		x := MLeft, y := MTop, w := MRight-MLeft, h := MBottom-MTop
	}
	else
	{
		StringSplit, S, Screen, |
		x := S1, y := S2, w := S3, h := S4
	}

	if (x = "") || (y = "") || (w = "") || (h = "")
		return -1

	chdc := CreateCompatibleDC(), hbm := CreateDIBSection(w, h, chdc), obm := SelectObject(chdc, hbm), hhdc := hhdc ? hhdc : GetDC()
	BitBlt(chdc, 0, 0, w, h, hhdc, x, y, Raster)
	ReleaseDC(hhdc)

	pBitmap := Gdip_CreateBitmapFromHBITMAP(hbm)
	SelectObject(chdc, obm), DeleteObject(hbm), DeleteDC(hhdc), DeleteDC(chdc)
	return pBitmap
}

;#####################################################################################

; Function				Gdip_BitmapFromHWND
; Description			Uses PrintWindow to get a handle to the specified window and return a bitmap from it
;
; hwnd					handle to the window to get a bitmap from
;
; return				If the function succeeds, the return value is a pointer to a gdi+ bitmap
;
; notes					Window must not be not minimised in order to get a handle to it's client area

Gdip_BitmapFromHWND(hwnd)
{
	WinGetPos,,, Width, Height, ahk_id %hwnd%
	hbm := CreateDIBSection(Width, Height), hdc := CreateCompatibleDC(), obm := SelectObject(hdc, hbm)
	PrintWindow(hwnd, hdc)
	pBitmap := Gdip_CreateBitmapFromHBITMAP(hbm)
	SelectObject(hdc, obm), DeleteObject(hbm), DeleteDC(hdc)
	return pBitmap
}

;#####################################################################################

; Function    			CreateRectF
; Description			Creates a RectF object, containing a the coordinates and dimensions of a rectangle
;
; RectF       			Name to call the RectF object
; x            			x-coordinate of the upper left corner of the rectangle
; y            			y-coordinate of the upper left corner of the rectangle
; w            			Width of the rectangle
; h            			Height of the rectangle
;
; return      			No return value

CreateRectF(ByRef RectF, x, y, w, h)
{
   VarSetCapacity(RectF, 16)
   NumPut(x, RectF, 0, "float"), NumPut(y, RectF, 4, "float"), NumPut(w, RectF, 8, "float"), NumPut(h, RectF, 12, "float")
}

;#####################################################################################

; Function    			CreateRect
; Description			Creates a Rect object, containing a the coordinates and dimensions of a rectangle
;
; RectF       			Name to call the RectF object
; x            			x-coordinate of the upper left corner of the rectangle
; y            			y-coordinate of the upper left corner of the rectangle
; w            			Width of the rectangle
; h            			Height of the rectangle
;
; return      			No return value

CreateRect(ByRef Rect, x, y, w, h)
{
	VarSetCapacity(Rect, 16)
	NumPut(x, Rect, 0, "uint"), NumPut(y, Rect, 4, "uint"), NumPut(w, Rect, 8, "uint"), NumPut(h, Rect, 12, "uint")
}
;#####################################################################################

; Function		    	CreateSizeF
; Description			Creates a SizeF object, containing an 2 values
;
; SizeF         		Name to call the SizeF object
; w            			w-value for the SizeF object
; h            			h-value for the SizeF object
;
; return      			No Return value

CreateSizeF(ByRef SizeF, w, h)
{
   VarSetCapacity(SizeF, 8)
   NumPut(w, SizeF, 0, "float"), NumPut(h, SizeF, 4, "float")
}
;#####################################################################################

; Function		    	CreatePointF
; Description			Creates a SizeF object, containing an 2 values
;
; SizeF         		Name to call the SizeF object
; w            			w-value for the SizeF object
; h            			h-value for the SizeF object
;
; return      			No Return value

CreatePointF(ByRef PointF, x, y)
{
   VarSetCapacity(PointF, 8)
   NumPut(x, PointF, 0, "float"), NumPut(y, PointF, 4, "float")
}
;#####################################################################################

; Function				CreateDIBSection
; Description			The CreateDIBSection function creates a DIB (Device Independent Bitmap) that applications can write to directly
;
; w						width of the bitmap to create
; h						height of the bitmap to create
; hdc					a handle to the device context to use the palette from
; bpp					bits per pixel (32 = ARGB)
; ppvBits				A pointer to a variable that receives a pointer to the location of the DIB bit values
;
; return				returns a DIB. A gdi bitmap
;
; notes					ppvBits will receive the location of the pixels in the DIB

CreateDIBSection(w, h, hdc="", bpp=32, ByRef ppvBits=0)
{
	Ptr := A_PtrSize ? "UPtr" : "UInt"

	hdc2 := hdc ? hdc : GetDC()
	VarSetCapacity(bi, 40, 0)

	NumPut(w, bi, 4, "uint")
	, NumPut(h, bi, 8, "uint")
	, NumPut(40, bi, 0, "uint")
	, NumPut(1, bi, 12, "ushort")
	, NumPut(0, bi, 16, "uInt")
	, NumPut(bpp, bi, 14, "ushort")

	hbm := DllCall("CreateDIBSection"
					, Ptr, hdc2
					, Ptr, &bi
					, "uint", 0
					, A_PtrSize ? "UPtr*" : "uint*", ppvBits
					, Ptr, 0
					, "uint", 0, Ptr)

	if !hdc
		ReleaseDC(hdc2)
	return hbm
}

;#####################################################################################

; Function				PrintWindow
; Description			The PrintWindow function copies a visual window into the specified device context (DC), typically a printer DC
;
; hwnd					A handle to the window that will be copied
; hdc					A handle to the device context
; Flags					Drawing options
;
; return				If the function succeeds, it returns a nonzero value
;
; PW_CLIENTONLY			= 1

PrintWindow(hwnd, hdc, Flags=0)
{
	Ptr := A_PtrSize ? "UPtr" : "UInt"

	return DllCall("PrintWindow", Ptr, hwnd, Ptr, hdc, "uint", Flags)
}

;#####################################################################################

; Function				DestroyIcon
; Description			Destroys an icon and frees any memory the icon occupied
;
; hIcon					Handle to the icon to be destroyed. The icon must not be in use
;
; return				If the function succeeds, the return value is nonzero

DestroyIcon(hIcon)
{
	return DllCall("DestroyIcon", A_PtrSize ? "UPtr" : "UInt", hIcon)
}

;#####################################################################################

PaintDesktop(hdc)
{
	return DllCall("PaintDesktop", A_PtrSize ? "UPtr" : "UInt", hdc)
}

;#####################################################################################

CreateCompatibleBitmap(hdc, w, h)
{
	return DllCall("gdi32\CreateCompatibleBitmap", A_PtrSize ? "UPtr" : "UInt", hdc, "int", w, "int", h)
}

;#####################################################################################

; Function				CreateCompatibleDC
; Description			This function creates a memory device context (DC) compatible with the specified device
;
; hdc					Handle to an existing device context
;
; return				returns the handle to a device context or 0 on failure
;
; notes					If this handle is 0 (by default), the function creates a memory device context compatible with the application's current screen

CreateCompatibleDC(hdc=0)
{
   return DllCall("CreateCompatibleDC", A_PtrSize ? "UPtr" : "UInt", hdc)
}

;#####################################################################################

; Function				SelectObject
; Description			The SelectObject function selects an object into the specified device context (DC). The new object replaces the previous object of the same type
;
; hdc					Handle to a DC
; hgdiobj				A handle to the object to be selected into the DC
;
; return				If the selected object is not a region and the function succeeds, the return value is a handle to the object being replaced
;
; notes					The specified object must have been created by using one of the following functions
;						Bitmap - CreateBitmap, CreateBitmapIndirect, CreateCompatibleBitmap, CreateDIBitmap, CreateDIBSection (A single bitmap cannot be selected into more than one DC at the same time)
;						Brush - CreateBrushIndirect, CreateDIBPatternBrush, CreateDIBPatternBrushPt, CreateHatchBrush, CreatePatternBrush, CreateSolidBrush
;						Font - CreateFont, CreateFontIndirect
;						Pen - CreatePen, CreatePenIndirect
;						Region - CombineRgn, CreateEllipticRgn, CreateEllipticRgnIndirect, CreatePolygonRgn, CreateRectRgn, CreateRectRgnIndirect
;
; notes					If the selected object is a region and the function succeeds, the return value is one of the following value
;
; SIMPLEREGION			= 2 Region consists of a single rectangle
; COMPLEXREGION			= 3 Region consists of more than one rectangle
; NULLREGION			= 1 Region is empty

SelectObject(hdc, hgdiobj)
{
	Ptr := A_PtrSize ? "UPtr" : "UInt"

	return DllCall("SelectObject", Ptr, hdc, Ptr, hgdiobj)
}

;#####################################################################################

; Function				DeleteObject
; Description			This function deletes a logical pen, brush, font, bitmap, region, or palette, freeing all system resources associated with the object
;						After the object is deleted, the specified handle is no longer valid
;
; hObject				Handle to a logical pen, brush, font, bitmap, region, or palette to delete
;
; return				Nonzero indicates success. Zero indicates that the specified handle is not valid or that the handle is currently selected into a device context

DeleteObject(hObject)
{
   return DllCall("DeleteObject", A_PtrSize ? "UPtr" : "UInt", hObject)
}

;#####################################################################################

; Function				GetDC
; Description			This function retrieves a handle to a display device context (DC) for the client area of the specified window.
;						The display device context can be used in subsequent graphics display interface (GDI) functions to draw in the client area of the window.
;
; hwnd					Handle to the window whose device context is to be retrieved. If this value is NULL, GetDC retrieves the device context for the entire screen
;
; return				The handle the device context for the specified window's client area indicates success. NULL indicates failure

GetDC(hwnd=0)
{
	return DllCall("GetDC", A_PtrSize ? "UPtr" : "UInt", hwnd)
}

;#####################################################################################

; DCX_CACHE = 0x2
; DCX_CLIPCHILDREN = 0x8
; DCX_CLIPSIBLINGS = 0x10
; DCX_EXCLUDERGN = 0x40
; DCX_EXCLUDEUPDATE = 0x100
; DCX_INTERSECTRGN = 0x80
; DCX_INTERSECTUPDATE = 0x200
; DCX_LOCKWINDOWUPDATE = 0x400
; DCX_NORECOMPUTE = 0x100000
; DCX_NORESETATTRS = 0x4
; DCX_PARENTCLIP = 0x20
; DCX_VALIDATE = 0x200000
; DCX_WINDOW = 0x1

GetDCEx(hwnd, flags=0, hrgnClip=0)
{
	Ptr := A_PtrSize ? "UPtr" : "UInt"

    return DllCall("GetDCEx", Ptr, hwnd, Ptr, hrgnClip, "int", flags)
}

;#####################################################################################

; Function				ReleaseDC
; Description			This function releases a device context (DC), freeing it for use by other applications. The effect of ReleaseDC depends on the type of device context
;
; hdc					Handle to the device context to be released
; hwnd					Handle to the window whose device context is to be released
;
; return				1 = released
;						0 = not released
;
; notes					The application must call the ReleaseDC function for each call to the GetWindowDC function and for each call to the GetDC function that retrieves a common device context
;						An application cannot use the ReleaseDC function to release a device context that was created by calling the CreateDC function; instead, it must use the DeleteDC function.

ReleaseDC(hdc, hwnd=0)
{
	Ptr := A_PtrSize ? "UPtr" : "UInt"

	return DllCall("ReleaseDC", Ptr, hwnd, Ptr, hdc)
}

;#####################################################################################

; Function				DeleteDC
; Description			The DeleteDC function deletes the specified device context (DC)
;
; hdc					A handle to the device context
;
; return				If the function succeeds, the return value is nonzero
;
; notes					An application must not delete a DC whose handle was obtained by calling the GetDC function. Instead, it must call the ReleaseDC function to free the DC

DeleteDC(hdc)
{
   return DllCall("DeleteDC", A_PtrSize ? "UPtr" : "UInt", hdc)
}
;#####################################################################################

; Function				Gdip_LibraryVersion
; Description			Get the current library version
;
; return				the library version
;
; notes					This is useful for non compiled programs to ensure that a person doesn't run an old version when testing your scripts

Gdip_LibraryVersion()
{
	return 1.45
}

;#####################################################################################

; Function				Gdip_LibrarySubVersion
; Description			Get the current library sub version
;
; return				the library sub version
;
; notes					This is the sub-version currently maintained by Rseding91
Gdip_LibrarySubVersion()
{
	return 1.47
}

;#####################################################################################

; Function:    			Gdip_BitmapFromBRA
; Description: 			Gets a pointer to a gdi+ bitmap from a BRA file
;
; BRAFromMemIn			The variable for a BRA file read to memory
; File					The name of the file, or its number that you would like (This depends on alternate parameter)
; Alternate				Changes whether the File parameter is the file name or its number
;
; return      			If the function succeeds, the return value is a pointer to a gdi+ bitmap
;						-1 = The BRA variable is empty
;						-2 = The BRA has an incorrect header
;						-3 = The BRA has information missing
;						-4 = Could not find file inside the BRA

Gdip_BitmapFromBRA(ByRef BRAFromMemIn, File, Alternate=0)
{
	Static FName = "ObjRelease"

	if !BRAFromMemIn
		return -1
	Loop, Parse, BRAFromMemIn, `n
	{
		if (A_Index = 1)
		{
			StringSplit, Header, A_LoopField, |
			if (Header0 != 4 || Header2 != "BRA!")
				return -2
		}
		else if (A_Index = 2)
		{
			StringSplit, Info, A_LoopField, |
			if (Info0 != 3)
				return -3
		}
		else
			break
	}
	if !Alternate
		StringReplace, File, File, \, \\, All
	RegExMatch(BRAFromMemIn, "mi`n)^" (Alternate ? File "\|.+?\|(\d+)\|(\d+)" : "\d+\|" File "\|(\d+)\|(\d+)") "$", FileInfo)
	if !FileInfo
		return -4

	hData := DllCall("GlobalAlloc", "uint", 2, Ptr, FileInfo2, Ptr)
	pData := DllCall("GlobalLock", Ptr, hData, Ptr)
	DllCall("RtlMoveMemory", Ptr, pData, Ptr, &BRAFromMemIn+Info2+FileInfo1, Ptr, FileInfo2)
	DllCall("GlobalUnlock", Ptr, hData)
	DllCall("ole32\CreateStreamOnHGlobal", Ptr, hData, "int", 1, A_PtrSize ? "UPtr*" : "UInt*", pStream)
	DllCall("gdiplus\GdipCreateBitmapFromStream", Ptr, pStream, A_PtrSize ? "UPtr*" : "UInt*", pBitmap)
	If (A_PtrSize)
		%FName%(pStream)
	Else
		DllCall(NumGet(NumGet(1*pStream)+8), "uint", pStream)
	return pBitmap
}

;#####################################################################################

; Function				Gdip_DrawRectangle
; Description			This function uses a pen to draw the outline of a rectangle into the Graphics of a bitmap
;
; pGraphics				Pointer to the Graphics of a bitmap
; pPen					Pointer to a pen
; x						x-coordinate of the top left of the rectangle
; y						y-coordinate of the top left of the rectangle
; w						width of the rectanlge
; h						height of the rectangle
;
; return				status enumeration. 0 = success
;
; notes					as all coordinates are taken from the top left of each pixel, then the entire width/height should be specified as subtracting the pen width

Gdip_DrawRectangle(pGraphics, pPen, x, y, w, h)
{
	Ptr := A_PtrSize ? "UPtr" : "UInt"

	return DllCall("gdiplus\GdipDrawRectangle", Ptr, pGraphics, Ptr, pPen, "float", x, "float", y, "float", w, "float", h)
}

;#####################################################################################

; Function				Gdip_DrawRoundedRectangle
; Description			This function uses a pen to draw the outline of a rounded rectangle into the Graphics of a bitmap
;
; pGraphics				Pointer to the Graphics of a bitmap
; pPen					Pointer to a pen
; x						x-coordinate of the top left of the rounded rectangle
; y						y-coordinate of the top left of the rounded rectangle
; w						width of the rectanlge
; h						height of the rectangle
; r						radius of the rounded corners
;
; return				status enumeration. 0 = success
;
; notes					as all coordinates are taken from the top left of each pixel, then the entire width/height should be specified as subtracting the pen width

Gdip_DrawRoundedRectangle(pGraphics, pPen, x, y, w, h, r)
{
	Gdip_SetClipRect(pGraphics, x-r, y-r, 2*r, 2*r, 4)
	Gdip_SetClipRect(pGraphics, x+w-r, y-r, 2*r, 2*r, 4)
	Gdip_SetClipRect(pGraphics, x-r, y+h-r, 2*r, 2*r, 4)
	Gdip_SetClipRect(pGraphics, x+w-r, y+h-r, 2*r, 2*r, 4)
	E := Gdip_DrawRectangle(pGraphics, pPen, x, y, w, h)
	Gdip_ResetClip(pGraphics)
	Gdip_SetClipRect(pGraphics, x-(2*r), y+r, w+(4*r), h-(2*r), 4)
	Gdip_SetClipRect(pGraphics, x+r, y-(2*r), w-(2*r), h+(4*r), 4)
	Gdip_DrawEllipse(pGraphics, pPen, x, y, 2*r, 2*r)
	Gdip_DrawEllipse(pGraphics, pPen, x+w-(2*r), y, 2*r, 2*r)
	Gdip_DrawEllipse(pGraphics, pPen, x, y+h-(2*r), 2*r, 2*r)
	Gdip_DrawEllipse(pGraphics, pPen, x+w-(2*r), y+h-(2*r), 2*r, 2*r)
	Gdip_ResetClip(pGraphics)
	return E
}

;#####################################################################################

; Function				Gdip_DrawEllipse
; Description			This function uses a pen to draw the outline of an ellipse into the Graphics of a bitmap
;
; pGraphics				Pointer to the Graphics of a bitmap
; pPen					Pointer to a pen
; x						x-coordinate of the top left of the rectangle the ellipse will be drawn into
; y						y-coordinate of the top left of the rectangle the ellipse will be drawn into
; w						width of the ellipse
; h						height of the ellipse
;
; return				status enumeration. 0 = success
;
; notes					as all coordinates are taken from the top left of each pixel, then the entire width/height should be specified as subtracting the pen width

Gdip_DrawEllipse(pGraphics, pPen, x, y, w, h)
{
	Ptr := A_PtrSize ? "UPtr" : "UInt"

	return DllCall("gdiplus\GdipDrawEllipse", Ptr, pGraphics, Ptr, pPen, "float", x, "float", y, "float", w, "float", h)
}

;#####################################################################################

; Function				Gdip_DrawBezier
; Description			This function uses a pen to draw the outline of a bezier (a weighted curve) into the Graphics of a bitmap
;
; pGraphics				Pointer to the Graphics of a bitmap
; pPen					Pointer to a pen
; x1					x-coordinate of the start of the bezier
; y1					y-coordinate of the start of the bezier
; x2					x-coordinate of the first arc of the bezier
; y2					y-coordinate of the first arc of the bezier
; x3					x-coordinate of the second arc of the bezier
; y3					y-coordinate of the second arc of the bezier
; x4					x-coordinate of the end of the bezier
; y4					y-coordinate of the end of the bezier
;
; return				status enumeration. 0 = success
;
; notes					as all coordinates are taken from the top left of each pixel, then the entire width/height should be specified as subtracting the pen width

Gdip_DrawBezier(pGraphics, pPen, x1, y1, x2, y2, x3, y3, x4, y4)
{
	Ptr := A_PtrSize ? "UPtr" : "UInt"

	return DllCall("gdiplus\GdipDrawBezier"
					, Ptr, pgraphics
					, Ptr, pPen
					, "float", x1
					, "float", y1
					, "float", x2
					, "float", y2
					, "float", x3
					, "float", y3
					, "float", x4
					, "float", y4)
}

;#####################################################################################

; Function				Gdip_DrawArc
; Description			This function uses a pen to draw the outline of an arc into the Graphics of a bitmap
;
; pGraphics				Pointer to the Graphics of a bitmap
; pPen					Pointer to a pen
; x						x-coordinate of the start of the arc
; y						y-coordinate of the start of the arc
; w						width of the arc
; h						height of the arc
; StartAngle			specifies the angle between the x-axis and the starting point of the arc
; SweepAngle			specifies the angle between the starting and ending points of the arc
;
; return				status enumeration. 0 = success
;
; notes					as all coordinates are taken from the top left of each pixel, then the entire width/height should be specified as subtracting the pen width

Gdip_DrawArc(pGraphics, pPen, x, y, w, h, StartAngle, SweepAngle)
{
	Ptr := A_PtrSize ? "UPtr" : "UInt"

	return DllCall("gdiplus\GdipDrawArc"
					, Ptr, pGraphics
					, Ptr, pPen
					, "float", x
					, "float", y
					, "float", w
					, "float", h
					, "float", StartAngle
					, "float", SweepAngle)
}

;#####################################################################################

; Function				Gdip_DrawPie
; Description			This function uses a pen to draw the outline of a pie into the Graphics of a bitmap
;
; pGraphics				Pointer to the Graphics of a bitmap
; pPen					Pointer to a pen
; x						x-coordinate of the start of the pie
; y						y-coordinate of the start of the pie
; w						width of the pie
; h						height of the pie
; StartAngle			specifies the angle between the x-axis and the starting point of the pie
; SweepAngle			specifies the angle between the starting and ending points of the pie
;
; return				status enumeration. 0 = success
;
; notes					as all coordinates are taken from the top left of each pixel, then the entire width/height should be specified as subtracting the pen width

Gdip_DrawPie(pGraphics, pPen, x, y, w, h, StartAngle, SweepAngle)
{
	Ptr := A_PtrSize ? "UPtr" : "UInt"

	return DllCall("gdiplus\GdipDrawPie", Ptr, pGraphics, Ptr, pPen, "float", x, "float", y, "float", w, "float", h, "float", StartAngle, "float", SweepAngle)
}

;#####################################################################################

; Function				Gdip_DrawLine
; Description			This function uses a pen to draw a line into the Graphics of a bitmap
;
; pGraphics				Pointer to the Graphics of a bitmap
; pPen					Pointer to a pen
; x1					x-coordinate of the start of the line
; y1					y-coordinate of the start of the line
; x2					x-coordinate of the end of the line
; y2					y-coordinate of the end of the line
;
; return				status enumeration. 0 = success

Gdip_DrawLine(pGraphics, pPen, x1, y1, x2, y2)
{
	Ptr := A_PtrSize ? "UPtr" : "UInt"

	return DllCall("gdiplus\GdipDrawLine"
					, Ptr, pGraphics
					, Ptr, pPen
					, "float", x1
					, "float", y1
					, "float", x2
					, "float", y2)
}

;#####################################################################################

; Function				Gdip_DrawLines
; Description			This function uses a pen to draw a series of joined lines into the Graphics of a bitmap
;
; pGraphics				Pointer to the Graphics of a bitmap
; pPen					Pointer to a pen
; Points				the coordinates of all the points passed as x1,y1|x2,y2|x3,y3.....
;
; return				status enumeration. 0 = success

Gdip_DrawLines(pGraphics, pPen, Points)
{
	Ptr := A_PtrSize ? "UPtr" : "UInt"
	StringSplit, Points, Points, |
	VarSetCapacity(PointF, 8*Points0)
	Loop, %Points0%
	{
		StringSplit, Coord, Points%A_Index%, `,
		NumPut(Coord1, PointF, 8*(A_Index-1), "float"), NumPut(Coord2, PointF, (8*(A_Index-1))+4, "float")
	}
	return DllCall("gdiplus\GdipDrawLines", Ptr, pGraphics, Ptr, pPen, Ptr, &PointF, "int", Points0)
}

;#####################################################################################

; Function				Gdip_FillRectangle
; Description			This function uses a brush to fill a rectangle in the Graphics of a bitmap
;
; pGraphics				Pointer to the Graphics of a bitmap
; pBrush				Pointer to a brush
; x						x-coordinate of the top left of the rectangle
; y						y-coordinate of the top left of the rectangle
; w						width of the rectanlge
; h						height of the rectangle
;
; return				status enumeration. 0 = success

Gdip_FillRectangle(pGraphics, pBrush, x, y, w, h)
{
	Ptr := A_PtrSize ? "UPtr" : "UInt"

	return DllCall("gdiplus\GdipFillRectangle"
					, Ptr, pGraphics
					, Ptr, pBrush
					, "float", x
					, "float", y
					, "float", w
					, "float", h)
}

;#####################################################################################

; Function				Gdip_FillRoundedRectangle
; Description			This function uses a brush to fill a rounded rectangle in the Graphics of a bitmap
;
; pGraphics				Pointer to the Graphics of a bitmap
; pBrush				Pointer to a brush
; x						x-coordinate of the top left of the rounded rectangle
; y						y-coordinate of the top left of the rounded rectangle
; w						width of the rectanlge
; h						height of the rectangle
; r						radius of the rounded corners
;
; return				status enumeration. 0 = success

Gdip_FillRoundedRectangle(pGraphics, pBrush, x, y, w, h, r)
{
	Region := Gdip_GetClipRegion(pGraphics)
	Gdip_SetClipRect(pGraphics, x-r, y-r, 2*r, 2*r, 4)
	Gdip_SetClipRect(pGraphics, x+w-r, y-r, 2*r, 2*r, 4)
	Gdip_SetClipRect(pGraphics, x-r, y+h-r, 2*r, 2*r, 4)
	Gdip_SetClipRect(pGraphics, x+w-r, y+h-r, 2*r, 2*r, 4)
	E := Gdip_FillRectangle(pGraphics, pBrush, x, y, w, h)
	Gdip_SetClipRegion(pGraphics, Region, 0)
	Gdip_SetClipRect(pGraphics, x-(2*r), y+r, w+(4*r), h-(2*r), 4)
	Gdip_SetClipRect(pGraphics, x+r, y-(2*r), w-(2*r), h+(4*r), 4)
	Gdip_FillEllipse(pGraphics, pBrush, x, y, 2*r, 2*r)
	Gdip_FillEllipse(pGraphics, pBrush, x+w-(2*r), y, 2*r, 2*r)
	Gdip_FillEllipse(pGraphics, pBrush, x, y+h-(2*r), 2*r, 2*r)
	Gdip_FillEllipse(pGraphics, pBrush, x+w-(2*r), y+h-(2*r), 2*r, 2*r)
	Gdip_SetClipRegion(pGraphics, Region, 0)
	Gdip_DeleteRegion(Region)
	return E
}

;#####################################################################################

; Function				Gdip_FillPolygon
; Description			This function uses a brush to fill a polygon in the Graphics of a bitmap
;
; pGraphics				Pointer to the Graphics of a bitmap
; pBrush				Pointer to a brush
; Points				the coordinates of all the points passed as x1,y1|x2,y2|x3,y3.....
;
; return				status enumeration. 0 = success
;
; notes					Alternate will fill the polygon as a whole, wheras winding will fill each new "segment"
; Alternate 			= 0
; Winding 				= 1

Gdip_FillPolygon(pGraphics, pBrush, Points, FillMode=0)
{
	Ptr := A_PtrSize ? "UPtr" : "UInt"

	StringSplit, Points, Points, |
	VarSetCapacity(PointF, 8*Points0)
	Loop, %Points0%
	{
		StringSplit, Coord, Points%A_Index%, `,
		NumPut(Coord1, PointF, 8*(A_Index-1), "float"), NumPut(Coord2, PointF, (8*(A_Index-1))+4, "float")
	}
	return DllCall("gdiplus\GdipFillPolygon", Ptr, pGraphics, Ptr, pBrush, Ptr, &PointF, "int", Points0, "int", FillMode)
}

;#####################################################################################

; Function				Gdip_FillPie
; Description			This function uses a brush to fill a pie in the Graphics of a bitmap
;
; pGraphics				Pointer to the Graphics of a bitmap
; pBrush				Pointer to a brush
; x						x-coordinate of the top left of the pie
; y						y-coordinate of the top left of the pie
; w						width of the pie
; h						height of the pie
; StartAngle			specifies the angle between the x-axis and the starting point of the pie
; SweepAngle			specifies the angle between the starting and ending points of the pie
;
; return				status enumeration. 0 = success

Gdip_FillPie(pGraphics, pBrush, x, y, w, h, StartAngle, SweepAngle)
{
	Ptr := A_PtrSize ? "UPtr" : "UInt"

	return DllCall("gdiplus\GdipFillPie"
					, Ptr, pGraphics
					, Ptr, pBrush
					, "float", x
					, "float", y
					, "float", w
					, "float", h
					, "float", StartAngle
					, "float", SweepAngle)
}

;#####################################################################################

; Function				Gdip_FillEllipse
; Description			This function uses a brush to fill an ellipse in the Graphics of a bitmap
;
; pGraphics				Pointer to the Graphics of a bitmap
; pBrush				Pointer to a brush
; x						x-coordinate of the top left of the ellipse
; y						y-coordinate of the top left of the ellipse
; w						width of the ellipse
; h						height of the ellipse
;
; return				status enumeration. 0 = success

Gdip_FillEllipse(pGraphics, pBrush, x, y, w, h)
{
	Ptr := A_PtrSize ? "UPtr" : "UInt"

	return DllCall("gdiplus\GdipFillEllipse", Ptr, pGraphics, Ptr, pBrush, "float", x, "float", y, "float", w, "float", h)
}

;#####################################################################################

; Function				Gdip_FillRegion
; Description			This function uses a brush to fill a region in the Graphics of a bitmap
;
; pGraphics				Pointer to the Graphics of a bitmap
; pBrush				Pointer to a brush
; Region				Pointer to a Region
;
; return				status enumeration. 0 = success
;
; notes					You can create a region Gdip_CreateRegion() and then add to this

Gdip_FillRegion(pGraphics, pBrush, Region)
{
	Ptr := A_PtrSize ? "UPtr" : "UInt"

	return DllCall("gdiplus\GdipFillRegion", Ptr, pGraphics, Ptr, pBrush, Ptr, Region)
}

;#####################################################################################

; Function				Gdip_FillPath
; Description			This function uses a brush to fill a path in the Graphics of a bitmap
;
; pGraphics				Pointer to the Graphics of a bitmap
; pBrush				Pointer to a brush
; Region				Pointer to a Path
;
; return				status enumeration. 0 = success

Gdip_FillPath(pGraphics, pBrush, Path)
{
	Ptr := A_PtrSize ? "UPtr" : "UInt"

	return DllCall("gdiplus\GdipFillPath", Ptr, pGraphics, Ptr, pBrush, Ptr, Path)
}

;#####################################################################################

; Function				Gdip_DrawImagePointsRect
; Description			This function draws a bitmap into the Graphics of another bitmap and skews it
;
; pGraphics				Pointer to the Graphics of a bitmap
; pBitmap				Pointer to a bitmap to be drawn
; Points				Points passed as x1,y1|x2,y2|x3,y3 (3 points: top left, top right, bottom left) describing the drawing of the bitmap
; sx					x-coordinate of source upper-left corner
; sy					y-coordinate of source upper-left corner
; sw					width of source rectangle
; sh					height of source rectangle
; Matrix				a matrix used to alter image attributes when drawing
;
; return				status enumeration. 0 = success
;
; notes					if sx,sy,sw,sh are missed then the entire source bitmap will be used
;						Matrix can be omitted to just draw with no alteration to ARGB
;						Matrix may be passed as a digit from 0 - 1 to change just transparency
;						Matrix can be passed as a matrix with any delimiter

Gdip_DrawImagePointsRect(pGraphics, pBitmap, Points, sx="", sy="", sw="", sh="", Matrix=1)
{
	Ptr := A_PtrSize ? "UPtr" : "UInt"

	StringSplit, Points, Points, |
	VarSetCapacity(PointF, 8*Points0)
	Loop, %Points0%
	{
		StringSplit, Coord, Points%A_Index%, `,
		NumPut(Coord1, PointF, 8*(A_Index-1), "float"), NumPut(Coord2, PointF, (8*(A_Index-1))+4, "float")
	}

	if (Matrix&1 = "")
		ImageAttr := Gdip_SetImageAttributesColorMatrix(Matrix)
	else if (Matrix != 1)
		ImageAttr := Gdip_SetImageAttributesColorMatrix("1|0|0|0|0|0|1|0|0|0|0|0|1|0|0|0|0|0|" Matrix "|0|0|0|0|0|1")

	if (sx = "" && sy = "" && sw = "" && sh = "")
	{
		sx := 0, sy := 0
		sw := Gdip_GetImageWidth(pBitmap)
		sh := Gdip_GetImageHeight(pBitmap)
	}

	E := DllCall("gdiplus\GdipDrawImagePointsRect"
				, Ptr, pGraphics
				, Ptr, pBitmap
				, Ptr, &PointF
				, "int", Points0
				, "float", sx
				, "float", sy
				, "float", sw
				, "float", sh
				, "int", 2
				, Ptr, ImageAttr
				, Ptr, 0
				, Ptr, 0)
	if ImageAttr
		Gdip_DisposeImageAttributes(ImageAttr)
	return E
}

;#####################################################################################

; Function				Gdip_DrawImage
; Description			This function draws a bitmap into the Graphics of another bitmap
;
; pGraphics				Pointer to the Graphics of a bitmap
; pBitmap				Pointer to a bitmap to be drawn
; dx					x-coord of destination upper-left corner
; dy					y-coord of destination upper-left corner
; dw					width of destination image
; dh					height of destination image
; sx					x-coordinate of source upper-left corner
; sy					y-coordinate of source upper-left corner
; sw					width of source image
; sh					height of source image
; Matrix				a matrix used to alter image attributes when drawing
;
; return				status enumeration. 0 = success
;
; notes					if sx,sy,sw,sh are missed then the entire source bitmap will be used
;						Gdip_DrawImage performs faster
;						Matrix can be omitted to just draw with no alteration to ARGB
;						Matrix may be passed as a digit from 0 - 1 to change just transparency
;						Matrix can be passed as a matrix with any delimiter. For example:
;						MatrixBright=
;						(
;						1.5		|0		|0		|0		|0
;						0		|1.5	|0		|0		|0
;						0		|0		|1.5	|0		|0
;						0		|0		|0		|1		|0
;						0.05	|0.05	|0.05	|0		|1
;						)
;
; notes					MatrixBright = 1.5|0|0|0|0|0|1.5|0|0|0|0|0|1.5|0|0|0|0|0|1|0|0.05|0.05|0.05|0|1
;						MatrixGreyScale = 0.299|0.299|0.299|0|0|0.587|0.587|0.587|0|0|0.114|0.114|0.114|0|0|0|0|0|1|0|0|0|0|0|1
;						MatrixNegative = -1|0|0|0|0|0|-1|0|0|0|0|0|-1|0|0|0|0|0|1|0|0|0|0|0|1

Gdip_DrawImage(pGraphics, pBitmap, dx="", dy="", dw="", dh="", sx="", sy="", sw="", sh="", Matrix=1)
{
	Ptr := A_PtrSize ? "UPtr" : "UInt"

	if (Matrix&1 = "")
		ImageAttr := Gdip_SetImageAttributesColorMatrix(Matrix)
	else if (Matrix != 1)
		ImageAttr := Gdip_SetImageAttributesColorMatrix("1|0|0|0|0|0|1|0|0|0|0|0|1|0|0|0|0|0|" Matrix "|0|0|0|0|0|1")

	if (sx = "" && sy = "" && sw = "" && sh = "")
	{
		if (dx = "" && dy = "" && dw = "" && dh = "")
		{
			sx := dx := 0, sy := dy := 0
			sw := dw := Gdip_GetImageWidth(pBitmap)
			sh := dh := Gdip_GetImageHeight(pBitmap)
		}
		else
		{
			sx := sy := 0
			sw := Gdip_GetImageWidth(pBitmap)
			sh := Gdip_GetImageHeight(pBitmap)
		}
	}

	E := DllCall("gdiplus\GdipDrawImageRectRect"
				, Ptr, pGraphics
				, Ptr, pBitmap
				, "float", dx
				, "float", dy
				, "float", dw
				, "float", dh
				, "float", sx
				, "float", sy
				, "float", sw
				, "float", sh
				, "int", 2
				, Ptr, ImageAttr
				, Ptr, 0
				, Ptr, 0)
	if ImageAttr
		Gdip_DisposeImageAttributes(ImageAttr)
	return E
}

;#####################################################################################

; Function				Gdip_SetImageAttributesColorMatrix
; Description			This function creates an image matrix ready for drawing
;
; Matrix				a matrix used to alter image attributes when drawing
;						passed with any delimeter
;
; return				returns an image matrix on sucess or 0 if it fails
;
; notes					MatrixBright = 1.5|0|0|0|0|0|1.5|0|0|0|0|0|1.5|0|0|0|0|0|1|0|0.05|0.05|0.05|0|1
;						MatrixGreyScale = 0.299|0.299|0.299|0|0|0.587|0.587|0.587|0|0|0.114|0.114|0.114|0|0|0|0|0|1|0|0|0|0|0|1
;						MatrixNegative = -1|0|0|0|0|0|-1|0|0|0|0|0|-1|0|0|0|0|0|1|0|0|0|0|0|1

Gdip_SetImageAttributesColorMatrix(Matrix)
{
	Ptr := A_PtrSize ? "UPtr" : "UInt"

	VarSetCapacity(ColourMatrix, 100, 0)
	Matrix := RegExReplace(RegExReplace(Matrix, "^[^\d-\.]+([\d\.])", "$1", "", 1), "[^\d-\.]+", "|")
	StringSplit, Matrix, Matrix, |
	Loop, 25
	{
		Matrix := (Matrix%A_Index% != "") ? Matrix%A_Index% : Mod(A_Index-1, 6) ? 0 : 1
		NumPut(Matrix, ColourMatrix, (A_Index-1)*4, "float")
	}
	DllCall("gdiplus\GdipCreateImageAttributes", A_PtrSize ? "UPtr*" : "uint*", ImageAttr)
	DllCall("gdiplus\GdipSetImageAttributesColorMatrix", Ptr, ImageAttr, "int", 1, "int", 1, Ptr, &ColourMatrix, Ptr, 0, "int", 0)
	return ImageAttr
}

;#####################################################################################

; Function				Gdip_GraphicsFromImage
; Description			This function gets the graphics for a bitmap used for drawing functions
;
; pBitmap				Pointer to a bitmap to get the pointer to its graphics
;
; return				returns a pointer to the graphics of a bitmap
;
; notes					a bitmap can be drawn into the graphics of another bitmap

Gdip_GraphicsFromImage(pBitmap)
{
	DllCall("gdiplus\GdipGetImageGraphicsContext", A_PtrSize ? "UPtr" : "UInt", pBitmap, A_PtrSize ? "UPtr*" : "UInt*", pGraphics)
	return pGraphics
}

;#####################################################################################

; Function				Gdip_GraphicsFromHDC
; Description			This function gets the graphics from the handle to a device context
;
; hdc					This is the handle to the device context
;
; return				returns a pointer to the graphics of a bitmap
;
; notes					You can draw a bitmap into the graphics of another bitmap

Gdip_GraphicsFromHDC(hdc)
{
    DllCall("gdiplus\GdipCreateFromHDC", A_PtrSize ? "UPtr" : "UInt", hdc, A_PtrSize ? "UPtr*" : "UInt*", pGraphics)
    return pGraphics
}

;#####################################################################################

; Function				Gdip_GetDC
; Description			This function gets the device context of the passed Graphics
;
; hdc					This is the handle to the device context
;
; return				returns the device context for the graphics of a bitmap

Gdip_GetDC(pGraphics)
{
	DllCall("gdiplus\GdipGetDC", A_PtrSize ? "UPtr" : "UInt", pGraphics, A_PtrSize ? "UPtr*" : "UInt*", hdc)
	return hdc
}

;#####################################################################################

; Function				Gdip_ReleaseDC
; Description			This function releases a device context from use for further use
;
; pGraphics				Pointer to the graphics of a bitmap
; hdc					This is the handle to the device context
;
; return				status enumeration. 0 = success

Gdip_ReleaseDC(pGraphics, hdc)
{
	Ptr := A_PtrSize ? "UPtr" : "UInt"

	return DllCall("gdiplus\GdipReleaseDC", Ptr, pGraphics, Ptr, hdc)
}

;#####################################################################################

; Function				Gdip_GraphicsClear
; Description			Clears the graphics of a bitmap ready for further drawing
;
; pGraphics				Pointer to the graphics of a bitmap
; ARGB					The colour to clear the graphics to
;
; return				status enumeration. 0 = success
;
; notes					By default this will make the background invisible
;						Using clipping regions you can clear a particular area on the graphics rather than clearing the entire graphics

Gdip_GraphicsClear(pGraphics, ARGB=0x00ffffff)
{
    return DllCall("gdiplus\GdipGraphicsClear", A_PtrSize ? "UPtr" : "UInt", pGraphics, "int", ARGB)
}

;#####################################################################################

; Function				Gdip_BlurBitmap
; Description			Gives a pointer to a blurred bitmap from a pointer to a bitmap
;
; pBitmap				Pointer to a bitmap to be blurred
; Blur					The Amount to blur a bitmap by from 1 (least blur) to 100 (most blur)
;
; return				If the function succeeds, the return value is a pointer to the new blurred bitmap
;						-1 = The blur parameter is outside the range 1-100
;
; notes					This function will not dispose of the original bitmap

Gdip_BlurBitmap(pBitmap, Blur)
{
	if (Blur > 100) || (Blur < 1)
		return -1

	sWidth := Gdip_GetImageWidth(pBitmap), sHeight := Gdip_GetImageHeight(pBitmap)
	dWidth := sWidth//Blur, dHeight := sHeight//Blur

	pBitmap1 := Gdip_CreateBitmap(dWidth, dHeight)
	G1 := Gdip_GraphicsFromImage(pBitmap1)
	Gdip_SetInterpolationMode(G1, 7)
	Gdip_DrawImage(G1, pBitmap, 0, 0, dWidth, dHeight, 0, 0, sWidth, sHeight)

	Gdip_DeleteGraphics(G1)

	pBitmap2 := Gdip_CreateBitmap(sWidth, sHeight)
	G2 := Gdip_GraphicsFromImage(pBitmap2)
	Gdip_SetInterpolationMode(G2, 7)
	Gdip_DrawImage(G2, pBitmap1, 0, 0, sWidth, sHeight, 0, 0, dWidth, dHeight)

	Gdip_DeleteGraphics(G2)
	Gdip_DisposeImage(pBitmap1)
	return pBitmap2
}

;#####################################################################################

; Function:     		Gdip_SaveBitmapToFile
; Description:  		Saves a bitmap to a file in any supported format onto disk
;
; pBitmap				Pointer to a bitmap
; sOutput      			The name of the file that the bitmap will be saved to. Supported extensions are: .BMP,.DIB,.RLE,.JPG,.JPEG,.JPE,.JFIF,.GIF,.TIF,.TIFF,.PNG
; Quality      			If saving as jpg (.JPG,.JPEG,.JPE,.JFIF) then quality can be 1-100 with default at maximum quality
;
; return      			If the function succeeds, the return value is zero, otherwise:
;						-1 = Extension supplied is not a supported file format
;						-2 = Could not get a list of encoders on system
;						-3 = Could not find matching encoder for specified file format
;						-4 = Could not get WideChar name of output file
;						-5 = Could not save file to disk
;
; notes					This function will use the extension supplied from the sOutput parameter to determine the output format

Gdip_SaveBitmapToFile(pBitmap, sOutput, Quality=75)
{
	Ptr := A_PtrSize ? "UPtr" : "UInt"

	SplitPath, sOutput,,, Extension
	if Extension not in BMP,DIB,RLE,JPG,JPEG,JPE,JFIF,GIF,TIF,TIFF,PNG
		return -1
	Extension := "." Extension

	DllCall("gdiplus\GdipGetImageEncodersSize", "uint*", nCount, "uint*", nSize)
	VarSetCapacity(ci, nSize)
	DllCall("gdiplus\GdipGetImageEncoders", "uint", nCount, "uint", nSize, Ptr, &ci)
	if !(nCount && nSize)
		return -2

	If (A_IsUnicode){
		StrGet_Name := "StrGet"
		Loop, %nCount%
		{
			sString := %StrGet_Name%(NumGet(ci, (idx := (48+7*A_PtrSize)*(A_Index-1))+32+3*A_PtrSize), "UTF-16")
			if !InStr(sString, "*" Extension)
				continue

			pCodec := &ci+idx
			break
		}
	} else {
		Loop, %nCount%
		{
			Location := NumGet(ci, 76*(A_Index-1)+44)
			nSize := DllCall("WideCharToMultiByte", "uint", 0, "uint", 0, "uint", Location, "int", -1, "uint", 0, "int",  0, "uint", 0, "uint", 0)
			VarSetCapacity(sString, nSize)
			DllCall("WideCharToMultiByte", "uint", 0, "uint", 0, "uint", Location, "int", -1, "str", sString, "int", nSize, "uint", 0, "uint", 0)
			if !InStr(sString, "*" Extension)
				continue

			pCodec := &ci+76*(A_Index-1)
			break
		}
	}

	if !pCodec
		return -3

	if (Quality != 75)
	{
		Quality := (Quality < 0) ? 0 : (Quality > 100) ? 100 : Quality
		if Extension in .JPG,.JPEG,.JPE,.JFIF
		{
			DllCall("gdiplus\GdipGetEncoderParameterListSize", Ptr, pBitmap, Ptr, pCodec, "uint*", nSize)
			VarSetCapacity(EncoderParameters, nSize, 0)
			DllCall("gdiplus\GdipGetEncoderParameterList", Ptr, pBitmap, Ptr, pCodec, "uint", nSize, Ptr, &EncoderParameters)
			Loop, % NumGet(EncoderParameters, "UInt")      ;%
			{
				elem := (24+(A_PtrSize ? A_PtrSize : 4))*(A_Index-1) + 4 + (pad := A_PtrSize = 8 ? 4 : 0)
				if (NumGet(EncoderParameters, elem+16, "UInt") = 1) && (NumGet(EncoderParameters, elem+20, "UInt") = 6)
				{
					p := elem+&EncoderParameters-pad-4
					NumPut(Quality, NumGet(NumPut(4, NumPut(1, p+0)+20, "UInt")), "UInt")
					break
				}
			}
		}
	}

	if (!A_IsUnicode)
	{
		nSize := DllCall("MultiByteToWideChar", "uint", 0, "uint", 0, Ptr, &sOutput, "int", -1, Ptr, 0, "int", 0)
		VarSetCapacity(wOutput, nSize*2)
		DllCall("MultiByteToWideChar", "uint", 0, "uint", 0, Ptr, &sOutput, "int", -1, Ptr, &wOutput, "int", nSize)
		VarSetCapacity(wOutput, -1)
		if !VarSetCapacity(wOutput)
			return -4
		E := DllCall("gdiplus\GdipSaveImageToFile", Ptr, pBitmap, Ptr, &wOutput, Ptr, pCodec, "uint", p ? p : 0)
	}
	else
		E := DllCall("gdiplus\GdipSaveImageToFile", Ptr, pBitmap, Ptr, &sOutput, Ptr, pCodec, "uint", p ? p : 0)
	return E ? -5 : 0
}

;#####################################################################################

; Function				Gdip_GetPixel
; Description			Gets the ARGB of a pixel in a bitmap
;
; pBitmap				Pointer to a bitmap
; x						x-coordinate of the pixel
; y						y-coordinate of the pixel
;
; return				Returns the ARGB value of the pixel

Gdip_GetPixel(pBitmap, x, y)
{
	DllCall("gdiplus\GdipBitmapGetPixel", A_PtrSize ? "UPtr" : "UInt", pBitmap, "int", x, "int", y, "uint*", ARGB)
	return ARGB
}

;#####################################################################################

; Function				Gdip_SetPixel
; Description			Sets the ARGB of a pixel in a bitmap
;
; pBitmap				Pointer to a bitmap
; x						x-coordinate of the pixel
; y						y-coordinate of the pixel
;
; return				status enumeration. 0 = success

Gdip_SetPixel(pBitmap, x, y, ARGB)
{
   return DllCall("gdiplus\GdipBitmapSetPixel", A_PtrSize ? "UPtr" : "UInt", pBitmap, "int", x, "int", y, "int", ARGB)
}

;#####################################################################################

; Function				Gdip_GetImageWidth
; Description			Gives the width of a bitmap
;
; pBitmap				Pointer to a bitmap
;
; return				Returns the width in pixels of the supplied bitmap

Gdip_GetImageWidth(pBitmap)
{
   DllCall("gdiplus\GdipGetImageWidth", A_PtrSize ? "UPtr" : "UInt", pBitmap, "uint*", Width)
   return Width
}

;#####################################################################################

; Function				Gdip_GetImageHeight
; Description			Gives the height of a bitmap
;
; pBitmap				Pointer to a bitmap
;
; return				Returns the height in pixels of the supplied bitmap

Gdip_GetImageHeight(pBitmap)
{
   DllCall("gdiplus\GdipGetImageHeight", A_PtrSize ? "UPtr" : "UInt", pBitmap, "uint*", Height)
   return Height
}

;#####################################################################################

; Function				Gdip_GetDimensions
; Description			Gives the width and height of a bitmap
;
; pBitmap				Pointer to a bitmap
; Width					ByRef variable. This variable will be set to the width of the bitmap
; Height				ByRef variable. This variable will be set to the height of the bitmap
;
; return				No return value
;						Gdip_GetDimensions(pBitmap, ThisWidth, ThisHeight) will set ThisWidth to the width and ThisHeight to the height

Gdip_GetImageDimensions(pBitmap, ByRef Width, ByRef Height)
{
	Ptr := A_PtrSize ? "UPtr" : "UInt"
	DllCall("gdiplus\GdipGetImageWidth", Ptr, pBitmap, "uint*", Width)
	DllCall("gdiplus\GdipGetImageHeight", Ptr, pBitmap, "uint*", Height)
}

;#####################################################################################

Gdip_GetDimensions(pBitmap, ByRef Width, ByRef Height)
{
	Gdip_GetImageDimensions(pBitmap, Width, Height)
}

;#####################################################################################

Gdip_GetImagePixelFormat(pBitmap)
{
	DllCall("gdiplus\GdipGetImagePixelFormat", A_PtrSize ? "UPtr" : "UInt", pBitmap, A_PtrSize ? "UPtr*" : "UInt*", Format)
	return Format
}

;#####################################################################################

; Function				Gdip_GetDpiX
; Description			Gives the horizontal dots per inch of the graphics of a bitmap
;
; pBitmap				Pointer to a bitmap
; Width					ByRef variable. This variable will be set to the width of the bitmap
; Height				ByRef variable. This variable will be set to the height of the bitmap
;
; return				No return value
;						Gdip_GetDimensions(pBitmap, ThisWidth, ThisHeight) will set ThisWidth to the width and ThisHeight to the height

Gdip_GetDpiX(pGraphics)
{
	DllCall("gdiplus\GdipGetDpiX", A_PtrSize ? "UPtr" : "uint", pGraphics, "float*", dpix)
	return Round(dpix)
}

;#####################################################################################

Gdip_GetDpiY(pGraphics)
{
	DllCall("gdiplus\GdipGetDpiY", A_PtrSize ? "UPtr" : "uint", pGraphics, "float*", dpiy)
	return Round(dpiy)
}

;#####################################################################################

Gdip_GetImageHorizontalResolution(pBitmap)
{
	DllCall("gdiplus\GdipGetImageHorizontalResolution", A_PtrSize ? "UPtr" : "uint", pBitmap, "float*", dpix)
	return Round(dpix)
}

;#####################################################################################

Gdip_GetImageVerticalResolution(pBitmap)
{
	DllCall("gdiplus\GdipGetImageVerticalResolution", A_PtrSize ? "UPtr" : "uint", pBitmap, "float*", dpiy)
	return Round(dpiy)
}

;#####################################################################################

Gdip_BitmapSetResolution(pBitmap, dpix, dpiy)
{
	return DllCall("gdiplus\GdipBitmapSetResolution", A_PtrSize ? "UPtr" : "uint", pBitmap, "float", dpix, "float", dpiy)
}

;#####################################################################################

Gdip_CreateBitmapFromFile(sFile, IconNumber=1, IconSize="")
{
	Ptr := A_PtrSize ? "UPtr" : "UInt"
	, PtrA := A_PtrSize ? "UPtr*" : "UInt*"

	SplitPath, sFile,,, ext
	if ext in exe,dll
	{
		Sizes := IconSize ? IconSize : 256 "|" 128 "|" 64 "|" 48 "|" 32 "|" 16
		BufSize := 16 + (2*(A_PtrSize ? A_PtrSize : 4))

		VarSetCapacity(buf, BufSize, 0)
		Loop, Parse, Sizes, |
		{
			DllCall("PrivateExtractIcons", "str", sFile, "int", IconNumber-1, "int", A_LoopField, "int", A_LoopField, PtrA, hIcon, PtrA, 0, "uint", 1, "uint", 0)

			if !hIcon
				continue

			if !DllCall("GetIconInfo", Ptr, hIcon, Ptr, &buf)
			{
				DestroyIcon(hIcon)
				continue
			}

			hbmMask  := NumGet(buf, 12 + ((A_PtrSize ? A_PtrSize : 4) - 4))
			hbmColor := NumGet(buf, 12 + ((A_PtrSize ? A_PtrSize : 4) - 4) + (A_PtrSize ? A_PtrSize : 4))
			if !(hbmColor && DllCall("GetObject", Ptr, hbmColor, "int", BufSize, Ptr, &buf))
			{
				DestroyIcon(hIcon)
				continue
			}
			break
		}
		if !hIcon
			return -1

		Width := NumGet(buf, 4, "int"), Height := NumGet(buf, 8, "int")
		hbm := CreateDIBSection(Width, -Height), hdc := CreateCompatibleDC(), obm := SelectObject(hdc, hbm)
		if !DllCall("DrawIconEx", Ptr, hdc, "int", 0, "int", 0, Ptr, hIcon, "uint", Width, "uint", Height, "uint", 0, Ptr, 0, "uint", 3)
		{
			DestroyIcon(hIcon)
			return -2
		}

		VarSetCapacity(dib, 104)
		DllCall("GetObject", Ptr, hbm, "int", A_PtrSize = 8 ? 104 : 84, Ptr, &dib) ; sizeof(DIBSECTION) = 76+2*(A_PtrSize=8?4:0)+2*A_PtrSize
		Stride := NumGet(dib, 12, "Int"), Bits := NumGet(dib, 20 + (A_PtrSize = 8 ? 4 : 0)) ; padding
		DllCall("gdiplus\GdipCreateBitmapFromScan0", "int", Width, "int", Height, "int", Stride, "int", 0x26200A, Ptr, Bits, PtrA, pBitmapOld)
		pBitmap := Gdip_CreateBitmap(Width, Height)
		G := Gdip_GraphicsFromImage(pBitmap)
		, Gdip_DrawImage(G, pBitmapOld, 0, 0, Width, Height, 0, 0, Width, Height)
		SelectObject(hdc, obm), DeleteObject(hbm), DeleteDC(hdc)
		Gdip_DeleteGraphics(G), Gdip_DisposeImage(pBitmapOld)
		DestroyIcon(hIcon)
	}
	else
	{
		if (!A_IsUnicode)
		{
			VarSetCapacity(wFile, 1024)
			DllCall("kernel32\MultiByteToWideChar", "uint", 0, "uint", 0, Ptr, &sFile, "int", -1, Ptr, &wFile, "int", 512)
			DllCall("gdiplus\GdipCreateBitmapFromFile", Ptr, &wFile, PtrA, pBitmap)
		}
		else
			DllCall("gdiplus\GdipCreateBitmapFromFile", Ptr, &sFile, PtrA, pBitmap)
	}

	return pBitmap
}

;#####################################################################################

Gdip_CreateBitmapFromHBITMAP(hBitmap, Palette=0)
{
	Ptr := A_PtrSize ? "UPtr" : "UInt"

	DllCall("gdiplus\GdipCreateBitmapFromHBITMAP", Ptr, hBitmap, Ptr, Palette, A_PtrSize ? "UPtr*" : "uint*", pBitmap)
	return pBitmap
}

;#####################################################################################

Gdip_CreateHBITMAPFromBitmap(pBitmap, Background=0xffffffff)
{
	DllCall("gdiplus\GdipCreateHBITMAPFromBitmap", A_PtrSize ? "UPtr" : "UInt", pBitmap, A_PtrSize ? "UPtr*" : "uint*", hbm, "int", Background)
	return hbm
}

;#####################################################################################

Gdip_CreateBitmapFromHICON(hIcon)
{
	DllCall("gdiplus\GdipCreateBitmapFromHICON", A_PtrSize ? "UPtr" : "UInt", hIcon, A_PtrSize ? "UPtr*" : "uint*", pBitmap)
	return pBitmap
}

;#####################################################################################

Gdip_CreateHICONFromBitmap(pBitmap)
{
	DllCall("gdiplus\GdipCreateHICONFromBitmap", A_PtrSize ? "UPtr" : "UInt", pBitmap, A_PtrSize ? "UPtr*" : "uint*", hIcon)
	return hIcon
}

;#####################################################################################

Gdip_CreateBitmap(Width, Height, Format=0x26200A)
{
    DllCall("gdiplus\GdipCreateBitmapFromScan0", "int", Width, "int", Height, "int", 0, "int", Format, A_PtrSize ? "UPtr" : "UInt", 0, A_PtrSize ? "UPtr*" : "uint*", pBitmap)
    Return pBitmap
}

;#####################################################################################

Gdip_CreateBitmapFromClipboard()
{
	Ptr := A_PtrSize ? "UPtr" : "UInt"

	if !DllCall("OpenClipboard", Ptr, 0)
		return -1
	if !DllCall("IsClipboardFormatAvailable", "uint", 8)
		return -2
	if !hBitmap := DllCall("GetClipboardData", "uint", 2, Ptr)
		return -3
	if !pBitmap := Gdip_CreateBitmapFromHBITMAP(hBitmap)
		return -4
	if !DllCall("CloseClipboard")
		return -5
	DeleteObject(hBitmap)
	return pBitmap
}

;#####################################################################################

Gdip_SetBitmapToClipboard(pBitmap)
{
	Ptr := A_PtrSize ? "UPtr" : "UInt"
	off1 := A_PtrSize = 8 ? 52 : 44, off2 := A_PtrSize = 8 ? 32 : 24
	hBitmap := Gdip_CreateHBITMAPFromBitmap(pBitmap)
	DllCall("GetObject", Ptr, hBitmap, "int", VarSetCapacity(oi, A_PtrSize = 8 ? 104 : 84, 0), Ptr, &oi)
	hdib := DllCall("GlobalAlloc", "uint", 2, Ptr, 40+NumGet(oi, off1, "UInt"), Ptr)
	pdib := DllCall("GlobalLock", Ptr, hdib, Ptr)
	DllCall("RtlMoveMemory", Ptr, pdib, Ptr, &oi+off2, Ptr, 40)
	DllCall("RtlMoveMemory", Ptr, pdib+40, Ptr, NumGet(oi, off2 - (A_PtrSize ? A_PtrSize : 4), Ptr), Ptr, NumGet(oi, off1, "UInt"))
	DllCall("GlobalUnlock", Ptr, hdib)
	DllCall("DeleteObject", Ptr, hBitmap)
	DllCall("OpenClipboard", Ptr, 0)
	DllCall("EmptyClipboard")
	DllCall("SetClipboardData", "uint", 8, Ptr, hdib)
	DllCall("CloseClipboard")
}

;#####################################################################################

Gdip_CloneBitmapArea(pBitmap, x, y, w, h, Format=0x26200A)
{
	DllCall("gdiplus\GdipCloneBitmapArea"
					, "float", x
					, "float", y
					, "float", w
					, "float", h
					, "int", Format
					, A_PtrSize ? "UPtr" : "UInt", pBitmap
					, A_PtrSize ? "UPtr*" : "UInt*", pBitmapDest)
	return pBitmapDest
}

;#####################################################################################
; Create resources
;#####################################################################################

Gdip_CreatePen(ARGB, w)
{
   DllCall("gdiplus\GdipCreatePen1", "UInt", ARGB, "float", w, "int", 2, A_PtrSize ? "UPtr*" : "UInt*", pPen)
   return pPen
}

;#####################################################################################

Gdip_CreatePenFromBrush(pBrush, w)
{
	DllCall("gdiplus\GdipCreatePen2", A_PtrSize ? "UPtr" : "UInt", pBrush, "float", w, "int", 2, A_PtrSize ? "UPtr*" : "UInt*", pPen)
	return pPen
}

;#####################################################################################

Gdip_BrushCreateSolid(ARGB=0xff000000)
{
	DllCall("gdiplus\GdipCreateSolidFill", "UInt", ARGB, A_PtrSize ? "UPtr*" : "UInt*", pBrush)
	return pBrush
}

;#####################################################################################

; HatchStyleHorizontal = 0
; HatchStyleVertical = 1
; HatchStyleForwardDiagonal = 2
; HatchStyleBackwardDiagonal = 3
; HatchStyleCross = 4
; HatchStyleDiagonalCross = 5
; HatchStyle05Percent = 6
; HatchStyle10Percent = 7
; HatchStyle20Percent = 8
; HatchStyle25Percent = 9
; HatchStyle30Percent = 10
; HatchStyle40Percent = 11
; HatchStyle50Percent = 12
; HatchStyle60Percent = 13
; HatchStyle70Percent = 14
; HatchStyle75Percent = 15
; HatchStyle80Percent = 16
; HatchStyle90Percent = 17
; HatchStyleLightDownwardDiagonal = 18
; HatchStyleLightUpwardDiagonal = 19
; HatchStyleDarkDownwardDiagonal = 20
; HatchStyleDarkUpwardDiagonal = 21
; HatchStyleWideDownwardDiagonal = 22
; HatchStyleWideUpwardDiagonal = 23
; HatchStyleLightVertical = 24
; HatchStyleLightHorizontal = 25
; HatchStyleNarrowVertical = 26
; HatchStyleNarrowHorizontal = 27
; HatchStyleDarkVertical = 28
; HatchStyleDarkHorizontal = 29
; HatchStyleDashedDownwardDiagonal = 30
; HatchStyleDashedUpwardDiagonal = 31
; HatchStyleDashedHorizontal = 32
; HatchStyleDashedVertical = 33
; HatchStyleSmallConfetti = 34
; HatchStyleLargeConfetti = 35
; HatchStyleZigZag = 36
; HatchStyleWave = 37
; HatchStyleDiagonalBrick = 38
; HatchStyleHorizontalBrick = 39
; HatchStyleWeave = 40
; HatchStylePlaid = 41
; HatchStyleDivot = 42
; HatchStyleDottedGrid = 43
; HatchStyleDottedDiamond = 44
; HatchStyleShingle = 45
; HatchStyleTrellis = 46
; HatchStyleSphere = 47
; HatchStyleSmallGrid = 48
; HatchStyleSmallCheckerBoard = 49
; HatchStyleLargeCheckerBoard = 50
; HatchStyleOutlinedDiamond = 51
; HatchStyleSolidDiamond = 52
; HatchStyleTotal = 53
Gdip_BrushCreateHatch(ARGBfront, ARGBback, HatchStyle=0)
{
	DllCall("gdiplus\GdipCreateHatchBrush", "int", HatchStyle, "UInt", ARGBfront, "UInt", ARGBback, A_PtrSize ? "UPtr*" : "UInt*", pBrush)
	return pBrush
}

;#####################################################################################

Gdip_CreateTextureBrush(pBitmap, WrapMode=1, x=0, y=0, w="", h="")
{
	Ptr := A_PtrSize ? "UPtr" : "UInt"
	, PtrA := A_PtrSize ? "UPtr*" : "UInt*"

	if !(w && h)
		DllCall("gdiplus\GdipCreateTexture", Ptr, pBitmap, "int", WrapMode, PtrA, pBrush)
	else
		DllCall("gdiplus\GdipCreateTexture2", Ptr, pBitmap, "int", WrapMode, "float", x, "float", y, "float", w, "float", h, PtrA, pBrush)
	return pBrush
}

;#####################################################################################

; WrapModeTile = 0
; WrapModeTileFlipX = 1
; WrapModeTileFlipY = 2
; WrapModeTileFlipXY = 3
; WrapModeClamp = 4
Gdip_CreateLineBrush(x1, y1, x2, y2, ARGB1, ARGB2, WrapMode=1)
{
	Ptr := A_PtrSize ? "UPtr" : "UInt"

	CreatePointF(PointF1, x1, y1), CreatePointF(PointF2, x2, y2)
	DllCall("gdiplus\GdipCreateLineBrush", Ptr, &PointF1, Ptr, &PointF2, "Uint", ARGB1, "Uint", ARGB2, "int", WrapMode, A_PtrSize ? "UPtr*" : "UInt*", LGpBrush)
	return LGpBrush
}

;#####################################################################################

; LinearGradientModeHorizontal = 0
; LinearGradientModeVertical = 1
; LinearGradientModeForwardDiagonal = 2
; LinearGradientModeBackwardDiagonal = 3
Gdip_CreateLineBrushFromRect(x, y, w, h, ARGB1, ARGB2, LinearGradientMode=1, WrapMode=1)
{
	CreateRectF(RectF, x, y, w, h)
	DllCall("gdiplus\GdipCreateLineBrushFromRect", A_PtrSize ? "UPtr" : "UInt", &RectF, "int", ARGB1, "int", ARGB2, "int", LinearGradientMode, "int", WrapMode, A_PtrSize ? "UPtr*" : "UInt*", LGpBrush)
	return LGpBrush
}

;#####################################################################################

Gdip_CloneBrush(pBrush)
{
	DllCall("gdiplus\GdipCloneBrush", A_PtrSize ? "UPtr" : "UInt", pBrush, A_PtrSize ? "UPtr*" : "UInt*", pBrushClone)
	return pBrushClone
}

;#####################################################################################
; Delete resources
;#####################################################################################

Gdip_DeletePen(pPen)
{
   return DllCall("gdiplus\GdipDeletePen", A_PtrSize ? "UPtr" : "UInt", pPen)
}

;#####################################################################################

Gdip_DeleteBrush(pBrush)
{
   return DllCall("gdiplus\GdipDeleteBrush", A_PtrSize ? "UPtr" : "UInt", pBrush)
}

;#####################################################################################

Gdip_DisposeImage(pBitmap)
{
   return DllCall("gdiplus\GdipDisposeImage", A_PtrSize ? "UPtr" : "UInt", pBitmap)
}

;#####################################################################################

Gdip_DeleteGraphics(pGraphics)
{
   return DllCall("gdiplus\GdipDeleteGraphics", A_PtrSize ? "UPtr" : "UInt", pGraphics)
}

;#####################################################################################

Gdip_DisposeImageAttributes(ImageAttr)
{
	return DllCall("gdiplus\GdipDisposeImageAttributes", A_PtrSize ? "UPtr" : "UInt", ImageAttr)
}

;#####################################################################################

Gdip_DeleteFont(hFont)
{
   return DllCall("gdiplus\GdipDeleteFont", A_PtrSize ? "UPtr" : "UInt", hFont)
}

;#####################################################################################

Gdip_DeleteStringFormat(hFormat)
{
   return DllCall("gdiplus\GdipDeleteStringFormat", A_PtrSize ? "UPtr" : "UInt", hFormat)
}

;#####################################################################################

Gdip_DeleteFontFamily(hFamily)
{
   return DllCall("gdiplus\GdipDeleteFontFamily", A_PtrSize ? "UPtr" : "UInt", hFamily)
}

;#####################################################################################

Gdip_DeleteMatrix(Matrix)
{
   return DllCall("gdiplus\GdipDeleteMatrix", A_PtrSize ? "UPtr" : "UInt", Matrix)
}

;#####################################################################################
; Text functions
;#####################################################################################

Gdip_TextToGraphics(pGraphics, Text, Options, Font="Arial", Width="", Height="", Measure=0)
{
	IWidth := Width, IHeight:= Height

	RegExMatch(Options, "i)X([\-\d\.]+)(p*)", xpos)
	RegExMatch(Options, "i)Y([\-\d\.]+)(p*)", ypos)
	RegExMatch(Options, "i)W([\-\d\.]+)(p*)", Width)
	RegExMatch(Options, "i)H([\-\d\.]+)(p*)", Height)
	RegExMatch(Options, "i)C(?!(entre|enter))([a-f\d]+)", Colour)
	RegExMatch(Options, "i)Top|Up|Bottom|Down|vCentre|vCenter", vPos)
	RegExMatch(Options, "i)NoWrap", NoWrap)
	RegExMatch(Options, "i)R(\d)", Rendering)
	RegExMatch(Options, "i)S(\d+)(p*)", Size)

	if !Gdip_DeleteBrush(Gdip_CloneBrush(Colour2))
		PassBrush := 1, pBrush := Colour2

	if !(IWidth && IHeight) && (xpos2 || ypos2 || Width2 || Height2 || Size2)
		return -1

	Style := 0, Styles := "Regular|Bold|Italic|BoldItalic|Underline|Strikeout"
	Loop, Parse, Styles, |
	{
		if RegExMatch(Options, "\b" A_loopField)
		Style |= (A_LoopField != "StrikeOut") ? (A_Index-1) : 8
	}

	Align := 0, Alignments := "Near|Left|Centre|Center|Far|Right"
	Loop, Parse, Alignments, |
	{
		if RegExMatch(Options, "\b" A_loopField)
			Align |= A_Index//2.1      ; 0|0|1|1|2|2
	}

	xpos := (xpos1 != "") ? xpos2 ? IWidth*(xpos1/100) : xpos1 : 0
	ypos := (ypos1 != "") ? ypos2 ? IHeight*(ypos1/100) : ypos1 : 0
	Width := Width1 ? Width2 ? IWidth*(Width1/100) : Width1 : IWidth
	Height := Height1 ? Height2 ? IHeight*(Height1/100) : Height1 : IHeight
	if !PassBrush
		Colour := "0x" (Colour2 ? Colour2 : "ff000000")
	Rendering := ((Rendering1 >= 0) && (Rendering1 <= 5)) ? Rendering1 : 4
	Size := (Size1 > 0) ? Size2 ? IHeight*(Size1/100) : Size1 : 12

	hFamily := Gdip_FontFamilyCreate(Font)
	hFont := Gdip_FontCreate(hFamily, Size, Style)
	FormatStyle := NoWrap ? 0x4000 | 0x1000 : 0x4000
	hFormat := Gdip_StringFormatCreate(FormatStyle)
	pBrush := PassBrush ? pBrush : Gdip_BrushCreateSolid(Colour)
	if !(hFamily && hFont && hFormat && pBrush && pGraphics)
		return !pGraphics ? -2 : !hFamily ? -3 : !hFont ? -4 : !hFormat ? -5 : !pBrush ? -6 : 0

	CreateRectF(RC, xpos, ypos, Width, Height)
	Gdip_SetStringFormatAlign(hFormat, Align)
	Gdip_SetTextRenderingHint(pGraphics, Rendering)
	ReturnRC := Gdip_MeasureString(pGraphics, Text, hFont, hFormat, RC)

	if vPos
	{
		StringSplit, ReturnRC, ReturnRC, |

		if (vPos = "vCentre") || (vPos = "vCenter")
			ypos += (Height-ReturnRC4)//2
		else if (vPos = "Top") || (vPos = "Up")
			ypos := 0
		else if (vPos = "Bottom") || (vPos = "Down")
			ypos := Height-ReturnRC4

		CreateRectF(RC, xpos, ypos, Width, ReturnRC4)
		ReturnRC := Gdip_MeasureString(pGraphics, Text, hFont, hFormat, RC)
	}

	if !Measure
		E := Gdip_DrawString(pGraphics, Text, hFont, hFormat, pBrush, RC)

	if !PassBrush
		Gdip_DeleteBrush(pBrush)
	Gdip_DeleteStringFormat(hFormat)
	Gdip_DeleteFont(hFont)
	Gdip_DeleteFontFamily(hFamily)
	return E ? E : ReturnRC
}

;#####################################################################################

Gdip_DrawString(pGraphics, sString, hFont, hFormat, pBrush, ByRef RectF)
{
	Ptr := A_PtrSize ? "UPtr" : "UInt"

	if (!A_IsUnicode)
	{
		nSize := DllCall("MultiByteToWideChar", "uint", 0, "uint", 0, Ptr, &sString, "int", -1, Ptr, 0, "int", 0)
		VarSetCapacity(wString, nSize*2)
		DllCall("MultiByteToWideChar", "uint", 0, "uint", 0, Ptr, &sString, "int", -1, Ptr, &wString, "int", nSize)
	}

	return DllCall("gdiplus\GdipDrawString"
					, Ptr, pGraphics
					, Ptr, A_IsUnicode ? &sString : &wString
					, "int", -1
					, Ptr, hFont
					, Ptr, &RectF
					, Ptr, hFormat
					, Ptr, pBrush)
}

;#####################################################################################

Gdip_MeasureString(pGraphics, sString, hFont, hFormat, ByRef RectF)
{
	Ptr := A_PtrSize ? "UPtr" : "UInt"

	VarSetCapacity(RC, 16)
	if !A_IsUnicode
	{
		nSize := DllCall("MultiByteToWideChar", "uint", 0, "uint", 0, Ptr, &sString, "int", -1, "uint", 0, "int", 0)
		VarSetCapacity(wString, nSize*2)
		DllCall("MultiByteToWideChar", "uint", 0, "uint", 0, Ptr, &sString, "int", -1, Ptr, &wString, "int", nSize)
	}

	DllCall("gdiplus\GdipMeasureString"
					, Ptr, pGraphics
					, Ptr, A_IsUnicode ? &sString : &wString
					, "int", -1
					, Ptr, hFont
					, Ptr, &RectF
					, Ptr, hFormat
					, Ptr, &RC
					, "uint*", Chars
					, "uint*", Lines)

	return &RC ? NumGet(RC, 0, "float") "|" NumGet(RC, 4, "float") "|" NumGet(RC, 8, "float") "|" NumGet(RC, 12, "float") "|" Chars "|" Lines : 0
}

; Near = 0
; Center = 1
; Far = 2
Gdip_SetStringFormatAlign(hFormat, Align)
{
   return DllCall("gdiplus\GdipSetStringFormatAlign", A_PtrSize ? "UPtr" : "UInt", hFormat, "int", Align)
}

; StringFormatFlagsDirectionRightToLeft    = 0x00000001
; StringFormatFlagsDirectionVertical       = 0x00000002
; StringFormatFlagsNoFitBlackBox           = 0x00000004
; StringFormatFlagsDisplayFormatControl    = 0x00000020
; StringFormatFlagsNoFontFallback          = 0x00000400
; StringFormatFlagsMeasureTrailingSpaces   = 0x00000800
; StringFormatFlagsNoWrap                  = 0x00001000
; StringFormatFlagsLineLimit               = 0x00002000
; StringFormatFlagsNoClip                  = 0x00004000
Gdip_StringFormatCreate(Format=0, Lang=0)
{
   DllCall("gdiplus\GdipCreateStringFormat", "int", Format, "int", Lang, A_PtrSize ? "UPtr*" : "UInt*", hFormat)
   return hFormat
}

; Regular = 0
; Bold = 1
; Italic = 2
; BoldItalic = 3
; Underline = 4
; Strikeout = 8
Gdip_FontCreate(hFamily, Size, Style=0)
{
   DllCall("gdiplus\GdipCreateFont", A_PtrSize ? "UPtr" : "UInt", hFamily, "float", Size, "int", Style, "int", 0, A_PtrSize ? "UPtr*" : "UInt*", hFont)
   return hFont
}

Gdip_FontFamilyCreate(Font)
{
	Ptr := A_PtrSize ? "UPtr" : "UInt"

	if (!A_IsUnicode)
	{
		nSize := DllCall("MultiByteToWideChar", "uint", 0, "uint", 0, Ptr, &Font, "int", -1, "uint", 0, "int", 0)
		VarSetCapacity(wFont, nSize*2)
		DllCall("MultiByteToWideChar", "uint", 0, "uint", 0, Ptr, &Font, "int", -1, Ptr, &wFont, "int", nSize)
	}

	DllCall("gdiplus\GdipCreateFontFamilyFromName"
					, Ptr, A_IsUnicode ? &Font : &wFont
					, "uint", 0
					, A_PtrSize ? "UPtr*" : "UInt*", hFamily)

	return hFamily
}

;#####################################################################################
; Matrix functions
;#####################################################################################

Gdip_CreateAffineMatrix(m11, m12, m21, m22, x, y)
{
   DllCall("gdiplus\GdipCreateMatrix2", "float", m11, "float", m12, "float", m21, "float", m22, "float", x, "float", y, A_PtrSize ? "UPtr*" : "UInt*", Matrix)
   return Matrix
}

Gdip_CreateMatrix()
{
   DllCall("gdiplus\GdipCreateMatrix", A_PtrSize ? "UPtr*" : "UInt*", Matrix)
   return Matrix
}

;#####################################################################################
; GraphicsPath functions
;#####################################################################################

; Alternate = 0
; Winding = 1
Gdip_CreatePath(BrushMode=0)
{
	DllCall("gdiplus\GdipCreatePath", "int", BrushMode, A_PtrSize ? "UPtr*" : "UInt*", Path)
	return Path
}

Gdip_AddPathEllipse(Path, x, y, w, h)
{
	return DllCall("gdiplus\GdipAddPathEllipse", A_PtrSize ? "UPtr" : "UInt", Path, "float", x, "float", y, "float", w, "float", h)
}

Gdip_AddPathPolygon(Path, Points)
{
	Ptr := A_PtrSize ? "UPtr" : "UInt"

	StringSplit, Points, Points, |
	VarSetCapacity(PointF, 8*Points0)
	Loop, %Points0%
	{
		StringSplit, Coord, Points%A_Index%, `,
		NumPut(Coord1, PointF, 8*(A_Index-1), "float"), NumPut(Coord2, PointF, (8*(A_Index-1))+4, "float")
	}

	return DllCall("gdiplus\GdipAddPathPolygon", Ptr, Path, Ptr, &PointF, "int", Points0)
}

Gdip_DeletePath(Path)
{
	return DllCall("gdiplus\GdipDeletePath", A_PtrSize ? "UPtr" : "UInt", Path)
}

;#####################################################################################
; Quality functions
;#####################################################################################

; SystemDefault = 0
; SingleBitPerPixelGridFit = 1
; SingleBitPerPixel = 2
; AntiAliasGridFit = 3
; AntiAlias = 4
Gdip_SetTextRenderingHint(pGraphics, RenderingHint)
{
	return DllCall("gdiplus\GdipSetTextRenderingHint", A_PtrSize ? "UPtr" : "UInt", pGraphics, "int", RenderingHint)
}

; Default = 0
; LowQuality = 1
; HighQuality = 2
; Bilinear = 3
; Bicubic = 4
; NearestNeighbor = 5
; HighQualityBilinear = 6
; HighQualityBicubic = 7
Gdip_SetInterpolationMode(pGraphics, InterpolationMode)
{
   return DllCall("gdiplus\GdipSetInterpolationMode", A_PtrSize ? "UPtr" : "UInt", pGraphics, "int", InterpolationMode)
}

; Default = 0
; HighSpeed = 1
; HighQuality = 2
; None = 3
; AntiAlias = 4
Gdip_SetSmoothingMode(pGraphics, SmoothingMode)
{
   return DllCall("gdiplus\GdipSetSmoothingMode", A_PtrSize ? "UPtr" : "UInt", pGraphics, "int", SmoothingMode)
}

; CompositingModeSourceOver = 0 (blended)
; CompositingModeSourceCopy = 1 (overwrite)
Gdip_SetCompositingMode(pGraphics, CompositingMode=0)
{
   return DllCall("gdiplus\GdipSetCompositingMode", A_PtrSize ? "UPtr" : "UInt", pGraphics, "int", CompositingMode)
}

;#####################################################################################
; Extra functions
;#####################################################################################

Gdip_Startup()
{
	Ptr := A_PtrSize ? "UPtr" : "UInt"

	if !DllCall("GetModuleHandle", "str", "gdiplus", Ptr)
		DllCall("LoadLibrary", "str", "gdiplus")
	VarSetCapacity(si, A_PtrSize = 8 ? 24 : 16, 0), si := Chr(1)
	DllCall("gdiplus\GdiplusStartup", A_PtrSize ? "UPtr*" : "uint*", pToken, Ptr, &si, Ptr, 0)
	return pToken
}

Gdip_Shutdown(pToken)
{
	Ptr := A_PtrSize ? "UPtr" : "UInt"

	DllCall("gdiplus\GdiplusShutdown", Ptr, pToken)
	if hModule := DllCall("GetModuleHandle", "str", "gdiplus", Ptr)
		DllCall("FreeLibrary", Ptr, hModule)
	return 0
}

; Prepend = 0; The new operation is applied before the old operation.
; Append = 1; The new operation is applied after the old operation.
Gdip_RotateWorldTransform(pGraphics, Angle, MatrixOrder=0)
{
	return DllCall("gdiplus\GdipRotateWorldTransform", A_PtrSize ? "UPtr" : "UInt", pGraphics, "float", Angle, "int", MatrixOrder)
}

Gdip_ScaleWorldTransform(pGraphics, x, y, MatrixOrder=0)
{
	return DllCall("gdiplus\GdipScaleWorldTransform", A_PtrSize ? "UPtr" : "UInt", pGraphics, "float", x, "float", y, "int", MatrixOrder)
}

Gdip_TranslateWorldTransform(pGraphics, x, y, MatrixOrder=0)
{
	return DllCall("gdiplus\GdipTranslateWorldTransform", A_PtrSize ? "UPtr" : "UInt", pGraphics, "float", x, "float", y, "int", MatrixOrder)
}

Gdip_ResetWorldTransform(pGraphics)
{
	return DllCall("gdiplus\GdipResetWorldTransform", A_PtrSize ? "UPtr" : "UInt", pGraphics)
}

Gdip_GetRotatedTranslation(Width, Height, Angle, ByRef xTranslation, ByRef yTranslation)
{
	pi := 3.14159, TAngle := Angle*(pi/180)

	Bound := (Angle >= 0) ? Mod(Angle, 360) : 360-Mod(-Angle, -360)
	if ((Bound >= 0) && (Bound <= 90))
		xTranslation := Height*Sin(TAngle), yTranslation := 0
	else if ((Bound > 90) && (Bound <= 180))
		xTranslation := (Height*Sin(TAngle))-(Width*Cos(TAngle)), yTranslation := -Height*Cos(TAngle)
	else if ((Bound > 180) && (Bound <= 270))
		xTranslation := -(Width*Cos(TAngle)), yTranslation := -(Height*Cos(TAngle))-(Width*Sin(TAngle))
	else if ((Bound > 270) && (Bound <= 360))
		xTranslation := 0, yTranslation := -Width*Sin(TAngle)
}

Gdip_GetRotatedDimensions(Width, Height, Angle, ByRef RWidth, ByRef RHeight)
{
	pi := 3.14159, TAngle := Angle*(pi/180)
	if !(Width && Height)
		return -1
	RWidth := Ceil(Abs(Width*Cos(TAngle))+Abs(Height*Sin(TAngle)))
	RHeight := Ceil(Abs(Width*Sin(TAngle))+Abs(Height*Cos(Tangle)))
}

; RotateNoneFlipNone   = 0
; Rotate90FlipNone     = 1
; Rotate180FlipNone    = 2
; Rotate270FlipNone    = 3
; RotateNoneFlipX      = 4
; Rotate90FlipX        = 5
; Rotate180FlipX       = 6
; Rotate270FlipX       = 7
; RotateNoneFlipY      = Rotate180FlipX
; Rotate90FlipY        = Rotate270FlipX
; Rotate180FlipY       = RotateNoneFlipX
; Rotate270FlipY       = Rotate90FlipX
; RotateNoneFlipXY     = Rotate180FlipNone
; Rotate90FlipXY       = Rotate270FlipNone
; Rotate180FlipXY      = RotateNoneFlipNone
; Rotate270FlipXY      = Rotate90FlipNone

Gdip_ImageRotateFlip(pBitmap, RotateFlipType=1)
{
	return DllCall("gdiplus\GdipImageRotateFlip", A_PtrSize ? "UPtr" : "UInt", pBitmap, "int", RotateFlipType)
}

; Replace = 0
; Intersect = 1
; Union = 2
; Xor = 3
; Exclude = 4
; Complement = 5
Gdip_SetClipRect(pGraphics, x, y, w, h, CombineMode=0)
{
   return DllCall("gdiplus\GdipSetClipRect",  A_PtrSize ? "UPtr" : "UInt", pGraphics, "float", x, "float", y, "float", w, "float", h, "int", CombineMode)
}

Gdip_SetClipPath(pGraphics, Path, CombineMode=0)
{
	Ptr := A_PtrSize ? "UPtr" : "UInt"
	return DllCall("gdiplus\GdipSetClipPath", Ptr, pGraphics, Ptr, Path, "int", CombineMode)
}

Gdip_ResetClip(pGraphics)
{
   return DllCall("gdiplus\GdipResetClip", A_PtrSize ? "UPtr" : "UInt", pGraphics)
}

Gdip_GetClipRegion(pGraphics)
{
	Region := Gdip_CreateRegion()
	DllCall("gdiplus\GdipGetClip", A_PtrSize ? "UPtr" : "UInt", pGraphics, "UInt*", Region)
	return Region
}

Gdip_SetClipRegion(pGraphics, Region, CombineMode=0)
{
	Ptr := A_PtrSize ? "UPtr" : "UInt"

	return DllCall("gdiplus\GdipSetClipRegion", Ptr, pGraphics, Ptr, Region, "int", CombineMode)
}

Gdip_CreateRegion()
{
	DllCall("gdiplus\GdipCreateRegion", "UInt*", Region)
	return Region
}

Gdip_DeleteRegion(Region)
{
	return DllCall("gdiplus\GdipDeleteRegion", A_PtrSize ? "UPtr" : "UInt", Region)
}

;#####################################################################################
; BitmapLockBits
;#####################################################################################

Gdip_LockBits(pBitmap, x, y, w, h, ByRef Stride, ByRef Scan0, ByRef BitmapData, LockMode = 3, PixelFormat = 0x26200a)
{
	Ptr := A_PtrSize ? "UPtr" : "UInt"

	CreateRect(Rect, x, y, w, h)
	VarSetCapacity(BitmapData, 16+2*(A_PtrSize ? A_PtrSize : 4), 0)
	E := DllCall("Gdiplus\GdipBitmapLockBits", Ptr, pBitmap, Ptr, &Rect, "uint", LockMode, "int", PixelFormat, Ptr, &BitmapData)
	Stride := NumGet(BitmapData, 8, "Int")
	Scan0 := NumGet(BitmapData, 16, Ptr)
	return E
}

;#####################################################################################

Gdip_UnlockBits(pBitmap, ByRef BitmapData)
{
	Ptr := A_PtrSize ? "UPtr" : "UInt"

	return DllCall("Gdiplus\GdipBitmapUnlockBits", Ptr, pBitmap, Ptr, &BitmapData)
}

;#####################################################################################

Gdip_SetLockBitPixel(ARGB, Scan0, x, y, Stride)
{
	Numput(ARGB, Scan0+0, (x*4)+(y*Stride), "UInt")
}

;#####################################################################################

Gdip_GetLockBitPixel(Scan0, x, y, Stride)
{
	return NumGet(Scan0+0, (x*4)+(y*Stride), "UInt")
}

;#####################################################################################

Gdip_PixelateBitmap(pBitmap, ByRef pBitmapOut, BlockSize)
{
	static PixelateBitmap

	Ptr := A_PtrSize ? "UPtr" : "UInt"

	if (!PixelateBitmap)
	{
		if A_PtrSize != 8 ; x86 machine code
		MCode_PixelateBitmap =
		(LTrim Join
		558BEC83EC3C8B4514538B5D1C99F7FB56578BC88955EC894DD885C90F8E830200008B451099F7FB8365DC008365E000894DC88955F08945E833FF897DD4
		397DE80F8E160100008BCB0FAFCB894DCC33C08945F88945FC89451C8945143BD87E608B45088D50028BC82BCA8BF02BF2418945F48B45E02955F4894DC4
		8D0CB80FAFCB03CA895DD08BD1895DE40FB64416030145140FB60201451C8B45C40FB604100145FC8B45F40FB604020145F883C204FF4DE475D6034D18FF
		4DD075C98B4DCC8B451499F7F98945148B451C99F7F989451C8B45FC99F7F98945FC8B45F899F7F98945F885DB7E648B450C8D50028BC82BCA83C103894D
		C48BC82BCA41894DF48B4DD48945E48B45E02955E48D0C880FAFCB03CA895DD08BD18BF38A45148B7DC48804178A451C8B7DF488028A45FC8804178A45F8
		8B7DE488043A83C2044E75DA034D18FF4DD075CE8B4DCC8B7DD447897DD43B7DE80F8CF2FEFFFF837DF0000F842C01000033C08945F88945FC89451C8945
		148945E43BD87E65837DF0007E578B4DDC034DE48B75E80FAF4D180FAFF38B45088D500203CA8D0CB18BF08BF88945F48B45F02BF22BFA2955F48945CC0F
		B6440E030145140FB60101451C0FB6440F010145FC8B45F40FB604010145F883C104FF4DCC75D8FF45E4395DE47C9B8B4DF00FAFCB85C9740B8B451499F7
		F9894514EB048365140033F63BCE740B8B451C99F7F989451CEB0389751C3BCE740B8B45FC99F7F98945FCEB038975FC3BCE740B8B45F899F7F98945F8EB
		038975F88975E43BDE7E5A837DF0007E4C8B4DDC034DE48B75E80FAF4D180FAFF38B450C8D500203CA8D0CB18BF08BF82BF22BFA2BC28B55F08955CC8A55
		1488540E038A551C88118A55FC88540F018A55F888140183C104FF4DCC75DFFF45E4395DE47CA68B45180145E0015DDCFF4DC80F8594FDFFFF8B451099F7
		FB8955F08945E885C00F8E450100008B45EC0FAFC38365DC008945D48B45E88945CC33C08945F88945FC89451C8945148945103945EC7E6085DB7E518B4D
		D88B45080FAFCB034D108D50020FAF4D18034DDC8BF08BF88945F403CA2BF22BFA2955F4895DC80FB6440E030145140FB60101451C0FB6440F010145FC8B
		45F40FB604080145F883C104FF4DC875D8FF45108B45103B45EC7CA08B4DD485C9740B8B451499F7F9894514EB048365140033F63BCE740B8B451C99F7F9
		89451CEB0389751C3BCE740B8B45FC99F7F98945FCEB038975FC3BCE740B8B45F899F7F98945F8EB038975F88975103975EC7E5585DB7E468B4DD88B450C
		0FAFCB034D108D50020FAF4D18034DDC8BF08BF803CA2BF22BFA2BC2895DC88A551488540E038A551C88118A55FC88540F018A55F888140183C104FF4DC8
		75DFFF45108B45103B45EC7CAB8BC3C1E0020145DCFF4DCC0F85CEFEFFFF8B4DEC33C08945F88945FC89451C8945148945103BC87E6C3945F07E5C8B4DD8
		8B75E80FAFCB034D100FAFF30FAF4D188B45088D500203CA8D0CB18BF08BF88945F48B45F02BF22BFA2955F48945C80FB6440E030145140FB60101451C0F
		B6440F010145FC8B45F40FB604010145F883C104FF4DC875D833C0FF45108B4DEC394D107C940FAF4DF03BC874068B451499F7F933F68945143BCE740B8B
		451C99F7F989451CEB0389751C3BCE740B8B45FC99F7F98945FCEB038975FC3BCE740B8B45F899F7F98945F8EB038975F88975083975EC7E63EB0233F639
		75F07E4F8B4DD88B75E80FAFCB034D080FAFF30FAF4D188B450C8D500203CA8D0CB18BF08BF82BF22BFA2BC28B55F08955108A551488540E038A551C8811
		8A55FC88540F018A55F888140883C104FF4D1075DFFF45088B45083B45EC7C9F5F5E33C05BC9C21800
		)
		else ; x64 machine code
		MCode_PixelateBitmap =
		(LTrim Join
		4489442418488954241048894C24085355565741544155415641574883EC28418BC1448B8C24980000004C8BDA99488BD941F7F9448BD0448BFA8954240C
		448994248800000085C00F8E9D020000418BC04533E4458BF299448924244C8954241041F7F933C9898C24980000008BEA89542404448BE889442408EB05
		4C8B5C24784585ED0F8E1A010000458BF1418BFD48897C2418450FAFF14533D233F633ED4533E44533ED4585C97E5B4C63BC2490000000418D040A410FAF
		C148984C8D441802498BD9498BD04D8BD90FB642010FB64AFF4403E80FB60203E90FB64AFE4883C2044403E003F149FFCB75DE4D03C748FFCB75D0488B7C
		24188B8C24980000004C8B5C2478418BC59941F7FE448BE8418BC49941F7FE448BE08BC59941F7FE8BE88BC69941F7FE8BF04585C97E4048639C24900000
		004103CA4D8BC1410FAFC94863C94A8D541902488BCA498BC144886901448821408869FF408871FE4883C10448FFC875E84803D349FFC875DA8B8C249800
		0000488B5C24704C8B5C24784183C20448FFCF48897C24180F850AFFFFFF8B6C2404448B2424448B6C24084C8B74241085ED0F840A01000033FF33DB4533
		DB4533D24533C04585C97E53488B74247085ED7E42438D0C04418BC50FAF8C2490000000410FAFC18D04814863C8488D5431028BCD0FB642014403D00FB6
		024883C2044403D80FB642FB03D80FB642FA03F848FFC975DE41FFC0453BC17CB28BCD410FAFC985C9740A418BC299F7F98BF0EB0233F685C9740B418BC3
		99F7F9448BD8EB034533DB85C9740A8BC399F7F9448BD0EB034533D285C9740A8BC799F7F9448BC0EB034533C033D24585C97E4D4C8B74247885ED7E3841
		8D0C14418BC50FAF8C2490000000410FAFC18D04814863C84A8D4431028BCD40887001448818448850FF448840FE4883C00448FFC975E8FFC2413BD17CBD
		4C8B7424108B8C2498000000038C2490000000488B5C24704503E149FFCE44892424898C24980000004C897424100F859EFDFFFF448B7C240C448B842480
		000000418BC09941F7F98BE8448BEA89942498000000896C240C85C00F8E3B010000448BAC2488000000418BCF448BF5410FAFC9898C248000000033FF33
		ED33F64533DB4533D24533C04585FF7E524585C97E40418BC5410FAFC14103C00FAF84249000000003C74898488D541802498BD90FB642014403D00FB602
		4883C2044403D80FB642FB03F00FB642FA03E848FFCB75DE488B5C247041FFC0453BC77CAE85C9740B418BC299F7F9448BE0EB034533E485C9740A418BC3
		99F7F98BD8EB0233DB85C9740A8BC699F7F9448BD8EB034533DB85C9740A8BC599F7F9448BD0EB034533D24533C04585FF7E4E488B4C24784585C97E3541
		8BC5410FAFC14103C00FAF84249000000003C74898488D540802498BC144886201881A44885AFF448852FE4883C20448FFC875E941FFC0453BC77CBE8B8C
		2480000000488B5C2470418BC1C1E00203F849FFCE0F85ECFEFFFF448BAC24980000008B6C240C448BA4248800000033FF33DB4533DB4533D24533C04585
		FF7E5A488B7424704585ED7E48418BCC8BC5410FAFC94103C80FAF8C2490000000410FAFC18D04814863C8488D543102418BCD0FB642014403D00FB60248
		83C2044403D80FB642FB03D80FB642FA03F848FFC975DE41FFC0453BC77CAB418BCF410FAFCD85C9740A418BC299F7F98BF0EB0233F685C9740B418BC399
		F7F9448BD8EB034533DB85C9740A8BC399F7F9448BD0EB034533D285C9740A8BC799F7F9448BC0EB034533C033D24585FF7E4E4585ED7E42418BCC8BC541
		0FAFC903CA0FAF8C2490000000410FAFC18D04814863C8488B442478488D440102418BCD40887001448818448850FF448840FE4883C00448FFC975E8FFC2
		413BD77CB233C04883C428415F415E415D415C5F5E5D5BC3
		)

		VarSetCapacity(PixelateBitmap, StrLen(MCode_PixelateBitmap)//2)
		Loop % StrLen(MCode_PixelateBitmap)//2		;%
			NumPut("0x" SubStr(MCode_PixelateBitmap, (2*A_Index)-1, 2), PixelateBitmap, A_Index-1, "UChar")
		DllCall("VirtualProtect", Ptr, &PixelateBitmap, Ptr, VarSetCapacity(PixelateBitmap), "uint", 0x40, A_PtrSize ? "UPtr*" : "UInt*", 0)
	}

	Gdip_GetImageDimensions(pBitmap, Width, Height)

	if (Width != Gdip_GetImageWidth(pBitmapOut) || Height != Gdip_GetImageHeight(pBitmapOut))
		return -1
	if (BlockSize > Width || BlockSize > Height)
		return -2

	E1 := Gdip_LockBits(pBitmap, 0, 0, Width, Height, Stride1, Scan01, BitmapData1)
	E2 := Gdip_LockBits(pBitmapOut, 0, 0, Width, Height, Stride2, Scan02, BitmapData2)
	if (E1 || E2)
		return -3

	E := DllCall(&PixelateBitmap, Ptr, Scan01, Ptr, Scan02, "int", Width, "int", Height, "int", Stride1, "int", BlockSize)

	Gdip_UnlockBits(pBitmap, BitmapData1), Gdip_UnlockBits(pBitmapOut, BitmapData2)
	return 0
}

;#####################################################################################

Gdip_ToARGB(A, R, G, B)
{
	return (A << 24) | (R << 16) | (G << 8) | B
}

;#####################################################################################

Gdip_FromARGB(ARGB, ByRef A, ByRef R, ByRef G, ByRef B)
{
	A := (0xff000000 & ARGB) >> 24
	R := (0x00ff0000 & ARGB) >> 16
	G := (0x0000ff00 & ARGB) >> 8
	B := 0x000000ff & ARGB
}

;#####################################################################################

Gdip_AFromARGB(ARGB)
{
	return (0xff000000 & ARGB) >> 24
}

;#####################################################################################

Gdip_RFromARGB(ARGB)
{
	return (0x00ff0000 & ARGB) >> 16
}

;#####################################################################################

Gdip_GFromARGB(ARGB)
{
	return (0x0000ff00 & ARGB) >> 8
}

;#####################################################################################

Gdip_BFromARGB(ARGB)
{
	return 0x000000ff & ARGB
}

;#####################################################################################

StrGetB(Address, Length=-1, Encoding=0)
{
	; Flexible parameter handling:
	if Length is not integer
	Encoding := Length,  Length := -1

	; Check for obvious errors.
	if (Address+0 < 1024)
		return

	; Ensure 'Encoding' contains a numeric identifier.
	if Encoding = UTF-16
		Encoding = 1200
	else if Encoding = UTF-8
		Encoding = 65001
	else if SubStr(Encoding,1,2)="CP"
		Encoding := SubStr(Encoding,3)

	if !Encoding ; "" or 0
	{
		; No conversion necessary, but we might not want the whole string.
		if (Length == -1)
			Length := DllCall("lstrlen", "uint", Address)
		VarSetCapacity(String, Length)
		DllCall("lstrcpyn", "str", String, "uint", Address, "int", Length + 1)
	}
	else if Encoding = 1200 ; UTF-16
	{
		char_count := DllCall("WideCharToMultiByte", "uint", 0, "uint", 0x400, "uint", Address, "int", Length, "uint", 0, "uint", 0, "uint", 0, "uint", 0)
		VarSetCapacity(String, char_count)
		DllCall("WideCharToMultiByte", "uint", 0, "uint", 0x400, "uint", Address, "int", Length, "str", String, "int", char_count, "uint", 0, "uint", 0)
	}
	else if Encoding is integer
	{
		; Convert from target encoding to UTF-16 then to the active code page.
		char_count := DllCall("MultiByteToWideChar", "uint", Encoding, "uint", 0, "uint", Address, "int", Length, "uint", 0, "int", 0)
		VarSetCapacity(String, char_count * 2)
		char_count := DllCall("MultiByteToWideChar", "uint", Encoding, "uint", 0, "uint", Address, "int", Length, "uint", &String, "int", char_count * 2)
		String := StrGetB(&String, char_count, 1200)
	}

	return String
}

В архиве исходник и скомпилированный вариант.

Post's attachments

COMTerminal.zip 982.08 kb, 3 downloads since 2020-07-06 

You don't have the permssions to download the attachments of this post.
Win 7 x64
AHK v1.1.24.00
                       Справка тебе в помощь.

2 (изменено: Alectric, 2020-10-09 16:26:22)

Re: AHK: COM Port terminal

Alectric пишет:

Недостатки:
- нет возможности распараллелить обработку данных и обработку порта
из-за этого нет возможности корректно обрабатывать большие пакеты данных и данные идущие с большой частотой;

Разделил программу на два потока: в первом крутится интерфейс и обрабатываются данные, во втором происходит работа с COM портом.
Теперь данные практически не дробятся на несколько пакетов и обрабатываются как единый блок.

Добавлена настройка "Пропуск пакетов" - правый клик на информационную строку "В обработке Х". На случай если нужны данные в текущем времени, чтобы не ждать обработку всех накопившихся пакетов, если они поступают быстрее чем обрабатываются.

Добавлена настройка "ReadIntervalTimeout" (Максимальное время, которое может пройти до поступления следующего байта в линию связи, в миллисекундах.) Для более точного разделения данных на пакеты. Настройка определяется автоматически в зависимости от параметров порта, но есть возможность ввести вручную.

Внес незначительные, мелкие правки.

Version:="2.6"
#SingleInstance,off

#NoEnv
;ListLines,Off
#KeyHistory,0
AutoTrim,Off
CoordMode,Mouse,Screen
CoordMode,ToolTip
CoordMode,Pixel
CoordMode,Caret,Screen
DetectHiddenWindows,On
SetFormat,float,0.3
Menu,Tray,UseErrorLevel
Menu,Tray,NoIcon
SendMode,Input
SetBatchLines,-1
SetControlDelay,-1
SetKeyDelay,-1
SetTitleMatchMode,2
SetWinDelay,-1
SetWorkingDir,%A_ScriptDir%
StringCaseSense,Off
OnExit,EXIT
ComObjError(false)
Gdip_Initialised:=Gdip_Startup()
;Meta:="\.*?+[{|()^$"
global Ptr := A_PtrSize ? "UPtr" : "UInt"
global COMFail

OnMessage(0x8080,"GetPointer")

_Diskonnect:=0		; UChar 1	+0
_ApplyCOMSettings:=1	; UChar 1	+1
_COMBusy:=2		; UChar 1	+2
_COMFail:=3		; UChar 1	+3
_DataAvailable:=4	; UChar 1	+4
_SendDataNow:=5		; UChar 1	+5
_RS232_Baud:=6		; Int   4	+6
_RS232_Parity:=10	; Int   4	+10
_RS232_Data:=14		; Int   4	+14
_RS232_Stop:=26		; Int   4	+26
_COMPort:=30		; Str   12	+30
_SendSize:=42		; Int   4	+42
_DataPos:=46		; Int   4	+46
_PackagesRecieved:=50	; Int   4	+50
_ReadIntervalTimeout:=54	; Int   4	+54

SendDataOffset:=100
RecieveDataOffset:=10000
DataSize:=1048576
DataPos:=0
ReadPos:=0

2ndThread=%1%
hBufinfo=%2%
hMainWin=%3%
msPointer=%4%

OnMessage(WM_MOVING:=0x0216, "WM_MOVING")
;OnMessage(WM_MOUSEMOVE:=0x200, "WM_MOUSEMOVE")
;IDC_SIZENS := DllCall("User32.dll\LoadCursor", Ptr, NULL, "Int", 32645, "UPtr")
IDC_SIZEWE := DllCall("User32.dll\LoadCursor", Ptr, NULL, "Int", 32644, Ptr)
IDC_SIZEALL:= DllCall("User32.dll\LoadCursor", Ptr, NULL, "Int", 32646, Ptr)
hIDC_ARROW := DllCall("User32.dll\LoadCursor", Ptr, NULL, "Int", 32512, Ptr)
IDC_ARROW := DllCall("CopyImage",Ptr,hIDC_ARROW,"UInt",0x2,"Int",0,"Int",0,"UInt",0)

RS232_Bytes_Reqest=4096
Rownums=100000
if (a_osversion~="10")
  add_size=65
else
  add_size=55
E_Text_h=560
Packages_Recieved:=0

RS232_Baud:=9600
RS232_Parity:="N"
RS232_Data:=8
RS232_Stop:=1

Gdip_Points_Num:=2000
Gdip_W:=358
Gdip_H:=358
Gdip_M:=Gdip_H//2
Gdip_ZoomX:=1
Gdip_ZoomY:=1
Gdip_X_Offset:=0 ;Gdip_Y_Offset:=Gdip_M
ColourPen1:=0x0000ff
ColourPen2:=0xff0000
ColourPen3:=0x00ff00
ColourPen4:=0x00ffff
ColourPen5:=0xff00ff
ColourPen6:=0xcccc00
ColourPen7:=0x808080
ColourPen8:=0x0000c0
ColourPen9:=0xc00000
ColourPen10:=0x00c000
ColourPen11:=0x0000ff
ColourPen12:=0xff0000
Monitor_value:={}
MaxNumOfGraf:=12
loop,% MaxNumOfGraf
  Monitor_value%a_index%:={}
Monitor_value_T:={}
GrafSmooth:=1
AutoMinMax:=1
AutoEnding:=1

ICON_CRC_ERROR:=132
ICON_EMPTY:=0
ICON_ERROR:=78
ICON_INFO:=222
ICON_REGISTER:=13 ;268
ICON_START:=138
ICON_V:=145
ICON_X:=272

RTUFunc1:="Read Coil Status (Чтение DO) 01"
RTUFunc2:="Read Input Status (Чтение DI) 02"
RTUFunc3:="Read Holding Reg. (Чтение AO) 03"
RTUFunc4:="Read Input Register (Чтение AI) 04"
RTUFunc5:="Force Single Coil (Запись DO) 05"
RTUFunc6:="Preset Single Reg. (Запись AO) 06"
RTUFunc15:="Force Mult. Coils (Запись неск. DO) 0f"
RTUFunc16:="Force Mult. Reg. (Запись неск. AO) 10"
RTU_Command_List:= RTUFunc1 "|" RTUFunc2 "|" RTUFunc3 "|" RTUFunc4 "|" RTUFunc5 "|" RTUFunc6 "|" RTUFunc15 "|" RTUFunc16 ;

RTUF1:="DO"
RTUF2:="DI"
RTUF3:="AO"
RTUF4:="AI"
RTUF5:="1 DO"
RTUF6:="1 AO"
RTUF15:="# DO"
RTUF16:="# AO"

RTUErr1:="У слейва нет такой команды."
RTUErr2:="Регистр недоступен."
RTUErr3:="Недопустимая величина значения."
RTUErr4:="Ошибка во время выполнения действия."
RTUErr5:="Обрабатываю запрос, ждите..."
RTUErr6:="Обрабатываю команду, ждите..."
RTUErr7:="Не могу выполнить программную функцию. Запросите диагностические данные."
RTUErr8:="Ошибка паритета. Повторите запрос или обратитесь в сервис."
RTUErr10:="Шлюз не правильно настроен или перегружен."
RTUErr11:="Слейв устройства нет в сети или от него нет ответа."

if !2ndThread ; Main
{
  menu,ListRightClick,add,Очистить,update
  menu,ListRightClick,add,Фильтровать данные,DataFilter
  menu,ListRightClick,add
  menu,ListRightClick,add,Копировать,ListCopySelected
  menu,ListRightClick,add,Отрисовать график,GrafDrawFromSelected
  menu,ListRightClick,disable,Отрисовать график ;

  menu,ListRightClick2,add,Пропуск пакетов,SkeepPack

  GuiCreate:
  Gui,1:+hwndhMainWin ; -DPIScale ; +alwaysontop
  Gui,1:+MinimizeBox +maximizebox +Resize ; +E0x80000
  Gui,1:Margin,10,10
  Gui,1:font,s8,Tahoma
  Gui,1:Add,StatusBar,+hwndhStatusBar,Готов

  ; Настройки COM-порта ;
  Gui,1:Add,GroupBox,xm ym w210 h175,Настройки COM-порта ;
  Gui,1:Add,Radio,Checked vChoiceCOM xp+10 yp+20 w130,USB-COM устройство ;
  Gui,1:Add,Radio,+hwndhCOMNo xp y+10 w106,COM-порт №:
  Gui,1:Add,Radio,+hwndhCOMName xp y+3 w106,Искать по имени:
  Gui,1:Add,Edit,xp+110 yp-20 w50 vManualCOM gChoseCOMNo Number Limit3,1
  Gui,1:Add,UpDown,Range1-30
  Gui,1:Add,Edit,xp y+3 w80 vManualCOMByName gChoseCOMName,CH340
  Gui,1:add,Button,xp-110 y+10 w90 gAddSettings,Дополнительно ;
  Gui,1:add,Button,x+10 yp w90 gAceptSettings,Применить ;
  Gui,1:add,text,xp-98 y+6 w187 h40 v_text_ gDiskonnectCOM

  ; Отправить данные в порт ;
  Gui,1:Add,GroupBox,xm y+15 w210 h484,Отправить данные в порт ;
  Gui,1:Add,Edit,xp+10 yp+22 w190 h300 vDataToSend gDataToSendCheck +hwndE_1,
  Gui,1:add,text,+hwndhE_RTU_Addres_1 xm+69 yp,Адрес DEC: ;Адрес DEC:
  Gui,1:add,text,+hwndhE_RTU_Addres_2 xp+35 y+8,HEX:
  Gui,1:add,Edit,+hwndhE_RTU_Addres_3 xm+130 yp-24 w70 gChangeAddres Number Limit3 vRTU_SlaveAdress,
  Gui,1:Add,UpDown,+hwndhE_RTU_Addres_4 Range0-255 Wrap,1
  Gui,1:add,Edit,+hwndhE_RTU_Addres_5 xp y+1 w70 gChangeAddres vRTU_SlaveAdressHEX,1

  Gui,1:add,text,+hwndhE_RTU_Command_1 xm+10 y+5,Команда: ;Команда:
  Gui,1:add,DropDownList,+hwndhE_RTU_Command_2 xm+10 y+5 w190 Choose3 vRTU_Command gSubmit AltSubmit,% RTU_Command_List
  Gui,1:add,text,+hwndhE_RTU_StartReg_1 xm+19 y+10,Начальный рег. DEC: ;№ регистра DEC:
  Gui,1:add,text,+hwndhE_RTU_StartReg_2 xp+85 y+8,HEX:
  Gui,1:add,Edit,+hwndhE_RTU_StartReg_3 xm+130 yp-24 w70 gChangeStartReg Number vRTU_StartReg,
  Gui,1:Add,UpDown,+hwndhE_RTU_StartReg_4 Range0-65535 Wrap 0x80,0
  Gui,1:add,Edit,+hwndhE_RTU_StartReg_5 xp y+1 w70 gChangeStartReg vRTU_StartRegHEX,0
  Gui,1:add,text,+hwndhE_RTU_RegNum_1 xm+10 y+10,Кол-во регистров DEC: ;Кол-во регистров DEC:
  Gui,1:add,text,+hwndhE_RTU_RegNum_2 xp+94 y+8,HEX:
  Gui,1:add,Edit,+hwndhE_RTU_RegNum_3 xm+130 yp-24 w70 gChangeRegNum Number vRTU_RegNum,
  Gui,1:Add,UpDown,+hwndhE_RTU_RegNum_4 Range1-100 Wrap 0x80,1
  Gui,1:add,Edit,+hwndhE_RTU_RegNum_5 xp y+1 w70 gChangeRegNum vRTU_RegNumHEX,1
  Gui,1:add,text,+hwndhE_RTU_Val_1 xm+10 y+5,Значение: ;Значение:
  Gui,1:add,checkbox,+hwndhE_RTU_Val_2 xm+10 y+85 vBeliveMe,Данным верить ;Данным верить:

  ; GuiControl,Move,% E_1,y470 h75
  ;loop,5
  ;  guicontrol,enable,% hE_RTU_RegNum_%a_index%
  ;loop,2
  ;  GuiControl,disable,% hE_RTU_Val_%a_index%
  ;GuiControl,disable,% E_1
   loop,2
     GuiControl,hide,% hE_RTU_Command_%a_index%
   loop,5
     GuiControl,hide,% hE_RTU_StartReg_%a_index%
   loop,5
     GuiControl,hide,% hE_RTU_Addres_%a_index%
   loop,5
     GuiControl,hide,% hE_RTU_RegNum_%a_index%
   loop,3
     GuiControl,hide,% hE_RTU_Val_%a_index%

  Gui,1:add,Radio,xm+10 ym+510 gConvertToSYM vChoiceFormat,SYM
  Gui,1:add,Radio,x+5 yp gConvertToDEC,DEC
  Gui,1:add,Radio,x+5 yp gConvertToHEX Checked,HEX
  Gui,1:add,Radio,x+5 yp gConvertToBIN,BIN
  PreviousConversion:="hex"
  Gui,1:Add,GroupBox,+hwndhEDEC1 xm+10 y+2 w190 h55,DEC ;
  Gui,1:add,Radio,+hwndhEDEC2 xm+20 yp+15 gChooseDEC vChoiceDEC,Unsigned
  Gui,1:add,Radio,+hwndhEDEC3 x+5 yp gChooseDEC,Signed
  Gui,1:add,Radio,+hwndhEDEC4 x+5 yp gChooseDEC,Float
  Gui,1:add,Radio,+hwndhEDEC5 xm+20 y+5 gChooseDECSize Group vChoiceDECSize,1
  Gui,1:add,Radio,+hwndhEDEC6 x+5 yp gChooseDECSize,2
  Gui,1:add,Radio,+hwndhEDEC7 x+5 yp gChooseDECSize,4
  Gui,1:add,Radio,+hwndhEDEC8 x+5 yp gChooseDECSize,8   Bytes
   loop,8
     GuiControl,hide,% hEDEC%a_index%

  Gui,1:add,text,xm+10 ym+580,Добавить контрольную сумму: ; CheckSumm ;
  Gui,1:add,DropDownList,xp y+5 w190 +hwndhCS vAddCS gSubmit Choose1 AltSubmit,Нет|CRC8|CRC16|Modbus RTU|Modbus RTU шаблон ;
  Gui,1:add,Button,xp y+10 w120 gButtonSendData,Отправить ;
  Gui,1:add,Button,x+ yp w69 gSendDataRepitedly +hwndhSendDataRepitedly,Циклично ;

  ; Буфер приёма COM-порта ;
  Gui,1:Add,GroupBox,xm+220 ym w720 h662 +hwndhGroupBufer,Буфер приёма COM-порта ;
  Gui,1:add,text,+hwndhBufinfo xp+10 yp+20 w110 vBufinfo gClearQueue,В обработке 0. ;

  Gui,1:add,text,+hwndhhide1 xp yp  w128,Ожидаемое число байт: ;
  Gui,1:add,edit,+hwndhhide2 x+5 yp-3 w70 Number Limit3 vRS232_Bytes_Reqest gSubmit2,% RS232_Bytes_Reqest
  Gui,1:add,UpDown,+hwndhhide3 Range1-128 gSubmit,% RS232_Bytes_Reqest
  Gui,1:Add,checkbox,+hwndhAutoByRe x+5 yp+3 checked vAuto_Bytes_Reqest gSubmit2,Автоматически ;
  GuiControl,hide,% hAutoByRe
  GuiControl,hide,% hhide1
  GuiControl,hide,% hhide2
  GuiControl,hide,% hhide3

  Gui,1:add,checkbox,xp-209 y+9 vFollowSelect gSubmit,Сохранять выделение таблицы (мониторить значение) ;
  Gui,1:add,ListView,xp y+7 w320 h%E_Text_h% +LV0x10000 v_List_ gListGetSelected AltSubmit grid Count%Rownums% NoSortHdr ReadOnly +hwndE_LV,#|№ байта|№ байта или описание|BIN|SYM|HEX|DEC|WORD|INT|DWORD|DINT|Float|Double
  LV_SetImageList(ImageListID:=IL_Create(300))
  loop,300
    IL_Add(ImageListID,"shell32.dll",a_index)

  ; Разделитель 1
  Gui,1:Add,Progress,x+0 yp w20 h%E_Text_h% vProgress cNavy backgroundSilver Vertical +hwndhProgress

  Gui,1:add,checkbox,x+0 ym+20 +hwndE_111 vEN_Kurvendrucker gKurvendrucker,Включить графопостроитель ;
  Gui,1:add,checkbox,xp y+10 vAddCRLF gSubmit Checked,Добавить перевод строки после каждого сообщения ;
  Gui,1:font,s12,Courier New
  tmp1:=E_Text_h-36
  Gui,1:add,edit,xp y+5 w360 h%tmp1% HScroll VScroll ReadOnly Multi v_InText_ hwndE_Text
  ; Разделитель 2
  Gui,1:Add,Progress,xp y+0 h20 w360 vProgress2 cSilver backgroundNavy +hwndhProgress2,100
  Gui,1:add,edit,% "xp+2 yp+22 w" Gdip_W-4 " h" 12 " +hwndhGrafPos"
  ;GuiControl,Hide,% hGrafPos

  Gui,1:font,s8,Tahoma
  Gui,1:add,button,xp-340 y+10 w200 gToTheEnd +hwndhTtEButton,Продолжить прокрутку ;
  Gui,1:add,button,% "x+10 yp w490 gUpdate +hwndhClButton",Очистить ;

  Gui,1:add,checkbox,xm+230 yp gSubmit2 vGrafPause +hwndhECB0,Остановить прокрутку (F1)

  Gui,1:add,text,xm+218 y+1 w1 +hwndhE0, ;
  Gui,1:font,s10 w1000,Tahoma
  loop,% MaxNumOfGraf
    Gui,1:add,checkbox,% "x+5 yp w50 gSubmit2 vEnableGraf" a_index " checked c" ColourPen%a_index% " +hwndhECB" a_index,% "[" a_index "]"

  loop,% MaxNumOfGraf+1
  {
    i:=a_index-1
    GuiControl,hide,% hECB%i%
  }
  GuiControl,hide,% hE0
  Gui,1:font,s8,Tahoma
  Gui,1:Show,,COM Terminal v%Version%
  Gui,1:+MinSize

  E%E_LV%=
  ( LTrim
  В столбце #: V - Контрольная сумма совпала`; X - не совпала
  )

  E%E_LV2%=
  ( LTrim
  Варианты представления выделенных байтов.
  )

  E%hProgress%=
  ( LTrim
  Шкала заполнения таблицы. При переполнении таблица и текстовое поле очистятся.
  )

  E%hProgress2%=
  ( LTrim
  Позиция в графике. Правый клик - сменить позицию.
  )

  E%E_1%=
  ( LTrim

  )

  E%E_111%=
  ( LTrim
  Kurvendrucker по немецки.
  )

  E%hECB12%=
  ( LTrim
  Правый клик - отключить лишние графики. Правый клик на отключенный - включить все.
  )

  E%hBufinfo%=
  ( LTrim
  Клик чтобы очистить очередь. Правый клик - меню.
  )
  E%hECB1%:=E%hECB2%:=E%hECB3%:=E%hECB4%:=E%hECB5%:=E%hECB6%:=E%hECB7%:=E%hECB8%:=E%hECB9%:=E%hECB10%:=E%hECB11%:=E%hECB12%
  VarSetCapacity(E_Text_start, 4), VarSetCapacity(E_Text_end, 4)

  Gdip_Initialise:
  if Gdip_Initialised
  {
    E%E_LV2%.=" Нажмите на нужный вариант для отрисовки графика."
    gui,3:+owner1
    Gui,3:+hwndhGraf -Caption +E0x80000 +LastFound +ToolWindow ;+AlwaysOnTop WS_EX_LAYERED := 0x80000
    Gui,3:Show,x0 y0 w1 h1
  ;  SetParent(hGraf,hMainWin,,0,0)

    Gdip_FontFamilyCreate(Gdip_Font)

    Gdip_hbm2 := CreateDIBSection(A_ScreenWidth,A_ScreenHeight)
    Main_Holst := CreateCompatibleDC()
    Gdip_obm2 := SelectObject(Main_Holst,Gdip_hbm2)
    Main_Holst_G := Gdip_GraphicsFromHDC(Main_Holst)
    Gdip_SetSmoothingMode(Main_Holst_G,4)

    Gdip_Font:="Lucida Console"
    Gdip_Font_Size:="s15"
    Gdip_Font_W:=10
    Gdip_Font_H:=15
    Gdip_Font_Selected_Colour:="ff000000"

    Gdip_WhiteBrush:= Gdip_BrushCreateSolid(0xffffffff) ; белая кисть для заливки
    Gdip_WhiteBrush2:= Gdip_BrushCreateSolid(0xd0ffffff) ; белая кисть для заливки
    Gdip_BlackBrush:= Gdip_BrushCreateSolid(0xff000000) ; черная кисть для заливки
    Gdip_WhitePen := Gdip_CreatePen(0xffffffff,1) ; карандаш для стирания
    Gdip_GrayPen := Gdip_CreatePen(0xff808080,1) ; карандаш для оси
    Gdip_GrayPen2 := Gdip_CreatePen(0xffe0e0e0,1) ; карандаш для оси
    Gdip_GrayPen3 := Gdip_CreatePen(0x11000000,1) ; карандаш для оси
    Gdip_BluePen := Gdip_CreatePen(0xff0000ff,1) ; карандашы для графика
    Gdip_BlackPen := Gdip_CreatePen(0xff000000,2) ; карандашы для графика
    Line_w:=1

    loop,% MaxNumOfGraf
    {
      Gdip_Pen%a_index%:=Gdip_CreatePen(0xff000000 | ColourPen%a_index%,Line_w)
      Gdip_Brush%a_index%:= Gdip_BrushCreateSolid(0x40000000 | ColourPen%a_index%) ; белая кисть для заливки
    }

    Gdip_FillRectangle(Main_Holst_G,Gdip_WhiteBrush,0,0,Gdip_Points_Num,Gdip_H)
  }

  Gui,1:ListView,% E_LV
  LV_ModifyCol(1,65) ;#
  LV_ModifyCol(2,0) ;№ байта
  LV_ModifyCol(3,45) ;№ байта или описание
  LV_ModifyCol(4,60) ;BIN
  LV_ModifyCol(5,20) ;SYM
  LV_ModifyCol(6,37) ;HEX
  LV_ModifyCol(7,32) ;DEC
  LV_ModifyCol(8,50) ;WORD
  LV_ModifyCol(9,50) ;INT
  LV_ModifyCol(10,20) ;DWORD
  LV_ModifyCol(11,20) ;DINT
  LV_ModifyCol(12,80) ;Float
  LV_ModifyCol(13,40) ;Double

  Column_HEX:=6
  Column_DEC:=7
  Column_WORD:=8
  Column_INT:=9
  Column_DWORD:=10
  Column_DINT:=11
  Column_Float:=12
  Column_Double:=13

  Gui,2:-sysmenu +owner1
  Gui,2:+hwndhAddWin ; -DPIScale ; +alwaysontop
  ;Gui,2:Color,dfdfdf
  Gui,2:Margin,10,10
  Gui,2:font,s8
  Gui,2:add,text,xm ym w120,Скорость передачи: ;
  Gui,2:Add,DropDownList,x+3 yp-3 w195 Choose14 vRS232_Baud_Choice gCalcTimeout,50|75|110|150|300|600|1200|1800|2000|2400|3600|4800|7200|9600|14400|19200|28800|38400|57600|115200|250000|300000|375000|500000|750000|1500000
  Gui,2:add,text,xm y+10 w120,Контрольные биты: ;
  Gui,2:Add,DropDownList,x+3 yp-3 w195 Choose1 vRS232_Parity_Choice gCalcTimeout,N (None) - без проверки|E (EVEN) - проверка на четность|O (Odd) - проверка на нечетность|M (MARk) - бит всегда 1|S (SPACE) - бит всегда 0
  Gui,2:add,text,xm y+10 w120,Число бит на символ: ;
  Gui,2:Add,DropDownList,x+3 yp-3 w195 Choose4 vRS232_Data_Choice gCalcTimeout,5|6|7|8
  Gui,2:add,text,xm y+10 w120,Число стоповых бит: ;
  Gui,2:Add,DropDownList,x+3 yp-3 w195 Choose1 vRS232_Stop_Choice gCalcTimeout,1|2

  Timeout:=((RS232_Data+4)/RS232_Baud)*1000
  ReadIntervalTimeout:=Ceil(Timeout)+1
  Gui,2:add,text,xm y+10 w120,ReadIntervalTimeout: ;
  Gui,2:Add,Edit,+hwndhRITimeout x+3 yp-3 w50 vReadIntervalTimeout,% ReadIntervalTimeout
  Gui,2:add,text,x+5 yp+3 w20 ,ms
  Gui,2:add,text,+hwndhTimeout x+10 yp w120,Расчетный = %Timeout% ms ;


  Gui,2:add,Button,xm y+10 w80 gDefaultSettings default,Поумолчанию ;
  Gui,2:add,Button,x+65 yp w80 gEndAddSettings default,Применить ;
  Gui,2:add,Button,x+10 yp w80 gCancelAddSettings,Отмена ;

  Gui,4:-sysmenu +owner1
  Gui,4:+hwndhFilterWin ; -DPIScale ; +alwaysontop
  Gui,4:Margin,10,10
  Gui,4:font,s8
  Gui,4:add,checkbox,xm ym gChangeFilter_Addres vFilter_Addres_EN,Фильтровать пакеты по Byte_№1/"Адресу" ;
  Gui,4:add,Edit, xm+255 yp-3 w70 gChangeFilter_Addres Number Limit3 vFilter_Addres,
  Gui,4:Add,UpDown, Range1-255 Wrap 0x80,1
  Gui,4:add,Edit, xp y+1 w70 gChangeFilter_Addres vFilter_AddresHEX,1
  Gui,4:add,checkbox,xm y+20 gChangeFilter_Command vFilter_Command_EN,Фильтровать пакеты по Byte_№2/"Команде" ;
  Gui,4:add,Edit,+hwndhFiltE1 xm+255 yp-3 w70 gChangeFilter_Command Number Limit3 vFilter_Command,
  Gui,4:Add,UpDown,+hwndhFiltE2 Range1-255 Wrap 0x80,1
  Gui,4:add,Edit,+hwndhFiltE3 xp y+1 w70 gChangeFilter_Command vFilter_CommandHEX,1
  Gui,4:Add,DropDownList,+hwndhFiltE4 xp-200 yp w195 gChangeFilter_Command vFilter_CommandRTU Choose3 AltSubmit,% RTU_Command_List

  Gui,4:add,checkbox,xm y+20 gChangeFilter_Byte vFilter_Byte_EN,Фильтровать по Byte_№3/"Кол-во байт" ;
  Gui,4:add,Edit, xm+255 yp-3 w70 gChangeFilter_Byte Number Limit3 vFilter_Byte,
  Gui,4:Add,UpDown, Range1-255 Wrap 0x80,1
  Gui,4:add,Edit, xp y+1 w70 gChangeFilter_Byte vFilter_ByteHEX,1

  Gui,4:Add,Radio,+hwndhFiltE5 xm y+20 Checked gChangeFilter_Addres vFilter_Message_EN,Принимать все ;
  Gui,4:Add,Radio,+hwndhFiltE6 xp y+5 gChangeFilter_Addres,Не принимать запросы мастера ;
  Gui,4:Add,Radio,+hwndhFiltE7 xp y+5 gChangeFilter_Addres,Не принимать ответы слейва ;
  Gui,4:Add,checkbox,+hwndhFiltE8 xp y+15 gChangeFilter_Addres vFilter_CRC_EN,Не принимать ошибки контрольной суммы ;
  Gui,4:add,Button,xm+255 y+20 w70 gDataFilterOk,Ok ;

  ;GuiControl,Show,% hFiltE4
  ;GuiControl,Show,% hFiltE5
  ;GuiControl,Show,% hFiltE6
  ;GuiControl,Show,% hFiltE7
  ;loop,3
  ;  GuiControl,disable,% hFiltE%a_index%
  GuiControl,hide,% hFiltE4
  GuiControl,hide,% hFiltE5
  GuiControl,hide,% hFiltE6
  GuiControl,hide,% hFiltE7
  loop,3
    GuiControl,enable,% hFiltE%a_index%
  GuiControl,hide,% hFiltE8

  gosub,HotkeysOn

  varsetcapacity(Data,DataSize,0xFF)
  msPointer:=&Data

  MainPID:=DllCall("GetCurrentProcessId")
  ; run slave
  Params:=" " MainPID " " hBufinfo " " hMainWin " " msPointer
  if a_iscompiled
    run,% a_scriptfullpath . Params,,UseErrorLevel,SlavePID
  else
    run,% """" a_ahkPath """ """ a_scriptfullpath """" Params,,UseErrorLevel,SlavePID
  settimer,WM_MOUSEMOVE,100
;  gosub,EndAddSettings
  F:=Func("CheckPIDExist").Bind(SlavePID)
  settimer,% F,2000

;  while(!slPointer)
;  {
;    sleep,100
;    if (a_index>20)
;    {
;      msgbox,Main`nОшибка получения указателя.
;      goto,EXIT
;    }
;  }
;  SLM:=new _ClassMemory("Ahk_PID" SlavePID)
;  SLM.write(slPointer+_COMBusy,0,"uChar")
  NumPut(0,Data,_COMBusy,"UChar")

  gosub,AceptSettings

MainLoop:
loop
{
  Loop_Time:=oldLoop_Time!="" ? a_tickcount-oldLoop_Time : 0
  oldLoop_Time:=a_tickcount
  sleep,10
  if (NumGet(Data,_Diskonnect,"UChar"))
    sleep,100
  else if (NumGet(Data,_COMBusy,"UChar"))
  {
    NumPut(0,Data,_COMBusy,"UChar")
    GuiControl,,_text_,% COMPort "`n`nнедоступен"
    UnCOMPort:=COMPort
    settimer,ResetUnCOMPort,2000
    ComPortList:=SearchCOM("",1)
    GuiControl,,% E_Text,% COMPort " недоступен.`nВыберите другой порт вручную.`n`nНайденные в системе порты:`n" ComPortList
  }
  else if (NumGet(Data,_COMFail,"UChar"))
  {
    NumPut(0,Data,_COMFail,"UChar")
    gosub,AceptSettings
  }
  else
  {
;    ; отправляем данные в буфер порта
    if SendDataRepitedly
      gosub,SendData

    if (!doonce and !NumGet(Data,_Diskonnect,"UChar"))
    {
      dots:=""
      doonce:=1
      GuiControl,,_text_,% "Подключен " COMPort ".`n`n`t`tОтключиться"
      GuiControl,,% E_Text,% "Подключен " COMPort "`r`n"
    }

    InWork:=NumGet(Data,_PackagesRecieved,"Int")-ReadPack
    if (InWork<0)
    {
      ReadPack:=NumGet(Data,_PackagesRecieved,"Int")
      InWork:=0
    }
    if (InWork>=10 and !swapcolor)
    {
      swapcolor:=!swapcolor
      GuiControl,+cRed,Bufinfo
    }
    else if (InWork<10 and swapcolor)
    {
      swapcolor:=!swapcolor
      GuiControl,+cblack,Bufinfo
    }
    GuiControl,,Bufinfo,% "В обработке " InWork " пакетов."
    if NumGet(Data,_DataAvailable,"UChar")
    {
      DataFromSlaveSize:=NumGet(Data,RecieveDataOffset+ReadPos,"Int")
;      tooltip,% DataFromSlaveSize "`n" ReadPos
      if (DataFromSlaveSize<0 or DataFromSlaveSize>4096)
      {
        NumPut(0,Data,_DataAvailable,"UChar")
        if (DataFromSlaveSize=-2)
          ReadPos:=ReadPack:=0
        else if (DataFromSlaveSize>4096)
          gosub,ClearQueue
        continue
      }
      if (DataFromSlaveSize>0)
      {
        Bufer:={}
        NumPut(0xFFFFFFFF,Data,RecieveDataOffset+ReadPos,"UInt")
        loop,% DataFromSlaveSize
        {
          addr:=RecieveDataOffset+ReadPos+3+a_index
          Bufer[a_index]:=NumGet(Data,addr,"UChar")
          NumPut(0xFF,Data,addr,"UChar")
        }
        ReadPos+=DataFromSlaveSize+4
        ReadPack++
        Packages_Recieved++
        if (SkeepPack and InWork>3)
          continue
        RS232_Bytes_Received:=DataFromSlaveSize
        Received_Addres:=Bufer[1]
        Received_Command:=Bufer[2]
        Received_Byte:=Bufer[3]
        ; определить корректность контрольной суммы
        CheckSummIsCorrect:=0
        if (AddCS>=4 and RS232_Bytes_Received>2)
        {
          tmp1:=(Bufer[RS232_Bytes_Received]<<8)|Bufer[RS232_Bytes_Received-1]
          tmp2:=CRC_RTU(Bufer,RS232_Bytes_Received-2)
          CheckSummIsCorrect:=(tmp1==tmp2)
        }
        else if (AddCS=2 or (AddCS=3 and RS232_Bytes_Received>2))
        {
          tmp0:=CRC16(Bufer,RS232_Bytes_Received-AddCSLenght)
          if AddCS=2
            tmp1:=Bufer[RS232_Bytes_Received]
          else if AddCS=3
            tmp1:=(Bufer[RS232_Bytes_Received]<<8)|Bufer[RS232_Bytes_Received-1]
          tmp2:=(AddCS=3) ? tmp0 : tmp0&0xff
          CheckSummIsCorrect:=(tmp1==tmp2)
        }
        ; определить тип пакета Modbus
        if (AddCS>=4)
        {
          Received_Message_Type:=""
          if  CheckSummIsCorrect
          {
            if (Received_Command>0 and Received_Command<5)
            {
              tmp1:=Bufer[3]
              if (RS232_Bytes_Received=tmp1+5)
                Received_Message_Type:="Ответ"
              else
                Received_Message_Type:="Запрос"
            }
            else if (Received_Command>4 and Received_Command<7)
              Received_Message_Type:="Запрос/Ответ"
            else if (Received_Command=0x0f or Received_Command=0x10)
            {
              tmp1:=Bufer[7]
              if (RS232_Bytes_Received=tmp1+9)
                Received_Message_Type:="Запрос"
              else
                Received_Message_Type:="Ответ"
            }
          }
        }
        ; отфильтровать пакеты
        Filtered:=0
        if (Filter_Addres_EN or Filter_Command_EN or Filter_Byte_EN or Filter_Message_EN>1 or Filter_CRC_EN)
        {
          if (Filter_Addres_EN and Received_Addres!=Filter_Addres)
            Filtered:=1
          if (Filter_Command_EN and Received_Command!=Filter_Command)
            Filtered:=1
          if (Filter_Byte_EN and Received_Byte!=Filter_Byte)
            Filtered:=1
          if (Filter_Message_EN=2 and Received_Message_Type="Запрос")
            Filtered:=1
          if (Filter_Message_EN=3 and Received_Message_Type="Ответ")
            Filtered:=1
          if (AddCS>1 and Filter_CRC_EN and !CheckSummIsCorrect)
            Filtered:=1
        }
        if !Filtered
        {
          GuiControl,-Redraw,% E_LV
          Add_rn:=0
          regcount:=0

          ; разобраться с принятыми данными
          loop,% RS232_Bytes_Received
          {
            rows++
            LV_Icon:=ICON_EMPTY
            LV_Hash:=""
            LV_DEC:=Bufer[a_index]
            LV_NumByte:=a_index-1
            LV_HEX:=format("0x{:02x}",LV_DEC)
            LV_Sym:=Chr(LV_HEX)
            LV_Bin:=DECtoBIN(LV_HEX)
            loop,% 8-strlen(LV_Bin)
              LV_Bin:="0" . LV_Bin
            LV_7Seg:=Arr7SEGtoASCII[(LV_DEC&~(1<<7))] . (LV_DEC&(1<<7)) ? "." : ""
            LV_WORD:=LV_INT:=LV_DWORD:=LV_DINT:=LV_Float:=LV_Double:=""
            if (RS232_Bytes_Received-a_index+2>2 and RS232_Bytes_Received>1)
            {
              varsetcapacity(LV_INT_tmp,2)
              numput(Bufer[a_index],LV_INT_tmp,1,"uchar")
              numput(Bufer[a_index+1],LV_INT_tmp,0,"uchar")
              LV_WORD:=numget(LV_INT_tmp,0,"ushort")
              LV_INT:=numget(LV_INT_tmp,0,"short")
              AddAdd(LV_INT)
            }
            if (RS232_Bytes_Received-a_index+2>4 and RS232_Bytes_Received>3)
            {
              varsetcapacity(LV_DINT_tmp,4)
              numput(Bufer[a_index],LV_DINT_tmp,3,"uchar")
              numput(Bufer[a_index+1],LV_DINT_tmp,2,"uchar")
              numput(Bufer[a_index+2],LV_DINT_tmp,1,"uchar")
              numput(Bufer[a_index+3],LV_DINT_tmp,0,"uchar")
              LV_DWORD:=numget(LV_DINT_tmp,0,"uint")
              LV_DINT:=numget(LV_DINT_tmp,0,"int")
              AddAdd(LV_DINT)
              LV_Float:=numget(LV_DINT_tmp,0,"float")
              AddAdd(LV_Float)
            }
            if (RS232_Bytes_Received-a_index+2>8 and RS232_Bytes_Received>7)
            {
              varsetcapacity(LV_Double_tmp,8)
              numput(Bufer[a_index],LV_Double_tmp,7,"uchar")
              numput(Bufer[a_index+1],LV_Double_tmp,6,"uchar")
              numput(Bufer[a_index+2],LV_Double_tmp,5,"uchar")
              numput(Bufer[a_index+3],LV_Double_tmp,4,"uchar")
              numput(Bufer[a_index+4],LV_Double_tmp,3,"uchar")
              numput(Bufer[a_index+5],LV_Double_tmp,2,"uchar")
              numput(Bufer[a_index+6],LV_Double_tmp,1,"uchar")
              numput(Bufer[a_index+7],LV_Double_tmp,0,"uchar")
              LV_Double:=numget(LV_Double_tmp,0,"Double")
              AddAdd(LV_Double)
            }

            if EN_Kurvendrucker
              Monitorintext.=LV_Sym
            if (a_index=1)
            {
              LV_Icon:=ICON_START
              if !Received_Message_Type
                LV_Hash:="Start " Packages_Recieved
              else
                LV_Hash:=Received_Message_Type " " Packages_Recieved
            }


            if (AddCS>=4)
            {
        ; работаем с Modbus
        ;      if CheckSummIsCorrect
              {
                LV_NumByte:=""
                if (Received_Message_Type="Ответ") ; ---------------------------------------------------------
                {
                  if (Received_Command>0 and Received_Command<5)
                  {
                    if (a_index=3)
                    {
                      if !RTUErrorWasOcured
                        LV_NumByte:="Кол-во байт далее"
                      else
                        LV_NumByte:=RTUErr%LV_DEC%
                      LV_Icon:=ICON_INFO
                    }
                    if (a_index>3 and a_index<RS232_Bytes_Received-1)
                    {
                      if ((Received_Command>2 and Received_Command<5) and !(a_index&1))
                      {
                        LV_NumByte:="+" regcount " (+0x" format("{:x}",regcount) ")"
                        regcount++
                        LV_Icon:=ICON_REGISTER
                        LV_Hash:="Значения регистров по смещению"
                      }
                      if (Received_Command>0 and Received_Command<3)
                      {
                        LV_NumByte:=(regcount+1)*8 "-" regcount*8
                        regcount++
                        LV_Icon:=ICON_REGISTER
                        LV_Hash:="Значения битов по смещению"
                      }
                    }
                  }
                  else if (Received_Command=0xf or Received_Command=0x10)
                  {
                    if (a_index=3)
                    {
                      LV_NumByte:="Стартовый регистр"
                      LV_Icon:=ICON_INFO
                      LV_tmp:=LV_WORD
                    }
                    if (a_index=4)
                      LV_NumByte:=LV_tmp " (" format("0x{:x}",LV_tmp) ")"
                    if (a_index=5)
                    {
                      LV_NumByte:="Кол-во запрашиваемых регистров"
                      LV_Icon:=ICON_INFO
                      LV_tmp:=LV_WORD
                    }
                    if (a_index=6)
                      LV_NumByte:=LV_tmp " (" format("0x{:x}",LV_tmp) ")"
                  }
                }
                else if (Received_Message_Type="Запрос") ; ---------------------------------------------------------
                {
                  if (Received_Command>0 and Received_Command<5)
                  {
                    if (a_index=3)
                    {
                      LV_NumByte:="Стартовый регистр"
                      LV_Icon:=ICON_INFO
                      LV_tmp:=LV_WORD
                    }
                    if (a_index=4)
                      LV_NumByte:=LV_tmp " (" format("0x{:x}",LV_tmp) ")"
                    if (a_index=5)
                    {
                      if (Received_Command<3)
                        LV_NumByte:="Кол-во запрашиваемых бит"
                      if  (Received_Command>2)
                        LV_NumByte:="Кол-во запрашиваемых регистров"
                      LV_Icon:=ICON_INFO
                      LV_tmp:=LV_WORD
                    }
                    if (a_index=6)
                      LV_NumByte:=LV_tmp " (" format("0x{:x}",LV_tmp) ")"
                  }
                  else if (Received_Command=0xf or Received_Command=0x10)
                  {
                    if (a_index=3)
                    {
                      LV_NumByte:="Стартовый регистр"
                      LV_Icon:=ICON_INFO
                      LV_tmp:=LV_WORD
                    }
                    if (a_index=4)
                      LV_NumByte:=LV_tmp " (" format("0x{:x}",LV_tmp) ")"
                    if (a_index=5)
                    {
                      LV_NumByte:="Кол-во записываемых регистров"
                      LV_Icon:=ICON_INFO
                      LV_tmp:=LV_WORD
                    }
                    if (a_index=6)
                      LV_NumByte:=LV_tmp " (" format("0x{:x}",LV_tmp) ")"
                    if (a_index=7)
                    {
                      LV_NumByte:="Кол-во байт далее = " LV_DEC
                      LV_Icon:=ICON_INFO
                      LV_tmp:=LV_WORD
                    }
                    if (a_index>7 and a_index<RS232_Bytes_Received-1)
                    {
                      if (Received_Command=0x10 and !(a_index&1))
                      {
                        LV_NumByte:="+" regcount " (+0x" format("{:x}",regcount) ")"
                        regcount++
                        LV_Icon:=ICON_REGISTER
                        LV_Hash:="Значения регистров по смещению"
                      }
                      if (Received_Command=0xf)
                      {
                        LV_NumByte:=(regcount+1)*8-1 "-" regcount*8
                        regcount++
                        LV_Icon:=ICON_REGISTER
                        LV_Hash:="Значения битов по смещению"
                      }
                    }
                  }
                }
                else if (Received_Message_Type="Запрос/Ответ") ; ---------------------------------------------------------
                {
                  if (a_index=3)
                  {
                    LV_NumByte:="Регистр"
                    LV_Icon:=ICON_INFO
                    LV_tmp:=LV_WORD
                  }
                  if (a_index=4)
                    LV_NumByte:=LV_tmp " (" format("0x{:x}",LV_tmp) ")"
                  if (a_index=5)
                  {
                    LV_NumByte:="Значение"
                    LV_Icon:=ICON_INFO
                    if (LV_WORD=0xff00)
                      LV_tmp:="ON (" format("0x{:x}",LV_WORD) ")"
                    else if (LV_WORD=0x0000)
                      LV_tmp:="OFF (" format("0x{:x}",LV_WORD) ")"
                    else
                      LV_tmp:=LV_WORD " (" format("0x{:x}",LV_WORD) ")"
                  }
                  if (a_index=6)
                    LV_NumByte:=LV_tmp
                }
                if (a_index=1)
                  LV_NumByte:="Адрес"
                if (a_index=2)
                {
                  RTUErrorWasOcured:=0
                  if (LV_DEC&0x80)
                  {
                    RTUErrorWasOcured:=1
                    LV_NumByte:="Ошибка"
                    LV_Icon:=ICON_ERROR
                  }
                  else
                  {
                    LV_NumByte:=RTUFunc%LV_DEC%
                    LV_Hash:=RTUF%LV_DEC%
                  }
                }
                if (a_index=3 and RTUErrorWasOcured)
                {
                  LV_NumByte:=RTUErr%LV_DEC%
                  LV_Icon:=ICON_INFO
                }
              }
        ;      else
        ;        LV_NumByte:=""
              if (a_index>RS232_Bytes_Received-2)
                  LV_NumByte:=""
            }
            if (a_index>1 and AddCS>1 and !CheckSummIsCorrect)
              LV_Icon:=ICON_X
            if (AddCS>1)
            {
              if ((AddCS>2 and a_index=(RS232_Bytes_Received-1)) or (a_index=RS232_Bytes_Received))
              {
                if CheckSummIsCorrect
                {
                  LV_Icon:=ICON_V
                  LV_Hash:="CRC" ;"V"
                  LV_NumByte:="Контрольная сумма верна"
                }
                else
                {
                  LV_Icon:=ICON_CRC_ERROR
                  LV_Hash:="CRC" ;"X"
                  LV_NumByte:="Ошибка контрольной суммы"
                }
              }
            }
        ; -------------------------------------------------------------------------------------
            if !EN_Kurvendrucker
              LV_Add("Icon" LV_Icon,LV_Hash,a_index-1,LV_NumByte,LV_Bin,LV_Sym,LV_HEX,LV_DEC,LV_WORD,LV_INT,LV_DWORD,LV_DINT,LV_Float,LV_Double)
        ;  #|№ байта|№ байта или описание|BIN|SYM|HEX|DEC|INT|WORD|DINT|DWORD|Float|Double
        ; -------------------------------------------------------------------------------------
            if !(LV_HEX=0x0a or LV_HEX=0x0d)
              tmp_intext.=(LV_HEX ? LV_Sym : " ")
            if (LV_HEX=0x0a) ; and LV_HEX_old=0x0d)
            {
              InText:=tmp_intext . (instr(tmp_intext,"`r`n") ? "" : "`r`n")
              tmp_intext=
              Add_rn:=1
              gosub,showtext
            }
            LV_HEX_old:=LV_HEX
          }
          if !EN_Kurvendrucker
            LV_Add("Icon" 0)
          GuiControl,+Redraw,% E_LV
          GuiControl,,Progress,% rows/Rownums*100
          if (rows>Rownums)
            gosub,Update
          if (tmp_intext)
          {
            InText:=tmp_intext . (AddCRLF ? "`r`n" : "")
            Add_rn:=1
            tmp_intext=
            gosub,showtext
          }
          if (!FollowSelect and !LV_GetCount("S"))
          {
            LV_Modify(LV_GetCount(),"Vis")
            sleep,10
          }
          else if (FollowSelect and CountSelected:=LV_GetCount("S"))
          {
            startpos:=LV_GetCount()-OldGetCount+LV_GetNext()
            LV_Modify(0,"-Select")
            loop,% CountSelected
              LV_Modify(startpos+a_index-1,"Select")
            LV_Modify(startpos+CountSelected-1,"Vis Focus")
            sleep,10
            gosub,ListGetSelected
          }
          else if (FollowSelect and !LV_GetCount("S"))
          {
            LV_Modify(LV_GetCount(),"Vis")
            sleep,10
          }
          if (MonitorVar)
          {
            varsetcapacity(Monitorvalue_tmp,Slave_Len)
            loop,% Slave_Len
              numput(Bufer[Slave_Start_Byte+a_index],Monitorvalue_tmp,Slave_Len-a_index,"uchar")
  ;tooltip,% Slave_Data_Type "`n=" Bufer[Slave_Start_Byte+1] "=`nstb=" Slave_Start_Byte "`nsl=" Slave_Len,0,,7
            Monitorvalue:=numget(Monitorvalue_tmp,0,Slave_Data_Type)

            if GrafPause
              Gdip_X_Offset+=1
            else
              Gdip_Marker_mx-=Gdip_ZoomX
            Monitor_value.InsertAt(1,Monitorvalue)
            Monitor_value_T.InsertAt(1,a_hour ":" a_min "." a_sec "." a_MSec)
            gosub,GrafDraw
          }
          OldGetCount:=LV_GetCount()

          if EN_Kurvendrucker
          {
            if AutoEnding
            {
              if ((EndSymbol="" or TryFind>10) and strlen(Monitorintext)>100)
              {
                findMetaSbl(Monitorintext,SplSymbol,EndSymbol)
                TryFind:=0
                tooltip
              }
              else if (EndSymbol="")
              {
                TryFind++
                tooltip,% "Поиск разделителей " 100-strlen(Monitorintext)
              }
            }
  ;tooltip,% TryFind "`n""" SplSymbol """`n""" EndSymbol """"

            if (EndSymbol!="")
            while(EndPos:=instr(Monitorintext,EndSymbol))
            {
;              RS232_Bytes_Reqest:=EndPos*2
;              GuiControl,,RS232_Bytes_Reqest,% RS232_Bytes_Reqest
              Monitoringtext:=substr(Monitorintext,1,EndPos-1)
              StringTrimLeft,Monitorintext,Monitorintext,% EndPos
              if (SplSymbol==EndSymbol)
              {
                loop,parse,Monitoringtext,% EndSymbol
                {
                  if (a_loopfield!="")
                  {
                    tmp1:=a_loopfield
                    if instr(tmp1,"`r")
                      StringTrimRight,tmp1,tmp1,1
                    Monitor_value1.InsertAt(1,tmp1)
                    Monitor_value_T.InsertAt(1,a_hour ":" a_min "." a_sec "." a_MSec)
                    if GrafPause
                      Gdip_X_Offset+=1
                    else
                      Gdip_Marker_mx-=Gdip_ZoomX
                  }
                }
                NumOfGraf:=1
              }
              else
              {
                if (Monitoringtext!="")
                {
                  NumOfGraf:=0
                  loop,parse,Monitoringtext,% SplSymbol
                  {
                    if (a_loopfield!="")
                    {
                      tmp1:=a_loopfield
                      if instr(tmp1,"`r")
                        StringTrimRight,tmp1,tmp1,1
                      Monitor_value%a_index%.InsertAt(1,tmp1)
                      NumOfGraf++
                    }
                  }
                  Monitor_value_T.InsertAt(1,a_hour ":" a_min "." a_sec "." a_MSec)
                  if GrafPause
                    Gdip_X_Offset+=1
                  else
                    Gdip_Marker_mx-=Gdip_ZoomX
                }
              }
            }
            gosub,GrafDraw
          }
        }
      }
    }
  }
}
}
else ;Slave
{
  SysGet,CAPTION,4
  SysGet,XBORDER,5
  SysGet,YBORDER,6
  SysGet,XEDGE,45
  SysGet,YEDGE,46
  BORDERX:=XBORDER+XEDGE
  BORDERY:=YBORDER+YEDGE+CAPTION

  F:=Func("CheckPIDExist").Bind(2ndThread)
  settimer,% F,2000

;  varsetcapacity(Data,DataSize,0xFF)
;  slPointer:=&Data
;  while(numget(Data,_COMBusy,"uChar"))
;  {
;    PostMessage,0x8080,0,%slPointer%,,ahk_pid %2ndThread%
;    sleep,100
;    if (a_index>20)
;    {
;      msgbox,Slave`nОшибка получения указателя.
;      goto,EXIT
;    }
;  }
  Controlgetpos,Bx,By,Bw,Bh,,ahk_id %hBufinfo%
  Gui,1:Margin,0,0
  Gui,1:font,s8,Tahoma
  gui,1:+hwndhSlaveWin -border
  Gui,1:+0x40000000 -0x80000000
  gui,1:add,text,x0 y0 w200 vBufinfo,Текст
  gui,1:show,NA w200
  SetParent(hSlaveWin,hMainWin,,Bx+Bw-BORDERX,By-BORDERY-4)

  settimer,CheckMainExist,1000
  Menu,Tray,Icon
  SlavePID:=DllCall("GetCurrentProcessId")
  varsetcapacity(In_Data,RecieveDataOffset)
  MSM:=new _ClassMemory("Ahk_PID" 2ndThread)
  varsetcapacity(Send_Data,RecieveDataOffset-SendDataOffset)

SlaveLoop:
loop
{
  Loop_Time:=oldLoop_Time!="" ? a_tickcount-oldLoop_Time : 0
  oldLoop_Time:=a_tickcount
/*
Array =
Size			Int  4	+0
Data byte 0		byte 1	+4
Data byte 1		byte 1
Data byte 2		byte 1
Data byte n		byte 1
Size			Int  4
Data byte 0		byte 1
Data byte 1		byte 1
Data byte 2		byte 1
Data byte n		byte 1
...

if Size == 0xFFFFFFFF
  End
*/

  if MSM.Read(msPointer+_Diskonnect,"UChar")
  {
    PackagesRecieved:=0
    RS232_Close(RS232_FileHandle)
  }
  else if (MSM.Read(msPointer+_ApplyCOMSettings,"UChar"))
  {
    MSM.write(msPointer+_ApplyCOMSettings,0,"UChar")
    PackagesRecieved:=0
    PackagesSended:=0
;    DataPos:=0
    RS232_Close(RS232_FileHandle)
    varsetcapacity(var,12)
    MSM.ReadRaw(msPointer+_COMPort,var,12)
    COMPort:=StrGet(&var,6)
    RS232_Baud:=MSM.Read(msPointer+_RS232_Baud,"Int")
    RS232_Parity:=chr(MSM.Read(msPointer+_RS232_Parity,"Int"))
    RS232_Data:=MSM.Read(msPointer+_RS232_Data,"Int")
    RS232_Stop:=MSM.Read(msPointer+_RS232_Stop,"Int")
    ReadIntervalTimeout:=MSM.Read(msPointer+_ReadIntervalTimeout,"Int")
;tooltip,% COMPort "`n" RS232_Baud "`n" RS232_Parity "`n" RS232_Data "`n" RS232_Stop "`n" ReadIntervalTimeout
    RS232_FileHandle:=RS232_Get_FileHandle(COMPort
                                          ,RS232_Baud
                                          ,RS232_Parity
                                          ,RS232_Data
                                          ,RS232_Stop
                                          ,ReadIntervalTimeout)

    if !RS232_FileHandle
      MSM.write(msPointer+_COMBusy,1,"UChar")
  }
  if COMFail
  {
    RS232_FileHandle:=0
    MSM.write(msPointer+_COMFail,1,"UChar")
    COMFail:=0
  }
  if RS232_FileHandle
  {
    if MSM.Read(msPointer+_SendDataNow,"UChar")
    {
      PackagesSended++
      MSM.write(msPointer+_SendDataNow,0,"UChar")
      size:=MSM.Read(msPointer+_SendSize,"Int")
      if size
      {
        MSM.ReadRaw(msPointer+SendDataOffset,Send_Data,size)
        RS232_Write(RS232_FileHandle,&Send_Data,size)
      }
    }
    MSM.write(msPointer+_DataPos,DataPos,"Int")
    MSM.write(msPointer+_PackagesRecieved,PackagesRecieved,"Int")
;    sleep,1
;tooltip,% RS232_FileHandle "`n" RS232_Bytes_Reqest "`n" RS232_Bytes_Received "`n" &Data "`n" DataPos
    RS232_Read(RS232_FileHandle,RS232_Bytes_Reqest,&In_Data,RS232_Bytes_Received)
;tooltip,% RS232_FileHandle "`n" RS232_Bytes_Reqest "`n" RS232_Bytes_Received "`n" &Data "`n" DataPos
    if RS232_Bytes_Received
    {
      MSM.WriteRaw(msPointer+RecieveDataOffset+DataPos+4,&In_Data,RS232_Bytes_Received)
      MSM.write(msPointer+RecieveDataOffset+DataPos,RS232_Bytes_Received,"Int")
      DataPos+=RS232_Bytes_Received+4
      PackagesRecieved++
      MSM.write(msPointer+_DataAvailable,1,"UChar")
    }
    if (DataPos>DataSize-RecieveDataOffset-10240)
    {
      MSM.write(msPointer+RecieveDataOffset+DataPos,0xFFFFFFFE,"UInt")
;      loop,% DataSize-RecieveDataOffset-10240*2
;        MSM.write(msPointer+RecieveDataOffset+a_index-1,0xFF,"UChar")
      DataPos:=0
      PackagesRecieved:=0
    }
    GuiControl,,Bufinfo,% "в буфере " PackagesRecieved ", отправлено " PackagesSended " пакетов."
  }
  else
    sleep,100
}
}
return

CheckMainExist:
process,exist,% 2ndThread
if !errorlevel
  exitapp
return

ClearQueue:
ReadPos:=NumGet(Data,_DataPos,"Int")
ReadPack:=NumGet(Data,_PackagesRecieved,"Int")
if (ReadPos<0)
  ReadPos:=0
if (ReadPack<0)
  ReadPack:=0
return

showtext:
  SendMessage, 0xB0, &E_Text_start, &E_Text_end, , ahk_id %E_Text%  ; EM_GETSEL
  SendMessage, 0x0E, , , , ahk_id %E_Text%    ;  WM_GETTEXTLENGTH
  textlength:=Errorlevel
  if !(NumGet(E_Text_start)=textlength)
    GuiControl, -Redraw, %E_Text%
  SendMessage, 0x00B1, textlength, textlength, , ahk_id %E_Text%    ;  EM_SETSEL
  SendMessage, 0x00C2 , TRUE, &InText,, ahk_id %E_Text%  ;	EM_REPLACESEL
  if !(NumGet(E_Text_start)=textlength)
    SendMessage, 0x0B1, NumGet(E_Text_start), NumGet(E_Text_end), , ahk_id %E_Text%  ; EM_SETSEL
  GuiControl, +Redraw, %E_Text%
  return

DiskonnectCOM:
  GuiControl,,_text_,% COMPort "`n`t`tОтключен"
  NumPut(1,Data,_Diskonnect,"UChar")
  return

Update:
  Packages_Recieved:=0
  rows=
  GuiControl,,_InText_
  GuiControl,,Progress,0
  LV_Delete()
  tmp_intext=
  return

SkeepPack:
  SkeepPack:=!SkeepPack
  if SkeepPack
  {
    menu,ListRightClick2,Check,Пропуск пакетов ;
    gosub,ClearQueue
  }
  else
    menu,ListRightClick2,UnCheck,Пропуск пакетов ;
  return

ToTheEnd:
  SendMessage, 0x0E, , , , ahk_id %E_Text%    ;  WM_GETTEXTLENGTH
  textlength:=Errorlevel
  SendMessage, 0x00B1, textlength, textlength, , ahk_id %E_Text%    ;  EM_SETSEL
  GuiControl, +Redraw, %E_Text%
  LV_Modify(0,"-Select")
  return

ResetUnCOMPort:
UnCOMPort:=""
return

ComList:
ComPortList:=SearchCOM("",1)
GuiControl,,% E_Text,% "`n`nНайденные в системе порты:`n" (ComPortList ? ComPortList : "Портов нет.")
return

AceptSettings:
  Gui,1:submit,nohide
  AddCSLenght:=(AddCS>2 ? 2:AddCS-1)
  settimer,ComList,1000
  COMPort:=""
  while(!COMPort)
  {
    Gui,1:submit,nohide
    sleep,100
    dots.=">"
    if (strlen(dots)>20)
      dots:=""
    if (ChoiceCOM=1)
      COMPort:=SearchCOM("USB")
    else if (ChoiceCOM=2)
      COMPort:="COM" ManualCOM
    else if (ChoiceCOM=3)
      COMPort:=SearchCOM(ManualCOMByName)
    if (!COMPort and ChoiceCOM)
      GuiControl,,_text_,% COMPort "`nCOM-порт не обнаружен`nПоиск " dots
    if (COMPort==UnCOMPort)
      COMPort:=""
  }
  settimer,ComList,off
  settimer,ResetUnCOMPort,off
  GuiControl,,_text_,% COMPort "`n`tподключение"
  GuiControl,,% E_Text,% COMPort " подключение"
  doonce:=0
  StrPut(COMPort,&Data+_COMPort,12)
  NumPut(RS232_Baud,Data,_RS232_Baud,"Int")
  NumPut(asc(RS232_Parity),Data,_RS232_Parity,"Int")
  NumPut(RS232_Data,Data,_RS232_Data,"Int")
  NumPut(RS232_Stop,Data,_RS232_Stop,"Int")
  NumPut(ReadIntervalTimeout,Data,_ReadIntervalTimeout,"Int")
  NumPut(0,Data,_Diskonnect,"UChar")
  NumPut(1,Data,_ApplyCOMSettings,"UChar")
  sleep,300
;  gosub,ClearQueue
  return

Submit2:
  Gui,1:submit,nohide
  AddCSLenght:=(AddCS>2 ? 2:AddCS-1)
  return

Submit:
  Gui,1:submit,nohide
  AddCSLenght:=(AddCS>2 ? 2:AddCS-1)
  if (a_GuiControl="AddCS")
  {
    if (AddCS=5)
    {
      GuiControl,Move,% E_1,y420 h75
      loop,2
        GuiControl,Show,% hE_RTU_Command_%a_index%
      loop,5
        GuiControl,Show,% hE_RTU_StartReg_%a_index%
      loop,5
        GuiControl,Show,% hE_RTU_Addres_%a_index%
      loop,5
        GuiControl,Show,% hE_RTU_RegNum_%a_index%
      loop,2
        GuiControl,Show,% hE_RTU_Val_%a_index%
    }
    else
    {
      GuiControl,enable,% E_1
      GuiControl,Move,% E_1,y210 h300
      loop,2
        GuiControl,hide,% hE_RTU_Command_%a_index%
      loop,5
        GuiControl,hide,% hE_RTU_StartReg_%a_index%
      loop,5
        GuiControl,hide,% hE_RTU_Addres_%a_index%
      loop,5
        GuiControl,hide,% hE_RTU_RegNum_%a_index%
      loop,2
        GuiControl,hide,% hE_RTU_Val_%a_index%
    }
    if (AddCS>=4)
    {
      GuiControl,Show,% hFiltE4
      GuiControl,Show,% hFiltE5
      GuiControl,Show,% hFiltE6
      GuiControl,Show,% hFiltE7
;      GuiControl,Show,% hAutoByRe
      loop,3
        GuiControl,disable,% hFiltE%a_index%
      Gui,4:submit,nohide
      Filter_CommandFlag:=1
      if (Filter_CommandRTU>0 and Filter_CommandRTU<7)
        Filter_Command:=Filter_CommandRTU
      else if (Filter_CommandRTU>6)
        Filter_Command:=Filter_CommandRTU+8
      GuiControl,,% hFiltE1,% Filter_Command
    }
    else
    {
      GuiControl,hide,% hFiltE4
      GuiControl,hide,% hFiltE5
      GuiControl,hide,% hFiltE6
      GuiControl,hide,% hFiltE7
;      GuiControl,hide,% hAutoByRe
      loop,3
        GuiControl,enable,% hFiltE%a_index%
    }
    if (AddCS>1)
      GuiControl,show,% hFiltE8
    else
      GuiControl,hide,% hFiltE8
  }
  if (AddCS=5)
  {
    if (RTU_Command>0 and RTU_Command<5)
    {
      loop,5
        guicontrol,enable,% hE_RTU_RegNum_%a_index%
      loop,2
        GuiControl,disable,% hE_RTU_Val_%a_index%
      GuiControl,disable,% E_1
    }
    if (RTU_Command=5 or RTU_Command=6)
    {
      if SendDataRepitedly
        gosub,SendDataRepitedly
      loop,5
        guicontrol,disable,% hE_RTU_RegNum_%a_index%
      loop,2
        GuiControl,enable,% hE_RTU_Val_%a_index%
      GuiControl,enable,% E_1
    }
    if (RTU_Command=7 or RTU_Command=8)
    {
      if SendDataRepitedly
        gosub,SendDataRepitedly
      loop,5
        guicontrol,enable,% hE_RTU_RegNum_%a_index%
      loop,2
        GuiControl,enable,% hE_RTU_Val_%a_index%
      GuiControl,enable,% E_1
    }
  }
  if (hi_lim<1)
    hi_lim:=1
  return

ChangeAddres:
  Gui,1:submit,nohide
  AddCSLenght:=(AddCS>2 ? 2:AddCS-1)
  if (a_GuiControl="RTU_SlaveAdress")
  {
    if RTU_SlaveAdressHEXFlag
    {
      RTU_SlaveAdressHEXFlag:=0
      return
    }
    RTU_SlaveAdressFlag:=1
    GuiControl,,RTU_SlaveAdressHEX,% format("{:X}",RTU_SlaveAdress)
  }
  else if (a_GuiControl="RTU_SlaveAdressHEX")
  {
    if RTU_SlaveAdressFlag
    {
      RTU_SlaveAdressFlag:=0
      return
    }
    RTU_SlaveAdressHEXFlag:=1
    tmp:="0x" RTU_SlaveAdressHEX
    GuiControl,,RTU_SlaveAdress,% tmp+0
  }
  return

ChangeStartReg:
  Gui,1:submit,nohide
  AddCSLenght:=(AddCS>2 ? 2:AddCS-1)
  if (a_GuiControl="RTU_StartReg")
  {
    if RTU_StartRegHEXFlag
    {
      RTU_StartRegHEXFlag:=0
      return
    }
    RTU_StartRegFlag:=1
    GuiControl,,RTU_StartRegHEX,% format("{:X}",RTU_StartReg)
  }
  else if (a_GuiControl="RTU_StartRegHEX")
  {
    if RTU_StartRegFlag
    {
      RTU_StartRegFlag:=0
      return
    }
    RTU_StartRegHEXFlag:=1
    tmp:="0x" RTU_StartRegHEX
    GuiControl,,RTU_StartReg,% tmp+0
  }
  return

ChangeRegNum:
  Gui,1:submit,nohide
  AddCSLenght:=(AddCS>2 ? 2:AddCS-1)
  if (a_GuiControl="RTU_RegNum")
  {
    if RTU_RegNumHEXFlag
    {
      RTU_RegNumHEXFlag:=0
      return
    }
    RTU_RegNumFlag:=1
    GuiControl,,RTU_RegNumHEX,% format("{:X}",RTU_RegNum)
  }
  else if (a_GuiControl="RTU_RegNumHEX")
  {
    if RTU_RegNumFlag
    {
      RTU_RegNumFlag:=0
      return
    }
    RTU_RegNumHEXFlag:=1
    tmp:="0x" RTU_RegNumHEX
    GuiControl,,RTU_RegNum,% tmp+0
  }
  return

ChangeFilter_Addres:
  Gui,4:submit,nohide
  if (a_GuiControl="Filter_Addres")
  {
    if Filter_AddresHEXFlag
    {
      Filter_AddresHEXFlag:=0
      return
    }
    Filter_AddresFlag:=1
    GuiControl,,Filter_AddresHEX,% format("{:X}",Filter_Addres)
  }
  else if (a_GuiControl="Filter_AddresHEX")
  {
    if Filter_AddresFlag
    {
      Filter_AddresFlag:=0
      return
    }
    Filter_AddresHEXFlag:=1
    tmp:="0x" Filter_AddresHEX
    GuiControl,,Filter_Addres,% tmp+0
  }
  return

ChangeFilter_Command:
  Gui,4:submit,nohide
  if (a_GuiControl="Filter_Command")
  {
    if (Filter_CommandHEXFlag)
    {
      Filter_CommandHEXFlag:=0
      return
    }
    Filter_CommandFlag:=1
    GuiControl,,Filter_CommandHEX,% format("{:X}",Filter_Command)
  }
  else if (a_GuiControl="Filter_CommandHEX")
  {
    if (Filter_CommandFlag)
    {
      Filter_CommandFlag:=0
      return
    }
    Filter_CommandHEXFlag:=1
    tmp:="0x" Filter_CommandHEX
    GuiControl,,Filter_Command,% tmp+0
    Filter_Command:=tmp+0
  }
  else if (a_GuiControl="Filter_CommandRTU")
  {
    Filter_CommandFlag:=1
    if (Filter_CommandRTU>0 and Filter_CommandRTU<7)
      Filter_Command:=Filter_CommandRTU
    else if (Filter_CommandRTU>6)
      Filter_Command:=Filter_CommandRTU+8
    GuiControl,,Filter_Command,% Filter_Command
  }
  return

ChangeFilter_Byte:
  Gui,4:submit,nohide
  if (a_GuiControl="Filter_Byte")
  {
    if Filter_ByteHEXFlag
    {
      Filter_ByteHEXFlag:=0
      return
    }
    Filter_ByteFlag:=1
    GuiControl,,Filter_ByteHEX,% format("{:X}",Filter_Byte)
  }
  else if (a_GuiControl="Filter_ByteHEX")
  {
    if Filter_ByteFlag
    {
      Filter_ByteFlag:=0
      return
    }
    Filter_ByteHEXFlag:=1
    tmp:="0x" Filter_ByteHEX
    GuiControl,,Filter_Byte,% tmp+0
  }
  return

ChoseCOMNo:
  Gui,1:submit,nohide
  AddCSLenght:=(AddCS>2 ? 2:AddCS-1)
  if (!noChoseCOMNo)
  {
    noChoseCOMNo:=1
    return
  }
  GuiControl,,% hCOMNo,1
  return

ChoseCOMName:
  Gui,1:submit,nohide
  AddCSLenght:=(AddCS>2 ? 2:AddCS-1)
  GuiControl,,% hCOMName,1
  return

AddSettings:
  Gui,2:show
  return
CalcTimeout:
  Gui,2:submit,nohide
  stringleft,RS232_Parity_Choice,RS232_Parity_Choice,1
  RS232_Baud:=RS232_Baud_Choice
  RS232_Parity:=RS232_Parity_Choice
  RS232_Data:=RS232_Data_Choice
  RS232_Stop:=RS232_Stop_Choice
  Timeout:=((RS232_Data+4)/RS232_Baud)*1000
  ReadIntervalTimeout:=Ceil(Timeout)+1
  GuiControl,,% hTimeout,% "Расчетный = " Timeout " ms"
  GuiControl,,% hRITimeout,% ReadIntervalTimeout
  return
CancelAddSettings:
  Gui,2:hide
  Gui,1:show
  return
EndAddSettings:
  Gui,2:submit
  Gui,1:show
  stringleft,RS232_Parity_Choice,RS232_Parity_Choice,1
  RS232_Baud:=RS232_Baud_Choice
  RS232_Parity:=RS232_Parity_Choice
  RS232_Data:=RS232_Data_Choice
  RS232_Stop:=RS232_Stop_Choice
  gosub,AceptSettings
  return
DefaultSettings:
  GuiControl,Choose,RS232_Baud_Choice,14
  GuiControl,Choose,RS232_Parity_Choice,1
  GuiControl,Choose,RS232_Data_Choice,4
  GuiControl,Choose,RS232_Stop_Choice,1
  gosub,CalcTimeout
  return

ButtonSendData:
  Gui,1:submit,nohide
  AddCSLenght:=(AddCS>2 ? 2:AddCS-1)
  SendData:=1
  gosub,SendData
  return

SendData:
  Gui,1:submit,nohide
  SendData:=0
  if SendDataRepitedly
  {
    Timeout:=Ceil(((RS232_Data_Choice+4)/RS232_Baud_Choice)*1000)*30
    SendTimer+=Loop_Time
    if (SendTimer<Timeout)
      return
    SendTimer:=0
  }
  else
    SendTimer:=0
  if (AddCS=5)
  {
    if (RTU_Command>0 and RTU_Command<5)
    {
;      if (Auto_Bytes_Reqest and (RTU_Command=1 or RTU_Command=2))
;        RS232_Bytes_Reqest:=Ceil(RTU_RegNum/8)+6
;      if (Auto_Bytes_Reqest and (RTU_Command=3 or RTU_Command=4))
;        RS232_Bytes_Reqest:=RTU_RegNum*2+6
      size_to_send:=6
      varsetcapacity(VarDataToSend,size_to_send+AddCSLenght)
      NumPut(RTU_SlaveAdress,VarDataToSend,0,"uchar")
      NumPut(RTU_Command,VarDataToSend,1,"uchar")
      NumPut(SwapByte(RTU_StartReg),VarDataToSend,2,"ushort")
      NumPut(SwapByte(RTU_RegNum),VarDataToSend,4,"ushort")
      NumPut(CRC_RTU(VarDataToSend,size_to_send),VarDataToSend,size_to_send,"ushort")
    }
    else if (RTU_Command=5 or RTU_Command=6)
    {
;      if Auto_Bytes_Reqest
;        RS232_Bytes_Reqest:=9
      size_to_send:=6
      varsetcapacity(VarDataToSend,size_to_send+AddCSLenght)
      NumPut(RTU_SlaveAdress,VarDataToSend,0,"uchar")
      NumPut(RTU_Command,VarDataToSend,1,"uchar")
      NumPut(SwapByte(RTU_StartReg),VarDataToSend,2,"ushort")

      if (ChoiceFormat=1) ;text
        size_to_send_tmp:=STRToDATA(DataToSend,VarDataToSend_tmp)
      else if (ChoiceFormat=2) ;DEC
        size_to_send_tmp:=DECToDATA(DataToSend,VarDataToSend_tmp,1,ChoiceType,ChoiceSize)
      else if (ChoiceFormat=3) ;HEX
        size_to_send_tmp:=HEXstrToDATA(DataToSend,VarDataToSend_tmp)
      else if (ChoiceFormat=4) ;BIN
        size_to_send_tmp:=BINstrToDATA(DataToSend,VarDataToSend_tmp)

      NumPut(numget(VarDataToSend_tmp,0,"Uchar"),VarDataToSend,4,"ushort")
      NumPut(numget(VarDataToSend_tmp,1,"Uchar"),VarDataToSend,5,"ushort")
      NumPut(CRC_RTU(VarDataToSend,size_to_send),VarDataToSend,size_to_send,"ushort")
    }
    else if (RTU_Command=7 or RTU_Command=8)
    {
;      if Auto_Bytes_Reqest
;        RS232_Bytes_Reqest:=9
      if (RTU_Command=7)
      {
        RTU_Command=15
        RTU_ByteNum:=ceil(RTU_RegNum/8)
        size_to_send:=7+ceil(RTU_RegNum/8)
      }
      if (RTU_Command=8)
      {
        RTU_Command=16
        RTU_ByteNum:=RTU_RegNum*2
        size_to_send:=7+RTU_ByteNum
      }
      varsetcapacity(VarDataToSend,size_to_send+AddCSLenght)
      varsetcapacity(VarDataToSend,size_to_send+AddCSLenght)
      NumPut(RTU_SlaveAdress,VarDataToSend,0,"uchar")
      NumPut(RTU_Command,VarDataToSend,1,"uchar")
      NumPut(SwapByte(RTU_StartReg),VarDataToSend,2,"ushort")
      NumPut(SwapByte(RTU_RegNum),VarDataToSend,4,"ushort")
      NumPut(RTU_ByteNum,VarDataToSend,6,"uchar")
      if (ChoiceFormat=1) ;text
        size_to_send_tmp:=STRToDATA(DataToSend,VarDataToSend_tmp)
      else if (ChoiceFormat=2) ;DEC
        size_to_send_tmp:=DECToDATA(DataToSend,VarDataToSend_tmp,1,ChoiceType,ChoiceSize)
      else if (ChoiceFormat=3) ;HEX
        size_to_send_tmp:=HEXstrToDATA(DataToSend,VarDataToSend_tmp)
      else if (ChoiceFormat=4) ;BIN
        size_to_send_tmp:=BINstrToDATA(DataToSend,VarDataToSend_tmp)
      loop,% RTU_ByteNum
        NumPut(numget(VarDataToSend_tmp,a_index-1,"Uchar"),VarDataToSend,a_index+6,"uchar")
      NumPut(CRC_RTU(VarDataToSend,size_to_send),VarDataToSend,size_to_send,"ushort")
    }
    if Auto_Bytes_Reqest
      Reset_Bytes_Reqest:=1
    if (RTU_Command>4 and !BeliveMe)
      return
    else if (RTU_Command>4 and BeliveMe)
      guicontrol,,BeliveMe,0
  }
  else if (AddCS=4)
  {
    if (ChoiceFormat=1) ;text
      size_to_send:=STRToDATA(DataToSend,VarDataToSend)
    else if (ChoiceFormat=2) ;dec
      size_to_send:=DECToDATA(DataToSend,VarDataToSend,1,ChoiceType,ChoiceSize)
    else if (ChoiceFormat=3) ;HEX
      size_to_send:=HEXstrToDATA(DataToSend,VarDataToSend)
    else if (ChoiceFormat=4) ;BIN
      size_to_send:=BINstrToDATA(DataToSend,VarDataToSend)
    NumPut(CRC_RTU(VarDataToSend,size_to_send),VarDataToSend,size_to_send,"ushort")
  }
  else
  {
    if (ChoiceFormat=1) ;text
      size_to_send:=STRToDATA(DataToSend,VarDataToSend)
    else if (ChoiceFormat=2) ;dec
      size_to_send:=DECToDATA(DataToSend,VarDataToSend,1,ChoiceType,ChoiceSize)
    else if (ChoiceFormat=3) ;HEX
      size_to_send:=HEXstrToDATA(DataToSend,VarDataToSend)
    else if (ChoiceFormat=4) ;BIN
      size_to_send:=BINstrToDATA(DataToSend,VarDataToSend)
    if (AddCS>0)
      NumPut(CRC16(VarDataToSend,size_to_send),VarDataToSend,size_to_send,AddCS==2?"Uchar":"ushort")
  }
  if !size_to_send
    return
  size:=size_to_send+AddCSLenght
  if (size>1024)
  {
    SB_SetText("Слишком много данных для отправки. " size " byte.")
    return
  }
  loop,% size
    NumPut(NumGet(VarDataToSend,a_index-1,"UChar"),Data,SendDataOffset+a_index-1,"UChar")
  NumPut(size,Data,_SendSize,"Int")
  NumPut(1,Data,_SendDataNow,"uChar")
  return

SendDataRepitedly:
  SendDataRepitedly:=!SendDataRepitedly
  if SendDataRepitedly
    GuiControl,,% hSendDataRepitedly,Стоп ;
  else
    GuiControl,,% hSendDataRepitedly,Циклично ;
  return

DataToSendCheck:
  Gui,1:submit,nohide
  AddCSLenght:=(AddCS>2 ? 2:AddCS-1)
  tmp1:=DataToSend
  if (ChoiceFormat=2)
  {
    RegExMatch(DataToSend,"[0-9 -\.]+",tmp1)
    if instr(DataToSend,".")
    {
      hEDEC4once:=0
      GuiControl,,% hEDEC4,1
      GuiControl,disable,% hEDEC5
      GuiControl,disable,% hEDEC6
;      GuiControl,,% hEDEC7,1
    }
    else if instr(DataToSend,"-")
    {
      hEDEC4once:=0
      GuiControl,,% hEDEC3,1
      GuiControl,enable,% hEDEC5
      GuiControl,enable,% hEDEC6
    }
    if (!hEDEC4once and !instr(DataToSend,"-") and !instr(DataToSend,"."))
    {
      hEDEC4once:=1
      GuiControl,,% hEDEC2,1
      GuiControl,enable,% hEDEC5
      GuiControl,enable,% hEDEC6
    }
  }
  if (ChoiceFormat=3)
    RegExMatch(DataToSend,"[A-Fa-f0-9 ]+",tmp1)
  if (ChoiceFormat=4)
    RegExMatch(DataToSend,"[0-1 ]+",tmp1)
  if (DataToSend!=tmp1)
  {
    GuiControl,,DataToSend,% tmp1
    PostMessage,0x00B1,1000,1000,,ahk_id %E_1%    ;  EM_SETSEL
    PostMessage,0xB7,,,,ahk_id %E_1%    ;  EM_SCROLLCARET
  }
  return



ConvertToSYM: ; to text
  gosub,ResetDECChoise
  Gui,1:submit,nohide
  AddCSLenght:=(AddCS>2 ? 2:AddCS-1)
  if (PreviousConversion="hex") ; hex to text
  {
    tmp_DataToSend:=""
    ClearVar(DataToSend)
    loop,% ceil(strlen(DataToSend)/2)
    {
      stringright,tmp2,DataToSend,2
      tmp_DataToSend:=chr("0x"tmp2) . tmp_DataToSend
      StringTrimRight,DataToSend,DataToSend,2
    }
  }
  else if (PreviousConversion="bin") ; bin to text
  {
    size:=BINstrToDATA(DataToSend,tmp1)
    tmp_DataToSend:=DATAtoSTR(&tmp1,0,size)
  }
  else if (PreviousConversion="dec") ; dec to text
  {
    DECToDATA(DataToSend,tmp1,1,ChoiceType,ChoiceSize)
    tmp_DataToSend:=DATAtoSTR(&tmp1,0,ChoiceSize)
  }
  GuiControl,,DataToSend,% tmp_DataToSend
  PreviousConversion:="sym"
  return

ConvertToHEX: ; to hex
  gosub,ResetDECChoise
  Gui,1:submit,nohide
  AddCSLenght:=(AddCS>2 ? 2:AddCS-1)
  if (PreviousConversion="sym") ; text to hex
  {
    tmp_DataToSend:=""
    loop,parse,DataToSend
      tmp_DataToSend.=format("{:02X}",Asc(A_LoopField)) " "
    stringtrimright,tmp_DataToSend,tmp_DataToSend,1
  }
  else if (PreviousConversion="bin") ; bin to hex
  {
    ClearVar(DataToSend)
    tmp_DataToSend:=format("{:02X}",BINtoDEC(DataToSend))
    if (strlen(tmp_DataToSend)&1)
      tmp_DataToSend:="0" . tmp_DataToSend
  }
  else if (PreviousConversion="dec") ; dec to hex
  {
    DECToDATA(DataToSend,tmp1,1,ChoiceType,ChoiceSize)
    tmp_DataToSend:=DATAtoHEX(&tmp1,0,ChoiceSize)
  }
  GuiControl,,DataToSend,% tmp_DataToSend
  PreviousConversion:="hex"
  return

ConvertToBIN: ; to bin
  gosub,ResetDECChoise
  Gui,1:submit,nohide
  AddCSLenght:=(AddCS>2 ? 2:AddCS-1)
  if (PreviousConversion="sym") ; text to bin
  {
    size:=STRToDATA(DataToSend,tmp0)
    tmp_DataToSend:=DATAtoBIN(&tmp0,0,size)
  }
  else if (PreviousConversion="hex") ; hex to bin
  {
    size:=HEXstrToDATA(DataToSend,tmp0)
    tmp_DataToSend:=DATAtoBIN(&tmp0,0,size)
  }
  else if (PreviousConversion="dec") ; dec to bin
  {
    DECToDATA(DataToSend,tmp1,1,ChoiceType,ChoiceSize)
    tmp_DataToSend:=DATAtoBIN(&tmp1,0,ChoiceSize)
  }
  GuiControl,,DataToSend,% tmp_DataToSend
  PreviousConversion:="bin"
  return

ConvertToDEC: ; to dec
  loop,8
    GuiControl,show,% hEDEC%a_index%
  Gui,1:submit,nohide
  AddCSLenght:=(AddCS>2 ? 2:AddCS-1)
  old_DataToSend:=DataToSend
  return
ConvertToDECRun:
  if !(ChoiceDEC and ChoiceDECSize)
    return
  ChoicePType:={1:"char",2:"short",3:"int",4:"int64"}
  if (ChoiceDEC=1)
    ChoiceType:="U" ChoicePType[ChoiceDECSize]
  else if (ChoiceDEC=2)
    ChoiceType:="" ChoicePType[ChoiceDECSize]
  else if (ChoiceDEC=3 and ChoiceDECSize=3)
    ChoiceType:="float"
  else if (ChoiceDEC=3 and ChoiceDECSize=4)
    ChoiceType:="duoble"
  if (ChoiceDECSize=3)
    ChoiceSize:=4
  else if (ChoiceDECSize=4)
    ChoiceSize:=8
  else
    ChoiceSize:=ChoiceDECSize
  if (PreviousConversion="sym") ; text to dec
  {
    STRToDATA(old_DataToSend,tmp0,1)
    tmp_DataToSend:=numget(tmp0,0,ChoiceType)
  }
  else if (PreviousConversion="hex") ; hex to dec
  {
    ClearVar(old_DataToSend)
    HEXstrToDATA(old_DataToSend,tmp0,1)
    tmp_DataToSend:=numget(tmp0,0,ChoiceType)
  }
  else if (PreviousConversion="bin") ; bin to dec
  {
    BINstrToDATA(old_DataToSend,tmp0,1)
    tmp_DataToSend:=numget(tmp0,0,ChoiceType)
  }
  GuiControl,,DataToSend,% tmp_DataToSend
  PreviousConversionWasDEC:=1
  return

ChooseDEC:
  Gui,1:submit,nohide
  AddCSLenght:=(AddCS>2 ? 2:AddCS-1)
  if (ChoiceDEC=3)
  {
    if (ChoiceDECSize and ChoiceDECSize<3)
    {
      GuiControl,,% hEDEC5,0
      GuiControl,,% hEDEC6,0
      GuiControl,,% hEDEC7,1
      ChoiceDECSize:=3
    }
    GuiControl,disable,% hEDEC5
    GuiControl,disable,% hEDEC6
  }
  else
  {
    GuiControl,enable,% hEDEC5
    GuiControl,enable,% hEDEC6
  }
  gosub,ConvertToDECRun
  return

ChooseDECSize:
  Gui,1:submit,nohide
  AddCSLenght:=(AddCS>2 ? 2:AddCS-1)
  GuiControl,disable,% hEDEC%a_index%
  gosub,ConvertToDECRun
  return

ResetDECChoise:
  loop,8
    GuiControl,hide,% hEDEC%a_index%
  GuiControl,,% hEDEC2,1
  GuiControl,,% hEDEC2,0
  GuiControl,,% hEDEC5,1
  GuiControl,,% hEDEC5,0
  ChoiceDEC:=0
  ChoiceDECSize:=0
  if PreviousConversionWasDEC
  {
    PreviousConversion:="dec"
    PreviousConversionWasDEC:=0
  }
  return

DataFilter:
  Gui,4:show,,Фильтровать данные
  if (AddCS>=4)
  {
    Gui,4:submit,nohide
    Filter_CommandFlag:=1
    if (Filter_CommandRTU>0 and Filter_CommandRTU<7)
      Filter_Command:=Filter_CommandRTU
    else if (Filter_CommandRTU>6)
      Filter_Command:=Filter_CommandRTU+8
    GuiControl,,% hFiltE1,% Filter_Command
  }
  return

DataFilterOk:
  Gui,4:submit
  return

ListGetSelected:
  if (A_GuiEvent="RightClick")
  {
    LV_Column:=LV_SubItemHitTest(E_LV)
    LV_Row:=LV_GetNext(RowNumber)
    if (LV_Column>5 and LV_Column<14 and !EN_Kurvendrucker)
      menu,ListRightClick,enable,Отрисовать график ;
    else
      menu,ListRightClick,disable,Отрисовать график ;
    menu,ListRightClick,show
  }
  return

ListCopySelected:
  LV_GetText(tmp1,LV_Row,LV_Column)
  clipboard:=tmp1
  return

Kurvendrucker:
  if SendDataRepitedly
  {
    GuiControl,,EN_Kurvendrucker,0
    gosub,SendDataRepitedly
    return
  }
  if MonitorVar
    MonitorVar:=0
  if (!Gdip_Initialised)
  {
    msgbox,Библиотека GDip++ не поддерживается в системе.
    return
  }
  Gui,1:submit,nohide
  AddCSLenght:=(AddCS>2 ? 2:AddCS-1)
  Monitorintext:=""
  if EN_Kurvendrucker
  {
    Graf_Start_Time:=a_tickcount
    gosub,GrafOpen
    GuiControlGet,E_pos,Pos,% E_LV
    GuiControlGet,T_pos,Pos,% E_Text
    tmp_Deltamx:=-(E_posw-10)
    if !((E_posw+tmp_Deltamx)<10 or (T_posw-tmp_Deltamx)<10)
    {
      MoveControl(E_LV,,,tmp_Deltamx,,0)
      MoveControl(hProgress,tmp_Deltamx,,,,0)
      MoveControl(hProgress2,tmp_Deltamx,,-tmp_Deltamx,,0)
      MoveControl(E_Text,tmp_Deltamx,,-tmp_Deltamx,,0)
      MoveControl(hGrafPos,tmp_Deltamx,,-tmp_Deltamx,,0)
      if (tmp_Deltamx>0)
        Gdip_X_Offset-=tmp_Deltamx*2
    }
;    GuiControl,Hide,% E_LV
;    GuiControl,Hide,% hProgress
    GuiControl,Hide,% hTtEButton
    GuiControl,Hide,% hClButton
    GuiControl,Hide,FollowSelect
    GuiControl,Choose,% hCS,1
    loop,% MaxNumOfGraf+1
    {
      i:=a_index-1
      GuiControl,Show,% hECB%i%
    }
    gosub,GrafUpdate
  }
  else
  {
;    GuiControl,show,% E_LV
;    GuiControl,show,% hProgress
    GuiControl,show,% hTtEButton
    GuiControl,show,% hClButton
    GuiControl,show,FollowSelect
    loop,% MaxNumOfGraf+1
    {
      i:=a_index-1
      GuiControl,Hide,% hECB%i%
    }
    gosub,GrafClose
    GuiControlGet,E_pos,Pos,% E_LV
    GuiControlGet,T_pos,Pos,% E_Text
    tmp_Deltamx:=300
    if !((E_posw+tmp_Deltamx)<10 or (T_posw-tmp_Deltamx)<10)
    {
      MoveControl(E_LV,,,tmp_Deltamx,,0)
      MoveControl(hProgress,tmp_Deltamx,,,,0)
      MoveControl(hProgress2,tmp_Deltamx,,-tmp_Deltamx,,0)
      MoveControl(E_Text,tmp_Deltamx,,-tmp_Deltamx,,0)
      MoveControl(hGrafPos,tmp_Deltamx,,-tmp_Deltamx,,0)
      if (tmp_Deltamx>0)
        Gdip_X_Offset-=tmp_Deltamx*2
    }
    gosub,GrafUpdate
  }
  return

GrafDrawFromSelected:
  if (!Gdip_Initialised)
  {
    msgbox,Библиотека GDip++ не поддерживается в системе.
    return
  }
  if AutoMinMax
    hi_lim:=1
  MonitorVar:=1
  gosub,GrafOpen

  Graf_Start_Time:=a_tickcount
  LV_GetText(Slave_Start_Byte,LV_Row,2)
  LV_GetText(Slave_Addres,LV_Row-Slave_Start_Byte,7)
  LV_GetText(Slave_Command,LV_Row-Slave_Start_Byte+1,7)
  LV_GetText(Slave_ByteNum,LV_Row-Slave_Start_Byte+2,7)
  Slave_ByteNum+=5
  if (LV_Column=Column_HEX)
  {
    Slave_Data_Type:="char"
    Slave_Len:=1
  }
  if (LV_Column=Column_DEC)
  {
    Slave_Data_Type:="uchar"
    Slave_Len:=1
  }
  if (LV_Column=Column_WORD)
  {
    Slave_Data_Type:="ushort"
    Slave_Len:=2
  }
  if (LV_Column=Column_INT)
  {
    Slave_Data_Type:="short"
    Slave_Len:=2
  }
  if (LV_Column=Column_DWORD)
  {
    Slave_Data_Type:="uint"
    Slave_Len:=4
  }
  if (LV_Column=Column_DINT)
  {
    Slave_Data_Type:="int"
    Slave_Len:=4
  }
  if (LV_Column=Column_Float)
  {
    Slave_Data_Type:="float"
    Slave_Len:=4
  }
  if (LV_Column=Column_Double)
  {
    Slave_Data_Type:="Double"
    Slave_Len:=8
  }
  return

GrafClose:
  if MonitorVar
    MonitorVar:=0
  GuiControlGet,E_pos,Pos,% E_Text
  GuiControlGet,T_pos,Pos,% hGrafPos
  tmpy:=T_posh-10
  MoveControl(E_Text,,,,tmpy,0)
  MoveControl(hProgress2,,tmpy,,,0)
  MoveControl(hGrafPos,,tmpy,,-tmpy,0)
  gosub,GrafRedraw
  return

GrafOpen:
  GuiControlGet,E_pos,Pos,% E_Text
  tmpy:=-E_posh+70
  MoveControl(E_Text,,,,tmpy,0)
  MoveControl(hProgress2,,tmpy,,,0)
  MoveControl(hGrafPos,,tmpy,,-tmpy,0)
  gosub,GrafCreateMenu
  gosub,GrafRedraw
  return

GrafClear:
  ArrClear(Monitor_value)
  ArrClear(Monitor_value_T)
  loop,% MaxNumOfGraf
    ArrClear(Monitor_value%a_index%)
  return

GrafCreateMenu:
  menu,GrafRightClick,DeleteAll
  menu,GrafRightClick,add,Автоматическое максимальное значение,GrafAutoMinMax
  menu,GrafRightClick,add,Задать максимальное значение вручную	F2,GrafChangeMinMax
  menu,GrafRightClick,add
  if EN_Kurvendrucker
  {
    menu,GrafRightClick,add,Автоматически определять разделитель графиков,GrafAutoEnding
    menu,GrafRightClick,add,Сбросить автоопределение разделителя	F5,GrafAutoEndingReset
    menu,GrafRightClick,add,Задать разделитель вручную	F3,GrafChangeEnding
    menu,GrafRightClick,add
    if AutoEnding
      menu,GrafRightClick,Check,Автоматически определять разделитель графиков ;
  }
  menu,GrafRightClick,add,Сглаживание "ступенек"	F4,GrafSmooth
  menu,GrafRightClick,add,Остановить прокрутку	F1,GrafPause
  menu,GrafRightClick,add
  menu,GrafRightClick,add,Очистить график,GrafClear
  menu,GrafRightClick,add
  menu,GrafRightClick,add,Увеличить	Колесико мыши или Ctrl+,ZoomIn
  menu,GrafRightClick,add,Уменьшить	Колесико мыши или Ctrl-,ZoomOut
  if MonitorVar
  {
    menu,GrafRightClick,add
    menu,GrafRightClick,add,Закрыть,GrafClose
  }
  if AutoMinMax
    menu,GrafRightClick,Check,Автоматическое максимальное значение ;
  if GrafSmooth
    menu,GrafRightClick,Check,Сглаживание "ступенек"	F4
  return

HotkeysOn:
  Hotkey,IfWinActive,% "ahk_id " hMainWin
  Hotkey,F1,F1_Label
  Hotkey,F2,F2_Label
  Hotkey,F3,F3_Label
  Hotkey,F4,F4_Label
  Hotkey,F5,F5_Label
  Hotkey,~$LButton,LButton_Label
  Hotkey,~$LButton up,LButton_up_Label
  Hotkey,~$RButton,RButton_Label
  Hotkey,*~$Right,Right_Label
  Hotkey,*~$Left,Left_Label
  Hotkey,^sc00D,sc00D_Label
  Hotkey,~$WheelUp,WheelUp_Label
  Hotkey,^sc00C,sc00C_Label
  Hotkey,~$WheelDown,WheelDown_Label
  Hotkey,^!+#vk4c,vk4c_Label
  Hotkey,Esc,Esc_Label

  Hotkey,IfWinActive,% "ahk_id " hGraf
  Hotkey,F1,F1_Label
  Hotkey,F2,F2_Label
  Hotkey,F3,F3_Label
  Hotkey,F4,F4_Label
  Hotkey,F5,F5_Label
  Hotkey,~$LButton,LButton_Label
  Hotkey,~$LButton up,LButton_up_Label
  Hotkey,~$RButton,RButton_Label
  Hotkey,*~$Right,Right_Label
  Hotkey,*~$Left,Left_Label
  Hotkey,^sc00D,sc00D_Label
  Hotkey,~$WheelUp,WheelUp_Label
  Hotkey,^sc00C,sc00C_Label
  Hotkey,~$WheelDown,WheelDown_Label
  Hotkey,^!+#vk4c,vk4c_Label
  Hotkey,Esc,Esc_Label

  return

HotkeysOff:
  return

F4_Label:
GrafSmooth:
  if GrafSmooth:=!GrafSmooth
    menu,GrafRightClick,Check,Сглаживание "ступенек"	F4
  else
    menu,GrafRightClick,UnCheck,Сглаживание "ступенек"	F4
  if (a_tickcount-StartDrawTime>500)
    gosub,GrafRedraw
  return

GrafAutoMinMax:
  if AutoMinMax:=!AutoMinMax
  {
    hi_lim:=1
    menu,GrafRightClick,Check,Автоматическое максимальное значение ;
  }
  else
    menu,GrafRightClick,UnCheck,Автоматическое максимальное значение ;
  if (a_tickcount-StartDrawTime>500)
    gosub,GrafRedraw
  return

F2_Label:
GrafChangeMinMax:
  InputBox,tmp1,Задать максимальное значение вручную	F2,Введите положительное число,,240,130,,,,12,% hi_lim
  if !ErrorLevel
  {
    menu,GrafRightClick,UnCheck,Автоматическое максимальное значение ;
    AutoMinMax:=0
    hi_lim:=tmp1
  }
  if (a_tickcount-StartDrawTime>500)
    gosub,GrafRedraw
  return

GrafAutoEnding:
  if AutoEnding:=!AutoEnding
  {
    AutoEnding:=1
    EndSymbol:=""
    TryFind:=0
    menu,GrafRightClick,Check,Автоматически определять разделитель графиков ;
  }
  else
    menu,GrafRightClick,UnCheck,Автоматически определять разделитель графиков ;
  return

F5_Label:
if !(EN_Kurvendrucker)
  return
GrafAutoEndingReset:
  EndSymbol:=""
  TryFind:=0
  return

F3_Label:
if !(EN_Kurvendrucker)
  return
GrafChangeEnding:
  InputBox,tmp1,Задать разделитель вручную,Введите разделительный символ,,240,130,,,,12,% EndSymbol
  if !ErrorLevel
  {
    menu,GrafRightClick,UnCheck,Автоматически определять разделитель графиков ;
    AutoEnding:=0
    EndSymbol:=tmp1
  }
  return

F1_Label:
if !(Monitorvalue or EN_Kurvendrucker)
  return
GrafPause:
  if GrafPause:=!GrafPause
  {
    GuiControl,,GrafPause,% GrafPause
    menu,GrafRightClick,Check,Остановить прокрутку	F1 ;
  }
  else
  {
    GuiControl,,GrafPause,% GrafPause
    menu,GrafRightClick,UnCheck,Остановить прокрутку	F1 ;
  }
  return

GrafDraw:
  StartDrawTime:=a_tickcount
GrafRedraw:
  if (Monitor_value_T.maxindex()<Gdip_W)
    Gdip_Points_Num:=Gdip_W
  else
    Gdip_Points_Num:=Monitor_value_T.maxindex()
  tmp0:=((Gdip_H//Gdip_ZoomY)//2)
  if (Gdip_Y_Offset<tmp0) ; верхний край
    Gdip_Y_Offset:=tmp0
  else if (Gdip_Y_Offset>Gdip_H-tmp0) ; нижний край
    Gdip_Y_Offset:=Gdip_H-tmp0
  if (Gdip_X_Offset>Gdip_Points_Num-Gdip_W/Gdip_ZoomX) ; левый край
    Gdip_X_Offset:=Gdip_Points_Num-Gdip_W/Gdip_ZoomX
  if (Gdip_X_Offset<0) ; правый край
    Gdip_X_Offset:=0
  Gdip_X_Offset:=round(Gdip_X_Offset)
  if AutoMinMax
  {
    if EN_Kurvendrucker
    {
      hi_lim:=0
      loop,% NumOfGraf
      {
        tmp1:=Ceil(ArrGetMax(Monitor_value%a_index%,Gdip_X_Offset,round(Gdip_W/Gdip_ZoomX)))
        if (hi_lim<tmp1)
          hi_lim:=tmp1
      }
    }
    else
      hi_lim:=Ceil(ArrGetMax(Monitor_value,Gdip_X_Offset,round(Gdip_W/Gdip_ZoomX)))
  }

  GuiControl,,Progress2,% round(100-Gdip_X_Offset/(Gdip_Points_Num-Gdip_W/Gdip_ZoomX)*100)
  ; очистить поле
  Gdip_FillRectangle(Main_Holst_G,Gdip_WhiteBrush,0,0,Gdip_Points_Num,Gdip_H)
  ; отрисовать сетку
  Gdip_DrawLine(Main_Holst_G,Gdip_GrayPen,0,Gdip_M
			  ,Gdip_Points_Num,Gdip_M)
  ; Отрисовать горизонтальные линии величин с текстом
  Grad:=10**NumOfSymbols:=(strlen(round(hi_lim))-1)
  GdipCoordX:=Gdip_Font_W ;Gdip_Points_Num-Gdip_Font_W*(NumOfSymbols+1)-10
  Gdip_Text_W:=Gdip_Font_W*NumOfSymbols
  loop,% round(hi_lim)//Grad
  {
    Val:=A_index*Grad
    PlussCoordY:=abs(round((Val/hi_lim)*Gdip_M)+Gdip_M-Gdip_H)
    MinusCoordY:=abs(round((-Val/hi_lim)*Gdip_M)+Gdip_M-Gdip_H)
    Gdip_FillRectangle(Main_Holst_G,Gdip_WhiteBrush2,GdipCoordX,PlussCoordY+1,Gdip_Text_W,Gdip_Font_H)
    Gdip_FillRectangle(Main_Holst_G,Gdip_WhiteBrush2,GdipCoordX,MinusCoordY-Gdip_Font_H,Gdip_Text_W,Gdip_Font_H)
    Gdip_TextToGraphics(Main_Holst_G,Val,"x" GdipCoordX " y" PlussCoordY+1 " Left cff000000 " Gdip_Font_Size,Gdip_Font)
    Gdip_TextToGraphics(Main_Holst_G,-Val,"x" GdipCoordX-10 " y" MinusCoordY-Gdip_Font_H " Left cff000000 " Gdip_Font_Size,Gdip_Font)
    Gdip_DrawLine(Main_Holst_G,Gdip_GrayPen2,0,PlussCoordY
			     ,Gdip_Points_Num,PlussCoordY)
    Gdip_DrawLine(Main_Holst_G,Gdip_GrayPen2,0,MinusCoordY
			     ,Gdip_Points_Num,MinusCoordY)
  }
; линия нуля
  Gdip_FillRectangle(Main_Holst_G,Gdip_WhiteBrush2,GdipCoordX,Gdip_M,Gdip_Font_W,Gdip_Font_H)
  Gdip_TextToGraphics(Main_Holst_G,0,"x" GdipCoordX " y" Gdip_M " Left cff000000 " Gdip_Font_Size,Gdip_Font)

; отрисовать синий график
  if MonitorVar
  {
    Gdip_Time_Stamp:=0
    Old_j:=0
    Smooth_GdipCoordX:=0
    Old_GdipCoordY:=abs(round((Monitorvalue/hi_lim)*(Gdip_M))+Gdip_M-Gdip_H) ;Gdip_M
    Old_Time:=Average_time_summ:=Average_time_Devider:=0
    Val_UnderGraf_Drawed:=0
    loop,% round(Gdip_W/Gdip_ZoomX)
    {
      i:=a_index
      j:=Monitor_value[i+Gdip_X_Offset]
      ; out=(inval/max)*(hi_lim-lo_lim)+lo_lim
      GdipCoordX:=round(Gdip_W-(i*Gdip_ZoomX))
      if (abs(j)<=abs(hi_lim))
        GdipCoordY:=abs(round((j/hi_lim)*(Gdip_M))+Gdip_M-Gdip_H)
      else if (j>0)
        GdipCoordY:=0
      else if (j<0)
        GdipCoordY:=Gdip_H
      if tmp0:=Monitor_value_T[i+Gdip_X_Offset]
      {
        RegExMatch(tmp0,"([\d]+).([\d]+).([\d]+).([\d]+)",tmp)
        Calc_Time:=(tmp1*3600+tmp2*60+tmp3)*1000+tmp4
        if Old_Time
        {
          Delta_Time:=Old_Time-Calc_Time
          Average_time_Devider++
          Average_time_summ:=Average_time_summ+Delta_Time
; отметить точки с задержкой
;          if (Average_time and Delta_Time>Average_time and Old_GdipCoordX!=GdipCoordX)
;            Gdip_DrawLine(Main_Holst_G,Gdip_GrayPen3,GdipCoordX+1,0
;				      ,GdipCoordX+1,Gdip_H)
        }
        Old_Time:=Calc_Time
      }
      if (!GrafSmooth or (Old_j!=j))
      {
; линия синего графика
        Gdip_DrawLine(Main_Holst_G,Gdip_BluePen,round(GdipCoordX+Smooth_GdipCoordX+Gdip_ZoomX),Old_GdipCoordY,GdipCoordX,GdipCoordY)
        if (Gdip_ZoomX>7)
          Gdip_DrawEllipse(Main_Holst_G,Gdip_BluePen,GdipCoordX-1,GdipCoordY-1,2,2)
        Old_GdipCoordY:=GdipCoordY
        Old_j:=j
        Smooth_GdipCoordX:=0
      }
      else
        Smooth_GdipCoordX+=1*Gdip_ZoomX
      Gdip_Time_Stamp+=Gdip_ZoomX
      if (Gdip_Time_Stamp>10*Gdip_Font_W)
      {
        Gdip_Time_Stamp:=0
; линия и текст временой шкалы
        Gdip_DrawLine(Main_Holst_G,Gdip_GrayPen2,GdipCoordX+1,0
				  ,GdipCoordX+1,Gdip_H)
        tmp0:=Monitor_value_T[i+Gdip_X_Offset]
        if tmp0
        {
          StringTrimRight,tmp0,tmp0,4
          Gdip_FillRectangle(Main_Holst_G,Gdip_WhiteBrush2,GdipCoordX+1,Gdip_H-Gdip_Font_H
      						  ,Gdip_Font_W*(strlen(tmp0)),Gdip_Font_H)
          Gdip_TextToGraphics(Main_Holst_G,tmp0,"x" GdipCoordX+1 " y" Gdip_H-Gdip_Font_H " Left cff000000 " Gdip_Font_Size,Gdip_Font)
        }
      }
; текст значения под курсором
      if (!Val_UnderGraf_Drawed)
      if (UnderGraf_mx>=GdipCoordX and UnderGraf_mx<=GdipCoordX+10)
      if (UnderGraf_my>GdipCoordY-10 and UnderGraf_my<GdipCoordY+10)
      {
        Val_UnderGraf_Drawed:=1
        tmp1:=(GdipCoordY-Gdip_Font_H)<0?0:(GdipCoordY-Gdip_Font_H)
        Gdip_FillRectangle(Main_Holst_G,Gdip_WhiteBrush2,GdipCoordX,tmp1,Gdip_Font_W*(strlen(j)),Gdip_Font_H)
        Gdip_TextToGraphics(Main_Holst_G,j,"x" GdipCoordX " y" tmp1 " Left c" Gdip_Font_Selected_Colour " " Gdip_Font_Size,Gdip_Font)
      }
      Old_GdipCoordX:=GdipCoordX
    }
    Average_time:=Average_time_summ//Average_time_Devider*2

    if (Monitorvalue>=0)
      addY:=0
    else
      addY:=-Gdip_Font_H
    Old_GdipCoordY:=abs(round((Monitorvalue/hi_lim)*(Gdip_M))+Gdip_M-Gdip_H) ;Gdip_M
    GdipCoordX:=Gdip_W-Gdip_Font_W*(strlen(Monitorvalue))
; текст текущего значения
    Gdip_FillRectangle(Main_Holst_G,Gdip_WhiteBrush2,GdipCoordX,Old_GdipCoordY+1+addY,Gdip_Font_W*(strlen(Monitorvalue)),Gdip_Font_H)
    Gdip_TextToGraphics(Main_Holst_G,Monitorvalue,"x" GdipCoordX " y" Old_GdipCoordY+1+addY " Left cff000000 " Gdip_Font_Size,Gdip_Font)

    if (Gdip_Marker_mx>0 and Gdip_Marker_mx<=Gdip_W and Gdip_Marker_my>0 and Gdip_Marker_my<=Gdip_H)
    {
      j:=Monitor_value[round((Gdip_W-Gdip_Marker_mx)/Gdip_ZoomX+Gdip_X_Offset)]
      if (j>=0)
        addY:=0
      else
        addY:=-Gdip_Font_H
      if (abs(j)<=abs(hi_lim))
        GdipCoordY:=abs(round((j/hi_lim)*(Gdip_M))+Gdip_M-Gdip_H)
      else if (j>0)
        GdipCoordY:=0
      else if (j<0)
        GdipCoordY:=Gdip_H
; время значения выбранного мышкой
      t:=Monitor_value_T[round((Gdip_W-Gdip_Marker_mx)/Gdip_ZoomX+Gdip_X_Offset)]
      Gdip_FillRectangle(Main_Holst_G,Gdip_WhiteBrush2,Gdip_Marker_mx,1,Gdip_Font_W*(strlen(t)),Gdip_Font_H)
      Gdip_TextToGraphics(Main_Holst_G,t,"x" round(Gdip_Marker_mx) " y" 1 " Left c" Gdip_Font_Selected_Colour " " Gdip_Font_Size,Gdip_Font)
; линия и значение выбранное мышкой
      Gdip_DrawLine(Main_Holst_G,Gdip_GrayPen,Gdip_Marker_mx,0,Gdip_Marker_mx,Gdip_H)
      tmp1:=GdipCoordY+addY<Gdip_Font_H?Gdip_Font_H:GdipCoordY+addY
      Gdip_FillRectangle(Main_Holst_G,Gdip_WhiteBrush2,Gdip_Marker_mx,tmp1,Gdip_Font_W*(strlen(j)),Gdip_Font_H)
      Gdip_TextToGraphics(Main_Holst_G,j,"x" round(Gdip_Marker_mx) " y" tmp1 " Left c" Gdip_Font_Selected_Colour " " Gdip_Font_Size,Gdip_Font)
    }
  }
; отрисовать остальные графики
  if EN_Kurvendrucker
  {
    loop,% NumOfGraf
    {
      CurGN:=a_index
      if !EnableGraf%CurGN%
        continue
      Old_j:=0
      Smooth_GdipCoordX:=0
      Old_GdipCoordY:=abs(round((Monitorvalue/hi_lim)*(Gdip_M))+Gdip_M-Gdip_H) ;Gdip_M
      Val_UnderGraf_Drawed:=0
      loop,% round(Gdip_W/Gdip_ZoomX)
      {
        i:=a_index
        j:=Monitor_value%CurGN%[i+Gdip_X_Offset]
        ; out=(inval/max)*(hi_lim-lo_lim)+lo_lim
        GdipCoordX:=round(Gdip_W-(i*Gdip_ZoomX))
        if (abs(j)<=abs(hi_lim))
          GdipCoordY:=abs(round((j/hi_lim)*(Gdip_M))+Gdip_M-Gdip_H)
        else if (j>0)
          GdipCoordY:=0
        else if (j<0)
          GdipCoordY:=Gdip_H
        if (!GrafSmooth or (Old_j!=j))
        {
  ; линия остальных графиков
          Gdip_DrawLine(Main_Holst_G,Gdip_Pen%CurGN%,round(GdipCoordX+Smooth_GdipCoordX+Gdip_ZoomX),Old_GdipCoordY,GdipCoordX,GdipCoordY)
          if (Gdip_ZoomX>7)
            Gdip_DrawEllipse(Main_Holst_G,Gdip_Pen%CurGN%,GdipCoordX-2,GdipCoordY-2,4,4)
          Old_GdipCoordY:=GdipCoordY
          Old_j:=j
          Smooth_GdipCoordX:=0
        }
        else
          Smooth_GdipCoordX+=1*Gdip_ZoomX
; текст значения под курсором
        if (!Val_UnderGraf_Drawed)
        if (UnderGraf_mx>=GdipCoordX and UnderGraf_mx<=GdipCoordX+10)
        if (UnderGraf_my>GdipCoordY-10 and UnderGraf_my<GdipCoordY+10)
        {
          Val_UnderGraf_Drawed:=1
          tmp1:=(GdipCoordY-Gdip_Font_H)<0?0:(GdipCoordY-Gdip_Font_H)
          Gdip_FillRectangle(Main_Holst_G,Gdip_Brush%CurGN%,GdipCoordX,tmp1,Gdip_Font_W*(strlen(j)),Gdip_Font_H)
          Gdip_TextToGraphics(Main_Holst_G,j,"x" GdipCoordX " y" tmp1 " Left c" Gdip_Font_Selected_Colour " " Gdip_Font_Size,Gdip_Font)
        }
      }
      if (Monitorvalue>=0)
        addY:=0
      else
        addY:=-Gdip_Font_H
      j:=Monitor_value%CurGN%[Gdip_X_Offset]
      Old_GdipCoordY:=abs(round((j/hi_lim)*(Gdip_M))+Gdip_M-Gdip_H) ;Gdip_M
      GdipCoordX:=Gdip_W-Gdip_Font_W*(strlen(j))
  ; текст текущего значения
;      Gdip_FillRectangle(Main_Holst_G,Gdip_WhiteBrush2,GdipCoordX,Old_GdipCoordY+1+addY,Gdip_Font_W*(strlen(j)),Gdip_Font_H)
;      Gdip_TextToGraphics(Main_Holst_G,j,"x" GdipCoordX " y" Old_GdipCoordY+1+addY " Left cff000000 " Gdip_Font_Size,Gdip_Font)

      if (Gdip_Marker_mx>0 and Gdip_Marker_mx<=Gdip_W and Gdip_Marker_my>0 and Gdip_Marker_my<=Gdip_H)
      {
        j:=Monitor_value%CurGN%[round((Gdip_W-Gdip_Marker_mx)/Gdip_ZoomX+Gdip_X_Offset)]
        if (j>=0)
          addY:=0
        else
          addY:=-Gdip_Font_H
        if (abs(j)<=abs(hi_lim))
          GdipCoordY:=abs(round((j/hi_lim)*(Gdip_M))+Gdip_M-Gdip_H)
        else if (j>0)
          GdipCoordY:=0
        else if (j<0)
          GdipCoordY:=Gdip_H
; время значения выбранного мышкой
        t:=Monitor_value_T[round((Gdip_W-Gdip_Marker_mx)/Gdip_ZoomX+Gdip_X_Offset)]
        Gdip_FillRectangle(Main_Holst_G,Gdip_WhiteBrush2,Gdip_Marker_mx,1,Gdip_Font_W*(strlen(t)),Gdip_Font_H)
        Gdip_TextToGraphics(Main_Holst_G,t,"x" round(Gdip_Marker_mx) " y" 1 " Left c" Gdip_Font_Selected_Colour " " Gdip_Font_Size,Gdip_Font)
  ; линия и значение выбранное мышкой
        if (j!="")
          tmp1:=CurGN "=" j
        else
          tmp1:=""
        Gdip_DrawLine(Main_Holst_G,Gdip_GrayPen,Gdip_Marker_mx,0,Gdip_Marker_mx,Gdip_H)
        Gdip_FillRectangle(Main_Holst_G,Gdip_Brush%CurGN%,Gdip_Marker_mx,Gdip_Font_H*CurGN,Gdip_Font_W*(strlen(tmp1)),Gdip_Font_H)
        Gdip_TextToGraphics(Main_Holst_G,tmp1,"x" round(Gdip_Marker_mx) " y" Gdip_Font_H*CurGN " Left c" Gdip_Font_Selected_Colour " " Gdip_Font_Size,Gdip_Font)
      }
    }
      Gdip_Time_Stamp:=0
      loop,% round(Gdip_W/Gdip_ZoomX)
      {
        i:=a_index
        Gdip_Time_Stamp+=Gdip_ZoomX
        if (Gdip_Time_Stamp>10*Gdip_Font_W)
        {
          GdipCoordX:=round(Gdip_W-(i*Gdip_ZoomX))
          Gdip_Time_Stamp:=0
          Gdip_DrawLine(Main_Holst_G,Gdip_GrayPen2,GdipCoordX+1,0
  				  ,GdipCoordX+1,Gdip_H)
          tmp0:=Monitor_value_T[i+Gdip_X_Offset]
          if tmp0
          {
  ; текст временой шкалы
            StringTrimRight,tmp0,tmp0,4
            Gdip_FillRectangle(Main_Holst_G,Gdip_WhiteBrush2,GdipCoordX+1,Gdip_H-Gdip_Font_H
        						  ,Gdip_Font_W*(strlen(tmp0)),Gdip_Font_H)
            Gdip_TextToGraphics(Main_Holst_G,tmp0,"x" GdipCoordX+1 " y" Gdip_H-Gdip_Font_H " Left cff000000 " Gdip_Font_Size,Gdip_Font)
          }
        }
      }
  }

GrafUpdate:
  ; Обновить отображение
  ; Скопировать отрисованный график
  if (a_osversion~="10")
    GuiControlGet,Gdip_,Pos,% hGrafPos
  else
    wingetpos,Gdip_X,Gdip_Y,Gdip_W,Gdip_H,% "ahk_id" hGrafPos
  Gdip_M:=Gdip_H//2
  if (MonitorVar or EN_Kurvendrucker)
    UpdateLayeredWindow(hGraf,Main_Holst,Gdip_X,Gdip_Y,Gdip_W,Gdip_H+1)
  else
    UpdateLayeredWindow(hGraf,Main_Holst,Gdip_X,Gdip_Y,Gdip_W,Gdip_H+1,0)
  DrawTime:=a_tickcount-StartDrawTime
;tooltip,% DrawTime "`n" Monitor_value_T.maxindex(),500,0,9
  UnderGraf_MouseMove:=0
  return

GuiSize:
  if (!Old_GuiWidth or !Old_GuiHeight)
  {
    Old_GuiWidth:=A_GuiWidth
    Old_GuiHeight:=A_GuiHeight
  }
  if (A_EventInfo=1)
    return
  DeltaGuiWidth:=A_GuiWidth-Old_GuiWidth
  DeltaGuiHeight:=A_GuiHeight-Old_GuiHeight
  if (MonitorVar or EN_Kurvendrucker)
  {
    hGrafPos_over:=MoveControl(hGrafPos,,,DeltaGuiWidth,DeltaGuiHeight)
    E_Text_over:=MoveControl(E_Text,,,DeltaGuiWidth,((hGrafPos_over&2)?DeltaGuiHeight:0))
    hProgress2_over:=MoveControl(hProgress2,,((hGrafPos_over&2)?DeltaGuiHeight:0),DeltaGuiWidth)
  }
  else
  {
    E_Text_over:=MoveControl(E_Text,,,DeltaGuiWidth,DeltaGuiHeight)
    hProgress2_over:=MoveControl(hProgress2,,DeltaGuiHeight,DeltaGuiWidth)
    hGrafPos_over:=MoveControl(hGrafPos,,DeltaGuiHeight,DeltaGuiWidth)
  }
  MoveControl(hGroupBufer,,,DeltaGuiWidth,DeltaGuiHeight)
  MoveControl(hClButton,,DeltaGuiHeight,DeltaGuiWidth)
  MoveControl(hTtEButton,,DeltaGuiHeight)
  MoveControl(E_LV,,,((E_Text_over&1)?DeltaGuiWidth:0),DeltaGuiHeight)
  MoveControl(hProgress,((E_Text_over&1)?DeltaGuiWidth:0),,,DeltaGuiHeight)
  loop,% MaxNumOfGraf+1
  {
    i:=a_index-1
    MoveControl(hECB%i%,,DeltaGuiHeight)
  }
;tooltip,% "dw" DeltaGuiWidth "`ndh" DeltaGuiHeight "`nw" A_GuiWidth "`nh" A_GuiHeight
  Old_GuiWidth:=A_GuiWidth
  Old_GuiHeight:=A_GuiHeight
  return

___________Hotkeys:
return

LButton_Label:
  mousegetpos,mx,my,mw,mc
  oldmx:=mx
  oldmy:=my
  oldmc:=mc
  oldmw:=mw
  if (mw=hGraf)
  {
    Gdip_Marker_mx:=mx-Gdip_X
    Gdip_Marker_my:=my-Gdip_Y
  }
  if (MouseAbovehMain)
  {
    ControlGet,hElement,hwnd,,%mc%,ahk_id %mw%
    if (hElement=hProgress)
      EditResize:=1
    if (hElement=hProgress2)
      EditResize:=2
    if (hElement=E_LV or hElement=E_LV2)
      ControlFocus,,ahk_id %hElement%
  }
  return

LButton_up_Label:
  EndResizing:
  EditResize:=0
  if (a_tickcount-StartDrawTime>500)
    gosub,GrafRedraw
  return

RButton_Label:
  mousegetpos,mx,my,mw,mc
  ControlGet,hElement,hwnd,,%mc%,ahk_id %mw%
  if (mw=hGraf)
    menu,GrafRightClick,show
  if (hElement=hBufinfo)
    menu,ListRightClick2,show
  if (hElement=hProgress2)
  {
    if (a_osversion~="10")
      GuiControlGet,E_pos,Pos,% hProgress2
    else
      wingetpos,E_posX,E_posY,E_posW,E_posH,% "ahk_id" hProgress2
    ClickPos:=(E_posw-(mx-E_posx))/E_posw*100
    tmp1:=round(Gdip_Points_Num-Gdip_W/Gdip_ZoomX)
    Gdip_X_Offset:=ClickPos*tmp1/100
    if (a_tickcount-StartDrawTime>500)
      gosub,GrafRedraw
  }
  loop,% MaxNumOfGraf
  {
    i:=a_index
    if (hElement=hECB%i%)
    {
      if (EnableGraf%i%)
      {
        loop,% MaxNumOfGraf
          if (a_index!=i)
            GuiControl,,EnableGraf%a_index%,0
      }
      else
      {
        loop,% MaxNumOfGraf
          GuiControl,,EnableGraf%a_index%,1
      }
      gosub,Submit2
      break
    }
  }
  return


Right_Label:
  if WinActive("Ahk_id " hGraf)
  {
    if getkeystate("Ctrl","P")
      Gdip_X_Offset-=20
    Gdip_Marker_mx+=20
    if (a_tickcount-StartDrawTime>500)
      gosub,GrafRedraw
  }
  return

Left_Label:
  if WinActive("Ahk_id " hGraf)
  {
    if getkeystate("Ctrl","P")
      Gdip_X_Offset+=5
    Gdip_Marker_mx-=5
    if (a_tickcount-StartDrawTime>500)
      gosub,GrafRedraw
  }
  return

ZoomIn:
sc00D_Label:
WheelUp_Label:
  if !((MouseAbovehGraf and (MonitorVar or EN_Kurvendrucker)))
    return
  if (Gdip_ZoomX>=1)
    Gdip_ZoomX+=1
  else
    Gdip_ZoomX+=0.1
  if (Gdip_ZoomX>10)
    Gdip_ZoomX:=10
  Gdip_ZoomY+=1
  if (Gdip_ZoomY>10)
    Gdip_ZoomY:=10
  if (a_tickcount-StartDrawTime>500)
    gosub,GrafRedraw
  return

ZoomOut:
sc00C_Label:
WheelDown_Label:
  if !((MouseAbovehGraf and (MonitorVar or EN_Kurvendrucker)))
    return
  if (Gdip_ZoomX>1)
    Gdip_ZoomX-=1
  else
    Gdip_ZoomX-=0.1
  if (Gdip_ZoomX<0.1)
    Gdip_ZoomX:=0.1
  Gdip_ZoomY-=1
  if (Gdip_ZoomY<1)
    Gdip_ZoomY:=1
  if (a_tickcount-StartDrawTime>500)
    gosub,GrafRedraw
  return

CheckPIDExist(PID)
{
  process,exist,% PID
  if !errorlevel
    gosub,Exit
}

Esc_Label:
EXIT:
guiclose:
  DllCall("SystemParametersInfo","UInt",0x57,"UInt",0,Ptr,"","UInt",0)
  DC_ARROW := DllCall("CopyImage",Ptr,IDC_ARROW,"UInt",0x2,"Int",0,"Int",0,"UInt",0)
  DllCall("SetSystemCursor",Ptr,DC_ARROW,"UInt",32512)
  if Gdip_Initialised
  {
    Gdip_DeletePen(Gdip_WhitePen)
    Gdip_DeletePen(Gdip_GrayPen)
    Gdip_DeletePen(Gdip_GrayPen2)
    Gdip_DeletePen(Gdip_BluePen)
    Gdip_DeletePen(Gdip_BlackPen)
    loop,% MaxNumOfGraf
      Gdip_DeletePen(Gdip_Pen%a_index%)
    Gdip_DeleteBrush(Gdip_WhiteBrush)
    Gdip_DeleteBrush(Gdip_WhiteBrush2)
    Gdip_DeleteBrush(Gdip_BlackBrush)
    SelectObject(Main_Holst,Gdip_obm2), DeleteObject(Gdip_hbm2), DeleteDC(Main_Holst), Gdip_DeleteGraphics(Main_Holst_G)
    Gdip_Shutdown(Gdip_Initialised)
  }
  RS232_Close(RS232_FileHandle)
  exitapp

; ////////// отработанные линии________ctrl+Shift+win+alt+L ____________________HOTKEYS_________________________
vk4c_Label:
  if !((WinActive("ahk_id " hMainWin) or WinActive("ahk_id " hGraf)))
    return
  ListLines
  return
#if

GetPointer(wParam,lParam)
{
  global
  slPointer:=lParam
}

WM_MOVING()
{
  global
  if 2ndThread
    return
  if (MonitorVar or EN_Kurvendrucker)
    gosub,GrafUpdate
}

WM_MOUSEMOVE:
  mousegetpos,mx,my,mw,mc
  if (mx!=oldmx or my!=oldmy)
  {
    Deltamx:=mx-oldmx
    Deltamy:=my-oldmy
    wingetpos,Gdip_X_,Gdip_Y_,Gdip_W_,Gdip_H_,% "ahk_id" hGrafPos
    UnderGraf_mx:=mx-Gdip_X_
    UnderGraf_my:=my-Gdip_Y_
    UnderGraf_MouseMove:=1
    MouseAbovehGraf:=(mw=hGraf)
    MouseAbovehMain:=(mw=hMainWin)
    if (MouseAbovehMain and oldmc!=mc)
    {
      ControlGet,hElement,hwnd,,%mc%,ahk_id %mw%
      SB_SetText(E%hElement%)
    }
    if (MouseAbovehMain or MouseAbovehGraf)
    {
      ControlGet,hElement,hwnd,,%mc%,ahk_id %mw%
      if (!ChangeCursorOnce and hElement=hProgress)
      {
        ChangeCursorOnce=1
        DllCall("SystemParametersInfo","UInt",0x57,"UInt",0,"UInt","","UInt",0)
        DllCall("SetSystemCursor",Ptr,IDC_SIZEWE,"UInt",32512)
      }
      if (!ChangeCursorOnce2 and hElement=hProgress2)
      {
        ChangeCursorOnce2=1
        DllCall("SystemParametersInfo","UInt",0x57,"UInt",0,"UInt","","UInt",0)
        DllCall("SetSystemCursor",Ptr,IDC_SIZEALL,"UInt",32512)
      }
   }
    if (ChangeCursorOnce or ChangeCursorOnce2) and ((hElement!=hProgress and hElement!=hProgress2) or !MouseAbovehMain)
    {
      ChangeCursorOnce=0
      ChangeCursorOnce2=0
      DllCall("SystemParametersInfo","UInt",0x57,"UInt",0,Ptr,"","UInt",0)
      DC_ARROW := DllCall("CopyImage",Ptr,IDC_ARROW,"UInt",0x2,"Int",0,"Int",0,"UInt",0)
      DllCall("SetSystemCursor",Ptr,DC_ARROW,"UInt",32512)
    }
    if (EditResize)
    {
      GuiControlGet,E_pos,Pos,% E_LV
      GuiControlGet,T_pos,Pos,% E_Text
      if !((E_posw+Deltamx)<10 or (T_posw-Deltamx)<10)
      {
        MoveControl(E_LV,,,Deltamx,,0)
        MoveControl(hProgress,Deltamx,,,,0)
        MoveControl(hProgress2,Deltamx,,-Deltamx,,0)
        MoveControl(E_Text,Deltamx,,-Deltamx,,0)
        MoveControl(hGrafPos,Deltamx,,-Deltamx,,0)
        if (Deltamx>0)
          Gdip_X_Offset-=Deltamx*2
        gosub,GrafUpdate
      }
    }
    if (EditResize=2)
    {
      GuiControlGet,E_pos,Pos,% E_Text
      GuiControlGet,T_pos,Pos,% hGrafPos
      if !((E_posh+Deltamy)<10 or (T_posh-Deltamy)<10)
      {
        MoveControl(E_Text,,,,Deltamy,0)
        MoveControl(hProgress2,,Deltamy,,,0)
        MoveControl(hGrafPos,,Deltamy,,-Deltamy,0)
        gosub,GrafUpdate
      }
    }

    Deltamx:=(mx-oldmx)*((3-Gdip_ZoomX<1) ? 1 : 3-Gdip_ZoomX)
    Deltamy:=(my-oldmy)*((3-Gdip_ZoomY<1) ? 1 : 3-Gdip_ZoomY)
    if (MouseAbovehGraf and getkeystate("LButton","P"))
    {
      Gdip_Y_Offset-=Deltamy
      Gdip_X_Offset+=Deltamx
      Gdip_Marker_mx+=Deltamx
      if (a_tickcount-StartDrawTime>500)
        gosub,GrafRedraw
    }
    if (MouseAbovehGraf and (oldmx!=mx or oldmy!=my) and a_tickcount-StartDrawTime>500)
    {
      SB_SetText("Дополнительное меню правой кнопкой мыши. Масштаб = " round(Gdip_ZoomX,1) ". Максимальное значение = " hi_lim ". Время отрисовки кадра = " DrawTime "ms. Чило точек = " Monitor_value_T.maxindex() ".")
      gosub,GrafRedraw
    }
  }
  oldmx:=mx
  oldmy:=my
  oldmc:=mc
  oldmw:=mw
  if (MouseAbovehGraf and (MonitorVar or EN_Kurvendrucker))
    SB_SetText("Дополнительное меню правой кнопкой мыши. Масштаб = " round(Gdip_ZoomX,1) ". Максимальное значение = " hi_lim ". Время отрисовки кадра = " DrawTime "ms. Чило точек = " Monitor_value_T.maxindex() ".")
  return

GetClientPos(hwnd)
{
  VarSetCapacity(pwi, 60, 0), NumPut(60, pwi, 0, "UInt")
  DllCall("GetWindowInfo", Ptr, hwnd, "UInt", &pwi)
  top := NumGet(pwi, 24, "Int") - NumGet(pwi, 8, "Int")
  left := NumGet(pwi, 52, "Int")
  w := NumGet(pwi, 28, "Int") - NumGet(pwi, 20, "Int")
  h := NumGet(pwi, 32, "Int") - NumGet(pwi, 24, "Int")
  return {"top":top,"left":left,"w":w,"h":h}
}

SendToTerminal(RS232_FileHandle,Addres=1,command=1,Dig0=0,Dig1=0,Dig2=0,Dig3=0,Dig4=0,Dig5=0)
{
  varsetcapacity(Out_Data,9)
  NumPut(Addres,Out_Data,0,"Uchar")
  NumPut(command,Out_Data,1,"Uchar")
  NumPut(Dig0,Out_Data,2,"Uchar")
  NumPut(Dig1,Out_Data,3,"Uchar")
  NumPut(Dig2,Out_Data,4,"Uchar")
  NumPut(Dig3,Out_Data,5,"Uchar")
  NumPut(Dig4,Out_Data,6,"Uchar")
  NumPut(Dig5,Out_Data,7,"Uchar")
  NumPut(CRC16(Out_Data,8),Out_Data,8,"Uchar")
  RS232_Write(RS232_FileHandle,&Out_Data,9)
}
CRC16(byref data,size)
{
  if isobject(data)
  {
    loop,% size
      sum+=data[a_index]
    return sum
  }
  else
  {
    loop,% size
      sum+=numget(data,a_index-1,"uchar")
    return sum
  }
}
CRC_RTU(byref data,size)
{
  if isobject(data)
  {
    y:=0xFFFF
    loop,% size
    {
      z:=y^data[a_index]
      loop,8
      {
        r:=z&1
        z>>=1
        if r
          z^=0xA001
      }
      y:=z
    }
    a:=z//256
    b:=mod(z,256)
    return (a<<8)|b
  }
  else
  {
    y:=0xFFFF
    loop,% size
    {
      z:=y^numget(data,a_index-1,"uchar")
      loop,8
      {
        r:=z&1
        z>>=1
        if r
          z^=0xA001
      }
      y:=z
    }
    a:=z//256
    b:=mod(z,256)
    return (a<<8)|b
  }
  ;1) y=65535;
  ;2) загрузить в переменную x первый байт из потока данных;
  ;3) вычислить переменную z = ИСКЛЮЧАЮЩЕЕ ИЛИ y c байтом потока данных x.
  ;4) сдвинуть переменную z на один бит вправо
  ;5) если сдвинутый бит равен 1 то вычислить переменную z:
  ;z= ИСКЛЮЧАЮЩЕЕ ИЛИ z с числом 0xA001
  ;6) повторять шаг 4 и шаг 5 еще 7 раз.
  ;7) y=z
  ;8) если все байты из потока данных обработаны перейти на шаг 10
  ;9) перейти на шаг 2
  ;10) вычислить первый и второй байты CRC16-кода a и b.
  ;a=z/256(нацело)
  ;b= остаток от деления z/256.
}

SwapByte(var)
{
  tmp1:=(var<<8)&0xff00
  tmp2:=(var>>8)
  return (tmp1|tmp2)
}

SearchCOM(name,list=0)
{
  Loop,HKLM,HARDWARE\DEVICEMAP\SERIALCOMM\
  {
    RegRead,COMPort
    FriendlyName:=""
    Loop,HKLM,SYSTEM\CurrentControlSet\Enum,1,1
    {
      if (A_LoopRegName="PortName")
      {
        RegRead,Outputvar
        if (Outputvar=COMPort)
        {
          RegRead,FriendlyName,% A_LoopRegKey,% RegExReplace(A_LoopRegSubKey, "(.*)\\Device Parameters", "$1"),FriendlyName
          if (!list and InStr(FriendlyName,name))
            return COMPort
        }
      }
    }
    if (FriendlyName="")
      FriendlyName:=A_LoopRegName
    if list
      out.=COMPort "`t" FriendlyName "`n"
    else if (InStr(A_LoopRegName,name))
      return COMPort
  }
  if list
    return out
}

RS232_Get_FileHandle(RS232_Port="COM1",RS232_Baud=9600,RS232_Parity="N",RS232_Data=8,RS232_Stop=1,ReadIntervalTimeout="")
{
; COMx[:][baud=b][parity=p][data=d][stop=s][to={on|off}][xon={on|off}][odsr={on|off}][octs={on|off}][dtr={on|off|hs}][rts={on|off|hs|tg}][idsr={on|off}]
  RS232_Settings=%RS232_Port%:baud=%RS232_Baud% parity=%RS232_Parity% data=%RS232_Data% stop=%RS232_Stop% to=off xon=off odsr=off octs=off dtr=Off rts=off idsr=off

  ;###### Extract/Format the RS232 COM Port Number ######
  ;7/23/08 Thanks krisky68 for finding/solving the bug in which RS232 COM Ports greater than 9 didn't work.
  StringSplit, RS232_Temp, RS232_Settings, `:
  RS232_Temp1_Len := StrLen(RS232_Temp1)  ;For COM Ports > 9 \\.\ needs to prepended to the COM Port name.
  If (RS232_Temp1_Len > 4)                ;So the valid names are
    RS232_COM = \\.\%RS232_Temp1%         ; ... COM8  COM9   \\.\COM10  \\.\COM11  \\.\COM12 and so on...
  Else
    RS232_COM = %RS232_Temp1%

  ;8/10/09 A BIG Thanks to trenton_xavier for figuring out how to make COM Ports greater than 9 work for USB-Serial Dongles.
  StringTrimLeft, RS232_Settings, RS232_Settings, RS232_Temp1_Len+1 ;Remove the COM number (+1 for the semicolon) for BuildCommDCB.
  ;MsgBox, RS232_COM=%RS232_COM% `nRS232_Settings=%RS232_Settings%

  ;###### Build RS232 COM DCB ######
  ;Creates the structure that contains the RS232 COM Port number, baud rate,...
  VarSetCapacity(DCB, 28, 0)
  BCD_Result := DllCall("BuildCommDCB"
       ,"str" , RS232_Settings ;lpDef
       ,Ptr, &DCB)        ;lpDCB
;  If (!BCD_Result)
;  {
;    MsgBox, There is a problem with Serial Port communication. `nFailed Dll BuildCommDCB, BCD_Result=%BCD_Result% `nThe Script Will Now COMFail.`n %DCB%
;    COMFail=1
;    return
;  }

  ;###### Create RS232 COM File ######
  ;Creates the RS232 COM Port File Handle
  RS232_FileHandle := DllCall("CreateFile"
       ,"Str" , RS232_COM     ;File Name
       ,"UInt", 0xC0000000   ;Desired Access
       ,"UInt", 3            ;Safe Mode
       ,"UInt", 0            ;Security Attributes
       ,"UInt", 3            ;Creation Disposition
       ,"UInt", 0            ;Flags And Attributes
       ,Ptr, 0            ;Template File
       ,"Cdecl Int")

  If (!RS232_FileHandle)
  {
;;    MsgBox, There is a problem with Serial Port communication. `nFailed Dll CreateFile, RS232_FileHandle=%RS232_FileHandle% `nThe Script Will Now COMFail.
    COMFail=1
    return
  }

  ;###### Set COM State ######
  ;Sets the RS232 COM Port number, baud rate,...
  SCS_Result := DllCall("SetCommState"
       ,Ptr, RS232_FileHandle ;File Handle
       ,Ptr, &DCB)          ;Pointer to DCB structure
  If (!SCS_Result)
  {
;;    MsgBox, There is a problem with Serial Port communication. `nFailed Dll SetCommState, SCS_Result=%SCS_Result% `nThe Script Will Now COMFail.
    RS232_Close(RS232_FileHandle)
    COMFail=1
    return
  }
  Timeout:=Ceil(((RS232_Data+4)/RS232_Baud)*1000)+1
  ;###### Create the SetCommTimeouts Structure ######
  if (ReadIntervalTimeout="")
    ReadIntervalTimeout      := Timeout    ; ms, max Interval betwen characters
  ReadTotalTimeoutMultiplier := 0          ; ms * reqested number of bytes
  ReadTotalTimeoutConstant   := ReadIntervalTimeout*100        ; ms
  WriteTotalTimeoutMultiplier:= 0          ; ms * sended number of bytes
  WriteTotalTimeoutConstant  := 0          ; ms
;tooltip,% RS232_Baud "`n" ReadIntervalTimeout "`n" (((RS232_Data+4)/RS232_Baud)*1000) "`n" ReadTotalTimeoutConstant,0,500,5

  if (ReadIntervalTimeout=0)
    ReadIntervalTimeout:=0xffffffff
  if (ReadTotalTimeoutConstant>500)
    ReadTotalTimeoutConstant:=500

  VarSetCapacity(Data, 20, 0) ; 5 * sizeof(DWORD)
  NumPut(ReadIntervalTimeout,         Data,  0, "UInt")
  NumPut(ReadTotalTimeoutMultiplier,  Data,  4, "UInt")
  NumPut(ReadTotalTimeoutConstant,    Data,  8, "UInt")
  NumPut(WriteTotalTimeoutMultiplier, Data, 12, "UInt")
  NumPut(WriteTotalTimeoutConstant,   Data, 16, "UInt")

  ;###### Set the RS232 COM Timeouts ######
  SCT_result := DllCall("SetCommTimeouts"
     ,Ptr, RS232_FileHandle ;File Handle
     ,Ptr, &Data)         ;Pointer to the data structure
  If !SCT_result
  {
;;    MsgBox, There is a problem with Serial Port communication. `nFailed Dll SetCommState, SCT_result=%SCT_result% `nThe Script Will Now COMFail.
    RS232_Close(RS232_FileHandle)
    COMFail=1
    return
  }
  Return RS232_FileHandle
}

RS232_Close(ByRef RS232_FileHandle)
{
  DllCall("CloseHandle",Ptr,RS232_FileHandle)
  RS232_FileHandle:=""
  return
}

RS232_Write(RS232_FileHandle,Data,Data_Length)
{
  if RS232_FileHandle
  WF_Result := DllCall("WriteFile"
       ,Ptr , RS232_FileHandle ;File Handle
       ,Ptr , Data          ;Pointer to string to send
       ,"UInt", Data_Length    ;Data Length
       ,A_PtrSize ? "UPtr*" : "uint*", Bytes_Sent     ;Returns pointer to num bytes sent
       ,"Int" , "NULL")
;  RS232_WaitCommEvent(RS232_FileHandle,0x4)
}

RS232_Read(RS232_FileHandle,Data_Length,Pointer,ByRef RS232_Bytes_Received)
{
  if RS232_FileHandle
  Read_Result := DllCall("ReadFile"
       ,Ptr , RS232_FileHandle   ; hFile
       ,Ptr  , Pointer             ; lpBuffer
       ,"Int"  , Data_Length        ; nNumberOfBytesToRead
       ,A_PtrSize ? "UPtr*" : "uint*", RS232_Bytes_Received   ; lpNumberOfBytesReceived
       ,"Int"  , 0)               ; lpOverlapped
  If (!Read_Result)
  {
    RS232_Close(RS232_FileHandle)
    COMFail=1
    return
  }
}

RS232_WaitCommEvent(RS232_FileHandle,Event=0x3ff)
{
/*
Events:
EV_BREAK:=0x0040 ;A break was detected on input.
EV_CTS:=0x0008 ;The CTS (clear-to-send) signal changed state.
EV_DSR:=0x0010 ;The DSR (data-set-ready) signal changed state.
EV_ERR:=0x0080 ;A line-status error occurred. Line-status errors are CE_FRAME, CE_OVERRUN, and CE_RXPARITY.
EV_RING:=0x0100 ;A ring indicator was detected.
EV_RLSD:=0x0020 ;The RLSD (receive-line-signal-detect) signal changed state.
EV_RXCHAR:=0x0001 ;A character was received and placed in the input buffer.
EV_RXFLAG:=0x0002 ;The event character was received and placed in the input buffer. The event character is specified in the device's DCB structure, which is applied to a serial port by using the SetCommState function.
EV_TXEMPTY:=0x0004 ;The last character in the output buffer was sent.
*/
  MaskSet := DllCall("SetCommMask"
       ,Ptr , RS232_FileHandle ;File Handle
       ,"int" , Event)          ;Mask
  if !MaskSet
  {
    RS232_Close(RS232_FileHandle)
    COMFail=1
    return
  }
  VarSetCapacity(Data,4,0)
  if RS232_FileHandle
  Success := DllCall("WaitCommEvent"
       ,Ptr, RS232_FileHandle ;File Handle
       ,Ptr, &Data          ;Pointer to Event
       ,Ptr, 0)
  if !Success
  {
    RS232_Close(RS232_FileHandle)
    COMFail=1
    return
  }
;  tooltip,% Errorlevel "`n" a_lasterror "`n" Success "`nEvent = " format("0x{:04x}",numget(Data,0,"Int64"))
}

LV_SubitemHitTest(HLV) {
   ; To run this with AHK_Basic change all DllCall types Ptr to "UInt", please.
   ; HLV - ListView's HWND
   LVM_SUBITEMHITTEST := 0x1039
   VarSetCapacity(POINT, 8, 0)
   ; Get the current cursor position in screen coordinates
   DllCall("User32.dll\GetCursorPos", Ptr, &POINT)
   ; Convert them to client coordinates related to the ListView
   DllCall("User32.dll\ScreenToClient", Ptr, HLV, Ptr, &POINT)
   ; Create a LVHITTESTINFO structure (see below)
   VarSetCapacity(LVHITTESTINFO, 24, 0)
   ; Store the relative mouse coordinates
   NumPut(NumGet(POINT, 0, "Int"), LVHITTESTINFO, 0, "Int")
   NumPut(NumGet(POINT, 4, "Int"), LVHITTESTINFO, 4, "Int")
   ; Send a LVM_SUBITEMHITTEST to the ListView
   SendMessage, LVM_SUBITEMHITTEST, 0, &LVHITTESTINFO, , ahk_id %HLV%
   ; If no item was found on this position, the return value is -1
   If (ErrorLevel = -1)
      Return 0
   ; Get the corresponding subitem (column)
   Subitem := NumGet(LVHITTESTINFO, 16, "Int") + 1
   Return Subitem
}

SetParent(hwnd,hNewParent,ClassNN="",x=0,y=0)
{
  if ClassNN
    ControlGet,hNewParent,Hwnd,,%ClassNN%,ahk_id %hNewParent%
  oldParent:=DllCall("SetParent",Ptr,hwnd,Ptr,hNewParent)
  WinMove,ahk_id %hwnd%,,%x%,%y%
  return oldParent
}

StrCut(byref string,sym)
{
  if Pos:=instr(string,sym)
  {
    stringleft,var,string,Pos-1
    stringtrimleft,string,string,Pos
    return var
  }
}

findMetaSbl(intext,byref Splitter,byref Ending)
{
  a:={}
  loop,parse,intext
  {
    if !(a_loopfield+1 or a_loopfield="-" or a_loopfield=".")
    {
      SmblExist:=0
      for i,n in a
      {
        if (i=a_loopfield)
        {
          SmblExist:=1
          a[i]++
          break
        }
      }
      if !SmblExist
        a[a_loopfield]:=1
    }
  }
  countS:=0
  countE:=32000
  for i,n in a
  {
    if (n>countS)
    {
      Splitter:=i
      countS:=n
    }
    if (n<countE)
    {
      Ending:=i
      countE:=n
    }
  }
}

CheckMetaSym(sym)
{
  static Meta:="\`n.*?+[{|()^$"
  if instr(Meta,sym)
    loop,parse,Meta
    {
      if (a_loopfield="`n" and instr(sym,a_loopfield))
        sym:="\n"
      else
        stringreplace,sym,sym,%a_loopfield%,\%a_loopfield%,all
    }
  return sym
}

AddAdd(byref var)
{
  if (var>=0)
    var:="+"var
}

SwapData(var_in,StartByte,NumBytes,byref var_out)
{
  varsetcapacity(var_out,NumBytes)
  loop,% NumBytes
    numput(numget(var_in,StartByte+a_index-1,"uchar"),var_out,NumBytes-a_index,"uchar")
}

CopyData(var_in,StartByte,NumBytes,var_out,out_StartByte)
{
  loop,% NumBytes
    numput(numget(var_in,StartByte+a_index-1,"uchar"),var_out,out_StartByte+a_index-1,"uchar")
}

ArrGetMax(arr,start=0,weight=1)
{
  start:=round(start)
  weight:=round(weight)
  loop,% weight
  {
    if (var<abs(arr[a_index+start]))
      var:=abs(arr[a_index+start])
  }
  return var
}

ArrClear(byref arr)
{
  arr:=""
  arr:=Object()
}

MoveControl(hwnd,x=0,y=0,w=0,h=0,min=10)
{
  overf:=0
  GuiControlGet,E_pos,Pos,% hwnd
  if (E_posw+w<min)
  {
    overf|=1
    x:=w
    w:=0
  }
  if (E_posh+h<min)
  {
    overf=2
    y:=h
    h:=0
  }
  GuiControl,Move,% hwnd,% " x" E_posx+x " y" E_posy+y " w" E_posw+w " h" E_posh+h
  return overf
}

DECtoBIN(h)
{
  stringreplace,h,h,%a_space%,,all
  h:=h+0
  a:=0
  while(h)
  {
    a++
    r:= (a=8?" ":"") . (h&1) . r
    h>>=1
    if (a=8)
      a:=0
  }
  if (a!=0)
  while(a!=8)
  {
    r:= 0 . r
    a++
  }
  return r
}

BINtoDEC(b)
{
  stringreplace,b,b,%a_space%,,all
  r:=0
  loop,parse,b
    r|=(a_loopfield<<(strlen(b)-a_index))
  return r
}

DATAtoSTR(var,st,size,sw=0)
{
  loop,% size
    str.=chr(numget(var+0,(sw?(size-a_index):(st+a_index-1)),"uchar"))
  return str
}

DATAtoHEX(var,st,size,sw=0)
{
  loop,% size
    hex.=format("{:02X}",numget(var+0,(sw?(size-a_index):(st+a_index-1)),"uchar"))
  return hex
}

DATAtoBIN(var,st,size,sw=0)
{
  static bin
  bin:=""
  loop,% size
  {
    h:=numget(var+0,(sw?(size-a_index):(st+a_index-1)),"uchar")
    r:=""
    loop,8
    {
      r:= (a=8?" ":"") . (h&1) . r
      h>>=1
      if (a_index=8)
        r:=" " . r
    }
    bin.= r
  }
  return bin
}

STRToDATA(str,byref var,swap=0)
{
  size:=strlen(str)
  varsetcapacity(var,size+2)
  loop,parse,str
  {
    if !swap
      NumPut(asc(a_loopfield),var,a_index-1,"Uchar")
    else
      NumPut(asc(a_loopfield),var,size-a_index,"Uchar")
  }
  return size
}

DECToDATA(str,byref var,swap=0,dtype="Uint64",size=8)
{
  varsetcapacity(var,size+2)
  NumPut(str+0,var,0,dtype)
  if swap
  {
    varsetcapacity(tmpvar,size)
    loop,% size
      numput(numget(var,a_index-1,"uchar"),tmpvar,size-a_index,"uchar")
    loop,% size
      numput(numget(tmpvar,a_index-1,"uchar"),var,a_index-1,"uchar")
  }
  return size
}

HEXstrToDATA(str,byref var,swap=0)
{
  stringreplace,str,str,%a_space%,,all
  size:=ceil(strlen(str)/2)
  varsetcapacity(var,size+2)
  loop,% size
  {
    stringright,tmp2,str,2
    if !swap
      NumPut("0x"tmp2,var,size-a_index,"Uchar")
    else
      NumPut("0x"tmp2,var,a_index-1,"Uchar")
    StringTrimRight,str,str,2
  }
  return size
}

BINstrToDATA(str,byref var,swap=0)
{
  stringreplace,str,str,%a_space%,,all
  size:=ceil(strlen(str)/8)
  varsetcapacity(var,size+2)
  loop,% size
  {
    stringright,tmp2,str,8
    if !swap
      NumPut(BINtoDEC(tmp2),var,size-a_index,"Uchar")
    else
      NumPut(BINtoDEC(tmp2),var,a_index-1,"Uchar")
    StringTrimRight,str,str,8
  }
  return size
}

ClearVar(byref var,symbol=" ")
{
  stringreplace,var,var,%symbol%,,all
}
_______________________________:
return

; Gdip standard library v1.45 by tic (Tariq Porter) 07/09/11
; Modifed by Rseding91 using fincs 64 bit compatible Gdip library 5/1/2013
; Supports: Basic, _L ANSi, _L Unicode x86 and _L Unicode x64
;
; Updated 2/20/2014 - fixed Gdip_CreateRegion() and Gdip_GetClipRegion() on AHK Unicode x86
; Updated 5/13/2013 - fixed Gdip_SetBitmapToClipboard() on AHK Unicode x64
;
;#####################################################################################
;#####################################################################################
; STATUS ENUMERATION
; Return values for functions specified to have status enumerated return type
;#####################################################################################
;
; Ok =						= 0
; GenericError				= 1
; InvalidParameter			= 2
; OutOfMemory				= 3
; ObjectBusy				= 4
; InsufficientBuffer		= 5
; NotImplemented			= 6
; Win32Error				= 7
; WrongState				= 8
; Aborted					= 9
; FileNotFound				= 10
; ValueOverflow				= 11
; AccessDenied				= 12
; UnknownImageFormat		= 13
; FontFamilyNotFound		= 14
; FontStyleNotFound			= 15
; NotTrueTypeFont			= 16
; UnsupportedGdiplusVersion	= 17
; GdiplusNotInitialized		= 18
; PropertyNotFound			= 19
; PropertyNotSupported		= 20
; ProfileNotFound			= 21
;
;#####################################################################################
;#####################################################################################
; FUNCTIONS
;#####################################################################################
;
; UpdateLayeredWindow(hwnd, hdc, x="", y="", w="", h="", Alpha=255)
; BitBlt(ddc, dx, dy, dw, dh, sdc, sx, sy, Raster="")
; StretchBlt(dDC, dx, dy, dw, dh, sDC, sx, sy, sw, sh, Raster="")
; SetImage(hwnd, hBitmap)
; Gdip_BitmapFromScreen(Screen=0, Raster="")
; CreateRectF(ByRef RectF, x, y, w, h)
; CreateSizeF(ByRef SizeF, w, h)
; CreateDIBSection
;
;#####################################################################################

; Function:     			UpdateLayeredWindow
; Description:  			Updates a layered window with the handle to the DC of a gdi bitmap
;
; hwnd        				Handle of the layered window to update
; hdc           			Handle to the DC of the GDI bitmap to update the window with
; Layeredx      			x position to place the window
; Layeredy      			y position to place the window
; Layeredw      			Width of the window
; Layeredh      			Height of the window
; Alpha         			Default = 255 : The transparency (0-255) to set the window transparency
;
; return      				If the function succeeds, the return value is nonzero
;
; notes						If x or y omitted, then layered window will use its current coordinates
;							If w or h omitted then current width and height will be used

UpdateLayeredWindow(hwnd, hdc, x="", y="", w="", h="", Alpha=255)
{
	Ptr := A_PtrSize ? "UPtr" : "UInt"

	if ((x != "") && (y != ""))
		VarSetCapacity(pt, 8), NumPut(x, pt, 0, "UInt"), NumPut(y, pt, 4, "UInt")

	if (w = "") ||(h = "")
		WinGetPos,,, w, h, ahk_id %hwnd%

	return DllCall("UpdateLayeredWindow"
					, Ptr, hwnd
					, Ptr, 0
					, Ptr, ((x = "") && (y = "")) ? 0 : &pt
					, "int64*", w|h<<32
					, Ptr, hdc
					, "int64*", 0
					, "uint", 0
					, "UInt*", Alpha<<16|1<<24
					, "uint", 2)
}

;#####################################################################################

; Function				BitBlt
; Description			The BitBlt function performs a bit-block transfer of the color data corresponding to a rectangle
;						of pixels from the specified source device context into a destination device context.
;
; dDC					handle to destination DC
; dx					x-coord of destination upper-left corner
; dy					y-coord of destination upper-left corner
; dw					width of the area to copy
; dh					height of the area to copy
; sDC					handle to source DC
; sx					x-coordinate of source upper-left corner
; sy					y-coordinate of source upper-left corner
; Raster				raster operation code
;
; return				If the function succeeds, the return value is nonzero
;
; notes					If no raster operation is specified, then SRCCOPY is used, which copies the source directly to the destination rectangle
;
; BLACKNESS				= 0x00000042
; NOTSRCERASE			= 0x001100A6
; NOTSRCCOPY			= 0x00330008
; SRCERASE				= 0x00440328
; DSTINVERT				= 0x00550009
; PATINVERT				= 0x005A0049
; SRCINVERT				= 0x00660046
; SRCAND				= 0x008800C6
; MERGEPAINT			= 0x00BB0226
; MERGECOPY				= 0x00C000CA
; SRCCOPY				= 0x00CC0020
; SRCPAINT				= 0x00EE0086
; PATCOPY				= 0x00F00021
; PATPAINT				= 0x00FB0A09
; WHITENESS				= 0x00FF0062
; CAPTUREBLT			= 0x40000000
; NOMIRRORBITMAP		= 0x80000000

BitBlt(ddc, dx, dy, dw, dh, sdc, sx, sy, Raster="")
{
	Ptr := A_PtrSize ? "UPtr" : "UInt"

	return DllCall("gdi32\BitBlt"
					, Ptr, dDC
					, "int", dx
					, "int", dy
					, "int", dw
					, "int", dh
					, Ptr, sDC
					, "int", sx
					, "int", sy
					, "uint", Raster ? Raster : 0x00CC0020)
}

;#####################################################################################

; Function				StretchBlt
; Description			The StretchBlt function copies a bitmap from a source rectangle into a destination rectangle,
;						stretching or compressing the bitmap to fit the dimensions of the destination rectangle, if necessary.
;						The system stretches or compresses the bitmap according to the stretching mode currently set in the destination device context.
;
; ddc					handle to destination DC
; dx					x-coord of destination upper-left corner
; dy					y-coord of destination upper-left corner
; dw					width of destination rectangle
; dh					height of destination rectangle
; sdc					handle to source DC
; sx					x-coordinate of source upper-left corner
; sy					y-coordinate of source upper-left corner
; sw					width of source rectangle
; sh					height of source rectangle
; Raster				raster operation code
;
; return				If the function succeeds, the return value is nonzero
;
; notes					If no raster operation is specified, then SRCCOPY is used. It uses the same raster operations as BitBlt

StretchBlt(ddc, dx, dy, dw, dh, sdc, sx, sy, sw, sh, Raster="")
{
	Ptr := A_PtrSize ? "UPtr" : "UInt"

	return DllCall("gdi32\StretchBlt"
					, Ptr, ddc
					, "int", dx
					, "int", dy
					, "int", dw
					, "int", dh
					, Ptr, sdc
					, "int", sx
					, "int", sy
					, "int", sw
					, "int", sh
					, "uint", Raster ? Raster : 0x00CC0020)
}

;#####################################################################################

; Function				SetStretchBltMode
; Description			The SetStretchBltMode function sets the bitmap stretching mode in the specified device context
;
; hdc					handle to the DC
; iStretchMode			The stretching mode, describing how the target will be stretched
;
; return				If the function succeeds, the return value is the previous stretching mode. If it fails it will return 0
;
; STRETCH_ANDSCANS 		= 0x01
; STRETCH_ORSCANS 		= 0x02
; STRETCH_DELETESCANS 	= 0x03
; STRETCH_HALFTONE 		= 0x04

SetStretchBltMode(hdc, iStretchMode=4)
{
	return DllCall("gdi32\SetStretchBltMode"
					, A_PtrSize ? "UPtr" : "UInt", hdc
					, "int", iStretchMode)
}

;#####################################################################################

; Function				SetImage
; Description			Associates a new image with a static control
;
; hwnd					handle of the control to update
; hBitmap				a gdi bitmap to associate the static control with
;
; return				If the function succeeds, the return value is nonzero

SetImage(hwnd, hBitmap)
{
	SendMessage, 0x172, 0x0, hBitmap,, ahk_id %hwnd%
	E := ErrorLevel
	DeleteObject(E)
	return E
}

;#####################################################################################

; Function				SetSysColorToControl
; Description			Sets a solid colour to a control
;
; hwnd					handle of the control to update
; SysColor				A system colour to set to the control
;
; return				If the function succeeds, the return value is zero
;
; notes					A control must have the 0xE style set to it so it is recognised as a bitmap
;						By default SysColor=15 is used which is COLOR_3DFACE. This is the standard background for a control
;
; COLOR_3DDKSHADOW				= 21
; COLOR_3DFACE					= 15
; COLOR_3DHIGHLIGHT				= 20
; COLOR_3DHILIGHT				= 20
; COLOR_3DLIGHT					= 22
; COLOR_3DSHADOW				= 16
; COLOR_ACTIVEBORDER			= 10
; COLOR_ACTIVECAPTION			= 2
; COLOR_APPWORKSPACE			= 12
; COLOR_BACKGROUND				= 1
; COLOR_BTNFACE					= 15
; COLOR_BTNHIGHLIGHT			= 20
; COLOR_BTNHILIGHT				= 20
; COLOR_BTNSHADOW				= 16
; COLOR_BTNTEXT					= 18
; COLOR_CAPTIONTEXT				= 9
; COLOR_DESKTOP					= 1
; COLOR_GRADIENTACTIVECAPTION	= 27
; COLOR_GRADIENTINACTIVECAPTION	= 28
; COLOR_GRAYTEXT				= 17
; COLOR_HIGHLIGHT				= 13
; COLOR_HIGHLIGHTTEXT			= 14
; COLOR_HOTLIGHT				= 26
; COLOR_INACTIVEBORDER			= 11
; COLOR_INACTIVECAPTION			= 3
; COLOR_INACTIVECAPTIONTEXT		= 19
; COLOR_INFOBK					= 24
; COLOR_INFOTEXT				= 23
; COLOR_MENU					= 4
; COLOR_MENUHILIGHT				= 29
; COLOR_MENUBAR					= 30
; COLOR_MENUTEXT				= 7
; COLOR_SCROLLBAR				= 0
; COLOR_WINDOW					= 5
; COLOR_WINDOWFRAME				= 6
; COLOR_WINDOWTEXT				= 8

SetSysColorToControl(hwnd, SysColor=15)
{
   WinGetPos,,, w, h, ahk_id %hwnd%
   bc := DllCall("GetSysColor", "Int", SysColor, "UInt")
   pBrushClear := Gdip_BrushCreateSolid(0xff000000 | (bc >> 16 | bc & 0xff00 | (bc & 0xff) << 16))
   pBitmap := Gdip_CreateBitmap(w, h), G := Gdip_GraphicsFromImage(pBitmap)
   Gdip_FillRectangle(G, pBrushClear, 0, 0, w, h)
   hBitmap := Gdip_CreateHBITMAPFromBitmap(pBitmap)
   SetImage(hwnd, hBitmap)
   Gdip_DeleteBrush(pBrushClear)
   Gdip_DeleteGraphics(G), Gdip_DisposeImage(pBitmap), DeleteObject(hBitmap)
   return 0
}

;#####################################################################################

; Function				Gdip_BitmapFromScreen
; Description			Gets a gdi+ bitmap from the screen
;
; Screen				0 = All screens
;						Any numerical value = Just that screen
;						x|y|w|h = Take specific coordinates with a width and height
; Raster				raster operation code
;
; return      			If the function succeeds, the return value is a pointer to a gdi+ bitmap
;						-1:		one or more of x,y,w,h not passed properly
;
; notes					If no raster operation is specified, then SRCCOPY is used to the returned bitmap

Gdip_BitmapFromScreen(Screen=0, Raster="")
{
	if (Screen = 0)
	{
		Sysget, x, 76
		Sysget, y, 77
		Sysget, w, 78
		Sysget, h, 79
	}
	else if (SubStr(Screen, 1, 5) = "hwnd:")
	{
		Screen := SubStr(Screen, 6)
		if !WinExist( "ahk_id " Screen)
			return -2
		WinGetPos,,, w, h, ahk_id %Screen%
		x := y := 0
		hhdc := GetDCEx(Screen, 3)
	}
	else if (Screen&1 != "")
	{
		Sysget, M, Monitor, %Screen%
		x := MLeft, y := MTop, w := MRight-MLeft, h := MBottom-MTop
	}
	else
	{
		StringSplit, S, Screen, |
		x := S1, y := S2, w := S3, h := S4
	}

	if (x = "") || (y = "") || (w = "") || (h = "")
		return -1

	chdc := CreateCompatibleDC(), hbm := CreateDIBSection(w, h, chdc), obm := SelectObject(chdc, hbm), hhdc := hhdc ? hhdc : GetDC()
	BitBlt(chdc, 0, 0, w, h, hhdc, x, y, Raster)
	ReleaseDC(hhdc)

	pBitmap := Gdip_CreateBitmapFromHBITMAP(hbm)
	SelectObject(chdc, obm), DeleteObject(hbm), DeleteDC(hhdc), DeleteDC(chdc)
	return pBitmap
}

;#####################################################################################

; Function				Gdip_BitmapFromHWND
; Description			Uses PrintWindow to get a handle to the specified window and return a bitmap from it
;
; hwnd					handle to the window to get a bitmap from
;
; return				If the function succeeds, the return value is a pointer to a gdi+ bitmap
;
; notes					Window must not be not minimised in order to get a handle to it's client area

Gdip_BitmapFromHWND(hwnd)
{
	WinGetPos,,, Width, Height, ahk_id %hwnd%
	hbm := CreateDIBSection(Width, Height), hdc := CreateCompatibleDC(), obm := SelectObject(hdc, hbm)
	PrintWindow(hwnd, hdc)
	pBitmap := Gdip_CreateBitmapFromHBITMAP(hbm)
	SelectObject(hdc, obm), DeleteObject(hbm), DeleteDC(hdc)
	return pBitmap
}

;#####################################################################################

; Function    			CreateRectF
; Description			Creates a RectF object, containing a the coordinates and dimensions of a rectangle
;
; RectF       			Name to call the RectF object
; x            			x-coordinate of the upper left corner of the rectangle
; y            			y-coordinate of the upper left corner of the rectangle
; w            			Width of the rectangle
; h            			Height of the rectangle
;
; return      			No return value

CreateRectF(ByRef RectF, x, y, w, h)
{
   VarSetCapacity(RectF, 16)
   NumPut(x, RectF, 0, "float"), NumPut(y, RectF, 4, "float"), NumPut(w, RectF, 8, "float"), NumPut(h, RectF, 12, "float")
}

;#####################################################################################

; Function    			CreateRect
; Description			Creates a Rect object, containing a the coordinates and dimensions of a rectangle
;
; RectF       			Name to call the RectF object
; x            			x-coordinate of the upper left corner of the rectangle
; y            			y-coordinate of the upper left corner of the rectangle
; w            			Width of the rectangle
; h            			Height of the rectangle
;
; return      			No return value

CreateRect(ByRef Rect, x, y, w, h)
{
	VarSetCapacity(Rect, 16)
	NumPut(x, Rect, 0, "uint"), NumPut(y, Rect, 4, "uint"), NumPut(w, Rect, 8, "uint"), NumPut(h, Rect, 12, "uint")
}
;#####################################################################################

; Function		    	CreateSizeF
; Description			Creates a SizeF object, containing an 2 values
;
; SizeF         		Name to call the SizeF object
; w            			w-value for the SizeF object
; h            			h-value for the SizeF object
;
; return      			No Return value

CreateSizeF(ByRef SizeF, w, h)
{
   VarSetCapacity(SizeF, 8)
   NumPut(w, SizeF, 0, "float"), NumPut(h, SizeF, 4, "float")
}
;#####################################################################################

; Function		    	CreatePointF
; Description			Creates a SizeF object, containing an 2 values
;
; SizeF         		Name to call the SizeF object
; w            			w-value for the SizeF object
; h            			h-value for the SizeF object
;
; return      			No Return value

CreatePointF(ByRef PointF, x, y)
{
   VarSetCapacity(PointF, 8)
   NumPut(x, PointF, 0, "float"), NumPut(y, PointF, 4, "float")
}
;#####################################################################################

; Function				CreateDIBSection
; Description			The CreateDIBSection function creates a DIB (Device Independent Bitmap) that applications can write to directly
;
; w						width of the bitmap to create
; h						height of the bitmap to create
; hdc					a handle to the device context to use the palette from
; bpp					bits per pixel (32 = ARGB)
; ppvBits				A pointer to a variable that receives a pointer to the location of the DIB bit values
;
; return				returns a DIB. A gdi bitmap
;
; notes					ppvBits will receive the location of the pixels in the DIB

CreateDIBSection(w, h, hdc="", bpp=32, ByRef ppvBits=0)
{
	Ptr := A_PtrSize ? "UPtr" : "UInt"

	hdc2 := hdc ? hdc : GetDC()
	VarSetCapacity(bi, 40, 0)

	NumPut(w, bi, 4, "uint")
	, NumPut(h, bi, 8, "uint")
	, NumPut(40, bi, 0, "uint")
	, NumPut(1, bi, 12, "ushort")
	, NumPut(0, bi, 16, "uInt")
	, NumPut(bpp, bi, 14, "ushort")

	hbm := DllCall("CreateDIBSection"
					, Ptr, hdc2
					, Ptr, &bi
					, "uint", 0
					, A_PtrSize ? "UPtr*" : "uint*", ppvBits
					, Ptr, 0
					, "uint", 0, Ptr)

	if !hdc
		ReleaseDC(hdc2)
	return hbm
}

;#####################################################################################

; Function				PrintWindow
; Description			The PrintWindow function copies a visual window into the specified device context (DC), typically a printer DC
;
; hwnd					A handle to the window that will be copied
; hdc					A handle to the device context
; Flags					Drawing options
;
; return				If the function succeeds, it returns a nonzero value
;
; PW_CLIENTONLY			= 1

PrintWindow(hwnd, hdc, Flags=0)
{
	Ptr := A_PtrSize ? "UPtr" : "UInt"

	return DllCall("PrintWindow", Ptr, hwnd, Ptr, hdc, "uint", Flags)
}

;#####################################################################################

; Function				DestroyIcon
; Description			Destroys an icon and frees any memory the icon occupied
;
; hIcon					Handle to the icon to be destroyed. The icon must not be in use
;
; return				If the function succeeds, the return value is nonzero

DestroyIcon(hIcon)
{
	return DllCall("DestroyIcon", A_PtrSize ? "UPtr" : "UInt", hIcon)
}

;#####################################################################################

PaintDesktop(hdc)
{
	return DllCall("PaintDesktop", A_PtrSize ? "UPtr" : "UInt", hdc)
}

;#####################################################################################

CreateCompatibleBitmap(hdc, w, h)
{
	return DllCall("gdi32\CreateCompatibleBitmap", A_PtrSize ? "UPtr" : "UInt", hdc, "int", w, "int", h)
}

;#####################################################################################

; Function				CreateCompatibleDC
; Description			This function creates a memory device context (DC) compatible with the specified device
;
; hdc					Handle to an existing device context
;
; return				returns the handle to a device context or 0 on failure
;
; notes					If this handle is 0 (by default), the function creates a memory device context compatible with the application's current screen

CreateCompatibleDC(hdc=0)
{
   return DllCall("CreateCompatibleDC", A_PtrSize ? "UPtr" : "UInt", hdc)
}

;#####################################################################################

; Function				SelectObject
; Description			The SelectObject function selects an object into the specified device context (DC). The new object replaces the previous object of the same type
;
; hdc					Handle to a DC
; hgdiobj				A handle to the object to be selected into the DC
;
; return				If the selected object is not a region and the function succeeds, the return value is a handle to the object being replaced
;
; notes					The specified object must have been created by using one of the following functions
;						Bitmap - CreateBitmap, CreateBitmapIndirect, CreateCompatibleBitmap, CreateDIBitmap, CreateDIBSection (A single bitmap cannot be selected into more than one DC at the same time)
;						Brush - CreateBrushIndirect, CreateDIBPatternBrush, CreateDIBPatternBrushPt, CreateHatchBrush, CreatePatternBrush, CreateSolidBrush
;						Font - CreateFont, CreateFontIndirect
;						Pen - CreatePen, CreatePenIndirect
;						Region - CombineRgn, CreateEllipticRgn, CreateEllipticRgnIndirect, CreatePolygonRgn, CreateRectRgn, CreateRectRgnIndirect
;
; notes					If the selected object is a region and the function succeeds, the return value is one of the following value
;
; SIMPLEREGION			= 2 Region consists of a single rectangle
; COMPLEXREGION			= 3 Region consists of more than one rectangle
; NULLREGION			= 1 Region is empty

SelectObject(hdc, hgdiobj)
{
	Ptr := A_PtrSize ? "UPtr" : "UInt"

	return DllCall("SelectObject", Ptr, hdc, Ptr, hgdiobj)
}

;#####################################################################################

; Function				DeleteObject
; Description			This function deletes a logical pen, brush, font, bitmap, region, or palette, freeing all system resources associated with the object
;						After the object is deleted, the specified handle is no longer valid
;
; hObject				Handle to a logical pen, brush, font, bitmap, region, or palette to delete
;
; return				Nonzero indicates success. Zero indicates that the specified handle is not valid or that the handle is currently selected into a device context

DeleteObject(hObject)
{
   return DllCall("DeleteObject", A_PtrSize ? "UPtr" : "UInt", hObject)
}

;#####################################################################################

; Function				GetDC
; Description			This function retrieves a handle to a display device context (DC) for the client area of the specified window.
;						The display device context can be used in subsequent graphics display interface (GDI) functions to draw in the client area of the window.
;
; hwnd					Handle to the window whose device context is to be retrieved. If this value is NULL, GetDC retrieves the device context for the entire screen
;
; return				The handle the device context for the specified window's client area indicates success. NULL indicates failure

GetDC(hwnd=0)
{
	return DllCall("GetDC", A_PtrSize ? "UPtr" : "UInt", hwnd)
}

;#####################################################################################

; DCX_CACHE = 0x2
; DCX_CLIPCHILDREN = 0x8
; DCX_CLIPSIBLINGS = 0x10
; DCX_EXCLUDERGN = 0x40
; DCX_EXCLUDEUPDATE = 0x100
; DCX_INTERSECTRGN = 0x80
; DCX_INTERSECTUPDATE = 0x200
; DCX_LOCKWINDOWUPDATE = 0x400
; DCX_NORECOMPUTE = 0x100000
; DCX_NORESETATTRS = 0x4
; DCX_PARENTCLIP = 0x20
; DCX_VALIDATE = 0x200000
; DCX_WINDOW = 0x1

GetDCEx(hwnd, flags=0, hrgnClip=0)
{
	Ptr := A_PtrSize ? "UPtr" : "UInt"

    return DllCall("GetDCEx", Ptr, hwnd, Ptr, hrgnClip, "int", flags)
}

;#####################################################################################

; Function				ReleaseDC
; Description			This function releases a device context (DC), freeing it for use by other applications. The effect of ReleaseDC depends on the type of device context
;
; hdc					Handle to the device context to be released
; hwnd					Handle to the window whose device context is to be released
;
; return				1 = released
;						0 = not released
;
; notes					The application must call the ReleaseDC function for each call to the GetWindowDC function and for each call to the GetDC function that retrieves a common device context
;						An application cannot use the ReleaseDC function to release a device context that was created by calling the CreateDC function; instead, it must use the DeleteDC function.

ReleaseDC(hdc, hwnd=0)
{
	Ptr := A_PtrSize ? "UPtr" : "UInt"

	return DllCall("ReleaseDC", Ptr, hwnd, Ptr, hdc)
}

;#####################################################################################

; Function				DeleteDC
; Description			The DeleteDC function deletes the specified device context (DC)
;
; hdc					A handle to the device context
;
; return				If the function succeeds, the return value is nonzero
;
; notes					An application must not delete a DC whose handle was obtained by calling the GetDC function. Instead, it must call the ReleaseDC function to free the DC

DeleteDC(hdc)
{
   return DllCall("DeleteDC", A_PtrSize ? "UPtr" : "UInt", hdc)
}
;#####################################################################################

; Function				Gdip_LibraryVersion
; Description			Get the current library version
;
; return				the library version
;
; notes					This is useful for non compiled programs to ensure that a person doesn't run an old version when testing your scripts

Gdip_LibraryVersion()
{
	return 1.45
}

;#####################################################################################

; Function				Gdip_LibrarySubVersion
; Description			Get the current library sub version
;
; return				the library sub version
;
; notes					This is the sub-version currently maintained by Rseding91
Gdip_LibrarySubVersion()
{
	return 1.47
}

;#####################################################################################

; Function:    			Gdip_BitmapFromBRA
; Description: 			Gets a pointer to a gdi+ bitmap from a BRA file
;
; BRAFromMemIn			The variable for a BRA file read to memory
; File					The name of the file, or its number that you would like (This depends on alternate parameter)
; Alternate				Changes whether the File parameter is the file name or its number
;
; return      			If the function succeeds, the return value is a pointer to a gdi+ bitmap
;						-1 = The BRA variable is empty
;						-2 = The BRA has an incorrect header
;						-3 = The BRA has information missing
;						-4 = Could not find file inside the BRA

Gdip_BitmapFromBRA(ByRef BRAFromMemIn, File, Alternate=0)
{
	Static FName = "ObjRelease"

	if !BRAFromMemIn
		return -1
	Loop, Parse, BRAFromMemIn, `n
	{
		if (A_Index = 1)
		{
			StringSplit, Header, A_LoopField, |
			if (Header0 != 4 || Header2 != "BRA!")
				return -2
		}
		else if (A_Index = 2)
		{
			StringSplit, Info, A_LoopField, |
			if (Info0 != 3)
				return -3
		}
		else
			break
	}
	if !Alternate
		StringReplace, File, File, \, \\, All
	RegExMatch(BRAFromMemIn, "mi`n)^" (Alternate ? File "\|.+?\|(\d+)\|(\d+)" : "\d+\|" File "\|(\d+)\|(\d+)") "$", FileInfo)
	if !FileInfo
		return -4

	hData := DllCall("GlobalAlloc", "uint", 2, Ptr, FileInfo2, Ptr)
	pData := DllCall("GlobalLock", Ptr, hData, Ptr)
	DllCall("RtlMoveMemory", Ptr, pData, Ptr, &BRAFromMemIn+Info2+FileInfo1, Ptr, FileInfo2)
	DllCall("GlobalUnlock", Ptr, hData)
	DllCall("ole32\CreateStreamOnHGlobal", Ptr, hData, "int", 1, A_PtrSize ? "UPtr*" : "UInt*", pStream)
	DllCall("gdiplus\GdipCreateBitmapFromStream", Ptr, pStream, A_PtrSize ? "UPtr*" : "UInt*", pBitmap)
	If (A_PtrSize)
		%FName%(pStream)
	Else
		DllCall(NumGet(NumGet(1*pStream)+8), "uint", pStream)
	return pBitmap
}

;#####################################################################################

; Function				Gdip_DrawRectangle
; Description			This function uses a pen to draw the outline of a rectangle into the Graphics of a bitmap
;
; pGraphics				Pointer to the Graphics of a bitmap
; pPen					Pointer to a pen
; x						x-coordinate of the top left of the rectangle
; y						y-coordinate of the top left of the rectangle
; w						width of the rectanlge
; h						height of the rectangle
;
; return				status enumeration. 0 = success
;
; notes					as all coordinates are taken from the top left of each pixel, then the entire width/height should be specified as subtracting the pen width

Gdip_DrawRectangle(pGraphics, pPen, x, y, w, h)
{
	Ptr := A_PtrSize ? "UPtr" : "UInt"

	return DllCall("gdiplus\GdipDrawRectangle", Ptr, pGraphics, Ptr, pPen, "float", x, "float", y, "float", w, "float", h)
}

;#####################################################################################

; Function				Gdip_DrawRoundedRectangle
; Description			This function uses a pen to draw the outline of a rounded rectangle into the Graphics of a bitmap
;
; pGraphics				Pointer to the Graphics of a bitmap
; pPen					Pointer to a pen
; x						x-coordinate of the top left of the rounded rectangle
; y						y-coordinate of the top left of the rounded rectangle
; w						width of the rectanlge
; h						height of the rectangle
; r						radius of the rounded corners
;
; return				status enumeration. 0 = success
;
; notes					as all coordinates are taken from the top left of each pixel, then the entire width/height should be specified as subtracting the pen width

Gdip_DrawRoundedRectangle(pGraphics, pPen, x, y, w, h, r)
{
	Gdip_SetClipRect(pGraphics, x-r, y-r, 2*r, 2*r, 4)
	Gdip_SetClipRect(pGraphics, x+w-r, y-r, 2*r, 2*r, 4)
	Gdip_SetClipRect(pGraphics, x-r, y+h-r, 2*r, 2*r, 4)
	Gdip_SetClipRect(pGraphics, x+w-r, y+h-r, 2*r, 2*r, 4)
	E := Gdip_DrawRectangle(pGraphics, pPen, x, y, w, h)
	Gdip_ResetClip(pGraphics)
	Gdip_SetClipRect(pGraphics, x-(2*r), y+r, w+(4*r), h-(2*r), 4)
	Gdip_SetClipRect(pGraphics, x+r, y-(2*r), w-(2*r), h+(4*r), 4)
	Gdip_DrawEllipse(pGraphics, pPen, x, y, 2*r, 2*r)
	Gdip_DrawEllipse(pGraphics, pPen, x+w-(2*r), y, 2*r, 2*r)
	Gdip_DrawEllipse(pGraphics, pPen, x, y+h-(2*r), 2*r, 2*r)
	Gdip_DrawEllipse(pGraphics, pPen, x+w-(2*r), y+h-(2*r), 2*r, 2*r)
	Gdip_ResetClip(pGraphics)
	return E
}

;#####################################################################################

; Function				Gdip_DrawEllipse
; Description			This function uses a pen to draw the outline of an ellipse into the Graphics of a bitmap
;
; pGraphics				Pointer to the Graphics of a bitmap
; pPen					Pointer to a pen
; x						x-coordinate of the top left of the rectangle the ellipse will be drawn into
; y						y-coordinate of the top left of the rectangle the ellipse will be drawn into
; w						width of the ellipse
; h						height of the ellipse
;
; return				status enumeration. 0 = success
;
; notes					as all coordinates are taken from the top left of each pixel, then the entire width/height should be specified as subtracting the pen width

Gdip_DrawEllipse(pGraphics, pPen, x, y, w, h)
{
	Ptr := A_PtrSize ? "UPtr" : "UInt"

	return DllCall("gdiplus\GdipDrawEllipse", Ptr, pGraphics, Ptr, pPen, "float", x, "float", y, "float", w, "float", h)
}

;#####################################################################################

; Function				Gdip_DrawBezier
; Description			This function uses a pen to draw the outline of a bezier (a weighted curve) into the Graphics of a bitmap
;
; pGraphics				Pointer to the Graphics of a bitmap
; pPen					Pointer to a pen
; x1					x-coordinate of the start of the bezier
; y1					y-coordinate of the start of the bezier
; x2					x-coordinate of the first arc of the bezier
; y2					y-coordinate of the first arc of the bezier
; x3					x-coordinate of the second arc of the bezier
; y3					y-coordinate of the second arc of the bezier
; x4					x-coordinate of the end of the bezier
; y4					y-coordinate of the end of the bezier
;
; return				status enumeration. 0 = success
;
; notes					as all coordinates are taken from the top left of each pixel, then the entire width/height should be specified as subtracting the pen width

Gdip_DrawBezier(pGraphics, pPen, x1, y1, x2, y2, x3, y3, x4, y4)
{
	Ptr := A_PtrSize ? "UPtr" : "UInt"

	return DllCall("gdiplus\GdipDrawBezier"
					, Ptr, pgraphics
					, Ptr, pPen
					, "float", x1
					, "float", y1
					, "float", x2
					, "float", y2
					, "float", x3
					, "float", y3
					, "float", x4
					, "float", y4)
}

;#####################################################################################

; Function				Gdip_DrawArc
; Description			This function uses a pen to draw the outline of an arc into the Graphics of a bitmap
;
; pGraphics				Pointer to the Graphics of a bitmap
; pPen					Pointer to a pen
; x						x-coordinate of the start of the arc
; y						y-coordinate of the start of the arc
; w						width of the arc
; h						height of the arc
; StartAngle			specifies the angle between the x-axis and the starting point of the arc
; SweepAngle			specifies the angle between the starting and ending points of the arc
;
; return				status enumeration. 0 = success
;
; notes					as all coordinates are taken from the top left of each pixel, then the entire width/height should be specified as subtracting the pen width

Gdip_DrawArc(pGraphics, pPen, x, y, w, h, StartAngle, SweepAngle)
{
	Ptr := A_PtrSize ? "UPtr" : "UInt"

	return DllCall("gdiplus\GdipDrawArc"
					, Ptr, pGraphics
					, Ptr, pPen
					, "float", x
					, "float", y
					, "float", w
					, "float", h
					, "float", StartAngle
					, "float", SweepAngle)
}

;#####################################################################################

; Function				Gdip_DrawPie
; Description			This function uses a pen to draw the outline of a pie into the Graphics of a bitmap
;
; pGraphics				Pointer to the Graphics of a bitmap
; pPen					Pointer to a pen
; x						x-coordinate of the start of the pie
; y						y-coordinate of the start of the pie
; w						width of the pie
; h						height of the pie
; StartAngle			specifies the angle between the x-axis and the starting point of the pie
; SweepAngle			specifies the angle between the starting and ending points of the pie
;
; return				status enumeration. 0 = success
;
; notes					as all coordinates are taken from the top left of each pixel, then the entire width/height should be specified as subtracting the pen width

Gdip_DrawPie(pGraphics, pPen, x, y, w, h, StartAngle, SweepAngle)
{
	Ptr := A_PtrSize ? "UPtr" : "UInt"

	return DllCall("gdiplus\GdipDrawPie", Ptr, pGraphics, Ptr, pPen, "float", x, "float", y, "float", w, "float", h, "float", StartAngle, "float", SweepAngle)
}

;#####################################################################################

; Function				Gdip_DrawLine
; Description			This function uses a pen to draw a line into the Graphics of a bitmap
;
; pGraphics				Pointer to the Graphics of a bitmap
; pPen					Pointer to a pen
; x1					x-coordinate of the start of the line
; y1					y-coordinate of the start of the line
; x2					x-coordinate of the end of the line
; y2					y-coordinate of the end of the line
;
; return				status enumeration. 0 = success

Gdip_DrawLine(pGraphics, pPen, x1, y1, x2, y2)
{
	Ptr := A_PtrSize ? "UPtr" : "UInt"

	return DllCall("gdiplus\GdipDrawLine"
					, Ptr, pGraphics
					, Ptr, pPen
					, "float", x1
					, "float", y1
					, "float", x2
					, "float", y2)
}

;#####################################################################################

; Function				Gdip_DrawLines
; Description			This function uses a pen to draw a series of joined lines into the Graphics of a bitmap
;
; pGraphics				Pointer to the Graphics of a bitmap
; pPen					Pointer to a pen
; Points				the coordinates of all the points passed as x1,y1|x2,y2|x3,y3.....
;
; return				status enumeration. 0 = success

Gdip_DrawLines(pGraphics, pPen, Points)
{
	Ptr := A_PtrSize ? "UPtr" : "UInt"
	StringSplit, Points, Points, |
	VarSetCapacity(PointF, 8*Points0)
	Loop, %Points0%
	{
		StringSplit, Coord, Points%A_Index%, `,
		NumPut(Coord1, PointF, 8*(A_Index-1), "float"), NumPut(Coord2, PointF, (8*(A_Index-1))+4, "float")
	}
	return DllCall("gdiplus\GdipDrawLines", Ptr, pGraphics, Ptr, pPen, Ptr, &PointF, "int", Points0)
}

;#####################################################################################

; Function				Gdip_FillRectangle
; Description			This function uses a brush to fill a rectangle in the Graphics of a bitmap
;
; pGraphics				Pointer to the Graphics of a bitmap
; pBrush				Pointer to a brush
; x						x-coordinate of the top left of the rectangle
; y						y-coordinate of the top left of the rectangle
; w						width of the rectanlge
; h						height of the rectangle
;
; return				status enumeration. 0 = success

Gdip_FillRectangle(pGraphics, pBrush, x, y, w, h)
{
	Ptr := A_PtrSize ? "UPtr" : "UInt"

	return DllCall("gdiplus\GdipFillRectangle"
					, Ptr, pGraphics
					, Ptr, pBrush
					, "float", x
					, "float", y
					, "float", w
					, "float", h)
}

;#####################################################################################

; Function				Gdip_FillRoundedRectangle
; Description			This function uses a brush to fill a rounded rectangle in the Graphics of a bitmap
;
; pGraphics				Pointer to the Graphics of a bitmap
; pBrush				Pointer to a brush
; x						x-coordinate of the top left of the rounded rectangle
; y						y-coordinate of the top left of the rounded rectangle
; w						width of the rectanlge
; h						height of the rectangle
; r						radius of the rounded corners
;
; return				status enumeration. 0 = success

Gdip_FillRoundedRectangle(pGraphics, pBrush, x, y, w, h, r)
{
	Region := Gdip_GetClipRegion(pGraphics)
	Gdip_SetClipRect(pGraphics, x-r, y-r, 2*r, 2*r, 4)
	Gdip_SetClipRect(pGraphics, x+w-r, y-r, 2*r, 2*r, 4)
	Gdip_SetClipRect(pGraphics, x-r, y+h-r, 2*r, 2*r, 4)
	Gdip_SetClipRect(pGraphics, x+w-r, y+h-r, 2*r, 2*r, 4)
	E := Gdip_FillRectangle(pGraphics, pBrush, x, y, w, h)
	Gdip_SetClipRegion(pGraphics, Region, 0)
	Gdip_SetClipRect(pGraphics, x-(2*r), y+r, w+(4*r), h-(2*r), 4)
	Gdip_SetClipRect(pGraphics, x+r, y-(2*r), w-(2*r), h+(4*r), 4)
	Gdip_FillEllipse(pGraphics, pBrush, x, y, 2*r, 2*r)
	Gdip_FillEllipse(pGraphics, pBrush, x+w-(2*r), y, 2*r, 2*r)
	Gdip_FillEllipse(pGraphics, pBrush, x, y+h-(2*r), 2*r, 2*r)
	Gdip_FillEllipse(pGraphics, pBrush, x+w-(2*r), y+h-(2*r), 2*r, 2*r)
	Gdip_SetClipRegion(pGraphics, Region, 0)
	Gdip_DeleteRegion(Region)
	return E
}

;#####################################################################################

; Function				Gdip_FillPolygon
; Description			This function uses a brush to fill a polygon in the Graphics of a bitmap
;
; pGraphics				Pointer to the Graphics of a bitmap
; pBrush				Pointer to a brush
; Points				the coordinates of all the points passed as x1,y1|x2,y2|x3,y3.....
;
; return				status enumeration. 0 = success
;
; notes					Alternate will fill the polygon as a whole, wheras winding will fill each new "segment"
; Alternate 			= 0
; Winding 				= 1

Gdip_FillPolygon(pGraphics, pBrush, Points, FillMode=0)
{
	Ptr := A_PtrSize ? "UPtr" : "UInt"

	StringSplit, Points, Points, |
	VarSetCapacity(PointF, 8*Points0)
	Loop, %Points0%
	{
		StringSplit, Coord, Points%A_Index%, `,
		NumPut(Coord1, PointF, 8*(A_Index-1), "float"), NumPut(Coord2, PointF, (8*(A_Index-1))+4, "float")
	}
	return DllCall("gdiplus\GdipFillPolygon", Ptr, pGraphics, Ptr, pBrush, Ptr, &PointF, "int", Points0, "int", FillMode)
}

;#####################################################################################

; Function				Gdip_FillPie
; Description			This function uses a brush to fill a pie in the Graphics of a bitmap
;
; pGraphics				Pointer to the Graphics of a bitmap
; pBrush				Pointer to a brush
; x						x-coordinate of the top left of the pie
; y						y-coordinate of the top left of the pie
; w						width of the pie
; h						height of the pie
; StartAngle			specifies the angle between the x-axis and the starting point of the pie
; SweepAngle			specifies the angle between the starting and ending points of the pie
;
; return				status enumeration. 0 = success

Gdip_FillPie(pGraphics, pBrush, x, y, w, h, StartAngle, SweepAngle)
{
	Ptr := A_PtrSize ? "UPtr" : "UInt"

	return DllCall("gdiplus\GdipFillPie"
					, Ptr, pGraphics
					, Ptr, pBrush
					, "float", x
					, "float", y
					, "float", w
					, "float", h
					, "float", StartAngle
					, "float", SweepAngle)
}

;#####################################################################################

; Function				Gdip_FillEllipse
; Description			This function uses a brush to fill an ellipse in the Graphics of a bitmap
;
; pGraphics				Pointer to the Graphics of a bitmap
; pBrush				Pointer to a brush
; x						x-coordinate of the top left of the ellipse
; y						y-coordinate of the top left of the ellipse
; w						width of the ellipse
; h						height of the ellipse
;
; return				status enumeration. 0 = success

Gdip_FillEllipse(pGraphics, pBrush, x, y, w, h)
{
	Ptr := A_PtrSize ? "UPtr" : "UInt"

	return DllCall("gdiplus\GdipFillEllipse", Ptr, pGraphics, Ptr, pBrush, "float", x, "float", y, "float", w, "float", h)
}

;#####################################################################################

; Function				Gdip_FillRegion
; Description			This function uses a brush to fill a region in the Graphics of a bitmap
;
; pGraphics				Pointer to the Graphics of a bitmap
; pBrush				Pointer to a brush
; Region				Pointer to a Region
;
; return				status enumeration. 0 = success
;
; notes					You can create a region Gdip_CreateRegion() and then add to this

Gdip_FillRegion(pGraphics, pBrush, Region)
{
	Ptr := A_PtrSize ? "UPtr" : "UInt"

	return DllCall("gdiplus\GdipFillRegion", Ptr, pGraphics, Ptr, pBrush, Ptr, Region)
}

;#####################################################################################

; Function				Gdip_FillPath
; Description			This function uses a brush to fill a path in the Graphics of a bitmap
;
; pGraphics				Pointer to the Graphics of a bitmap
; pBrush				Pointer to a brush
; Region				Pointer to a Path
;
; return				status enumeration. 0 = success

Gdip_FillPath(pGraphics, pBrush, Path)
{
	Ptr := A_PtrSize ? "UPtr" : "UInt"

	return DllCall("gdiplus\GdipFillPath", Ptr, pGraphics, Ptr, pBrush, Ptr, Path)
}

;#####################################################################################

; Function				Gdip_DrawImagePointsRect
; Description			This function draws a bitmap into the Graphics of another bitmap and skews it
;
; pGraphics				Pointer to the Graphics of a bitmap
; pBitmap				Pointer to a bitmap to be drawn
; Points				Points passed as x1,y1|x2,y2|x3,y3 (3 points: top left, top right, bottom left) describing the drawing of the bitmap
; sx					x-coordinate of source upper-left corner
; sy					y-coordinate of source upper-left corner
; sw					width of source rectangle
; sh					height of source rectangle
; Matrix				a matrix used to alter image attributes when drawing
;
; return				status enumeration. 0 = success
;
; notes					if sx,sy,sw,sh are missed then the entire source bitmap will be used
;						Matrix can be omitted to just draw with no alteration to ARGB
;						Matrix may be passed as a digit from 0 - 1 to change just transparency
;						Matrix can be passed as a matrix with any delimiter

Gdip_DrawImagePointsRect(pGraphics, pBitmap, Points, sx="", sy="", sw="", sh="", Matrix=1)
{
	Ptr := A_PtrSize ? "UPtr" : "UInt"

	StringSplit, Points, Points, |
	VarSetCapacity(PointF, 8*Points0)
	Loop, %Points0%
	{
		StringSplit, Coord, Points%A_Index%, `,
		NumPut(Coord1, PointF, 8*(A_Index-1), "float"), NumPut(Coord2, PointF, (8*(A_Index-1))+4, "float")
	}

	if (Matrix&1 = "")
		ImageAttr := Gdip_SetImageAttributesColorMatrix(Matrix)
	else if (Matrix != 1)
		ImageAttr := Gdip_SetImageAttributesColorMatrix("1|0|0|0|0|0|1|0|0|0|0|0|1|0|0|0|0|0|" Matrix "|0|0|0|0|0|1")

	if (sx = "" && sy = "" && sw = "" && sh = "")
	{
		sx := 0, sy := 0
		sw := Gdip_GetImageWidth(pBitmap)
		sh := Gdip_GetImageHeight(pBitmap)
	}

	E := DllCall("gdiplus\GdipDrawImagePointsRect"
				, Ptr, pGraphics
				, Ptr, pBitmap
				, Ptr, &PointF
				, "int", Points0
				, "float", sx
				, "float", sy
				, "float", sw
				, "float", sh
				, "int", 2
				, Ptr, ImageAttr
				, Ptr, 0
				, Ptr, 0)
	if ImageAttr
		Gdip_DisposeImageAttributes(ImageAttr)
	return E
}

;#####################################################################################

; Function				Gdip_DrawImage
; Description			This function draws a bitmap into the Graphics of another bitmap
;
; pGraphics				Pointer to the Graphics of a bitmap
; pBitmap				Pointer to a bitmap to be drawn
; dx					x-coord of destination upper-left corner
; dy					y-coord of destination upper-left corner
; dw					width of destination image
; dh					height of destination image
; sx					x-coordinate of source upper-left corner
; sy					y-coordinate of source upper-left corner
; sw					width of source image
; sh					height of source image
; Matrix				a matrix used to alter image attributes when drawing
;
; return				status enumeration. 0 = success
;
; notes					if sx,sy,sw,sh are missed then the entire source bitmap will be used
;						Gdip_DrawImage performs faster
;						Matrix can be omitted to just draw with no alteration to ARGB
;						Matrix may be passed as a digit from 0 - 1 to change just transparency
;						Matrix can be passed as a matrix with any delimiter. For example:
;						MatrixBright=
;						(
;						1.5		|0		|0		|0		|0
;						0		|1.5	|0		|0		|0
;						0		|0		|1.5	|0		|0
;						0		|0		|0		|1		|0
;						0.05	|0.05	|0.05	|0		|1
;						)
;
; notes					MatrixBright = 1.5|0|0|0|0|0|1.5|0|0|0|0|0|1.5|0|0|0|0|0|1|0|0.05|0.05|0.05|0|1
;						MatrixGreyScale = 0.299|0.299|0.299|0|0|0.587|0.587|0.587|0|0|0.114|0.114|0.114|0|0|0|0|0|1|0|0|0|0|0|1
;						MatrixNegative = -1|0|0|0|0|0|-1|0|0|0|0|0|-1|0|0|0|0|0|1|0|0|0|0|0|1

Gdip_DrawImage(pGraphics, pBitmap, dx="", dy="", dw="", dh="", sx="", sy="", sw="", sh="", Matrix=1)
{
	Ptr := A_PtrSize ? "UPtr" : "UInt"

	if (Matrix&1 = "")
		ImageAttr := Gdip_SetImageAttributesColorMatrix(Matrix)
	else if (Matrix != 1)
		ImageAttr := Gdip_SetImageAttributesColorMatrix("1|0|0|0|0|0|1|0|0|0|0|0|1|0|0|0|0|0|" Matrix "|0|0|0|0|0|1")

	if (sx = "" && sy = "" && sw = "" && sh = "")
	{
		if (dx = "" && dy = "" && dw = "" && dh = "")
		{
			sx := dx := 0, sy := dy := 0
			sw := dw := Gdip_GetImageWidth(pBitmap)
			sh := dh := Gdip_GetImageHeight(pBitmap)
		}
		else
		{
			sx := sy := 0
			sw := Gdip_GetImageWidth(pBitmap)
			sh := Gdip_GetImageHeight(pBitmap)
		}
	}

	E := DllCall("gdiplus\GdipDrawImageRectRect"
				, Ptr, pGraphics
				, Ptr, pBitmap
				, "float", dx
				, "float", dy
				, "float", dw
				, "float", dh
				, "float", sx
				, "float", sy
				, "float", sw
				, "float", sh
				, "int", 2
				, Ptr, ImageAttr
				, Ptr, 0
				, Ptr, 0)
	if ImageAttr
		Gdip_DisposeImageAttributes(ImageAttr)
	return E
}

;#####################################################################################

; Function				Gdip_SetImageAttributesColorMatrix
; Description			This function creates an image matrix ready for drawing
;
; Matrix				a matrix used to alter image attributes when drawing
;						passed with any delimeter
;
; return				returns an image matrix on sucess or 0 if it fails
;
; notes					MatrixBright = 1.5|0|0|0|0|0|1.5|0|0|0|0|0|1.5|0|0|0|0|0|1|0|0.05|0.05|0.05|0|1
;						MatrixGreyScale = 0.299|0.299|0.299|0|0|0.587|0.587|0.587|0|0|0.114|0.114|0.114|0|0|0|0|0|1|0|0|0|0|0|1
;						MatrixNegative = -1|0|0|0|0|0|-1|0|0|0|0|0|-1|0|0|0|0|0|1|0|0|0|0|0|1

Gdip_SetImageAttributesColorMatrix(Matrix)
{
	Ptr := A_PtrSize ? "UPtr" : "UInt"

	VarSetCapacity(ColourMatrix, 100, 0)
	Matrix := RegExReplace(RegExReplace(Matrix, "^[^\d-\.]+([\d\.])", "$1", "", 1), "[^\d-\.]+", "|")
	StringSplit, Matrix, Matrix, |
	Loop, 25
	{
		Matrix := (Matrix%A_Index% != "") ? Matrix%A_Index% : Mod(A_Index-1, 6) ? 0 : 1
		NumPut(Matrix, ColourMatrix, (A_Index-1)*4, "float")
	}
	DllCall("gdiplus\GdipCreateImageAttributes", A_PtrSize ? "UPtr*" : "uint*", ImageAttr)
	DllCall("gdiplus\GdipSetImageAttributesColorMatrix", Ptr, ImageAttr, "int", 1, "int", 1, Ptr, &ColourMatrix, Ptr, 0, "int", 0)
	return ImageAttr
}

;#####################################################################################

; Function				Gdip_GraphicsFromImage
; Description			This function gets the graphics for a bitmap used for drawing functions
;
; pBitmap				Pointer to a bitmap to get the pointer to its graphics
;
; return				returns a pointer to the graphics of a bitmap
;
; notes					a bitmap can be drawn into the graphics of another bitmap

Gdip_GraphicsFromImage(pBitmap)
{
	DllCall("gdiplus\GdipGetImageGraphicsContext", A_PtrSize ? "UPtr" : "UInt", pBitmap, A_PtrSize ? "UPtr*" : "UInt*", pGraphics)
	return pGraphics
}

;#####################################################################################

; Function				Gdip_GraphicsFromHDC
; Description			This function gets the graphics from the handle to a device context
;
; hdc					This is the handle to the device context
;
; return				returns a pointer to the graphics of a bitmap
;
; notes					You can draw a bitmap into the graphics of another bitmap

Gdip_GraphicsFromHDC(hdc)
{
    DllCall("gdiplus\GdipCreateFromHDC", A_PtrSize ? "UPtr" : "UInt", hdc, A_PtrSize ? "UPtr*" : "UInt*", pGraphics)
    return pGraphics
}

;#####################################################################################

; Function				Gdip_GetDC
; Description			This function gets the device context of the passed Graphics
;
; hdc					This is the handle to the device context
;
; return				returns the device context for the graphics of a bitmap

Gdip_GetDC(pGraphics)
{
	DllCall("gdiplus\GdipGetDC", A_PtrSize ? "UPtr" : "UInt", pGraphics, A_PtrSize ? "UPtr*" : "UInt*", hdc)
	return hdc
}

;#####################################################################################

; Function				Gdip_ReleaseDC
; Description			This function releases a device context from use for further use
;
; pGraphics				Pointer to the graphics of a bitmap
; hdc					This is the handle to the device context
;
; return				status enumeration. 0 = success

Gdip_ReleaseDC(pGraphics, hdc)
{
	Ptr := A_PtrSize ? "UPtr" : "UInt"

	return DllCall("gdiplus\GdipReleaseDC", Ptr, pGraphics, Ptr, hdc)
}

;#####################################################################################

; Function				Gdip_GraphicsClear
; Description			Clears the graphics of a bitmap ready for further drawing
;
; pGraphics				Pointer to the graphics of a bitmap
; ARGB					The colour to clear the graphics to
;
; return				status enumeration. 0 = success
;
; notes					By default this will make the background invisible
;						Using clipping regions you can clear a particular area on the graphics rather than clearing the entire graphics

Gdip_GraphicsClear(pGraphics, ARGB=0x00ffffff)
{
    return DllCall("gdiplus\GdipGraphicsClear", A_PtrSize ? "UPtr" : "UInt", pGraphics, "int", ARGB)
}

;#####################################################################################

; Function				Gdip_BlurBitmap
; Description			Gives a pointer to a blurred bitmap from a pointer to a bitmap
;
; pBitmap				Pointer to a bitmap to be blurred
; Blur					The Amount to blur a bitmap by from 1 (least blur) to 100 (most blur)
;
; return				If the function succeeds, the return value is a pointer to the new blurred bitmap
;						-1 = The blur parameter is outside the range 1-100
;
; notes					This function will not dispose of the original bitmap

Gdip_BlurBitmap(pBitmap, Blur)
{
	if (Blur > 100) || (Blur < 1)
		return -1

	sWidth := Gdip_GetImageWidth(pBitmap), sHeight := Gdip_GetImageHeight(pBitmap)
	dWidth := sWidth//Blur, dHeight := sHeight//Blur

	pBitmap1 := Gdip_CreateBitmap(dWidth, dHeight)
	G1 := Gdip_GraphicsFromImage(pBitmap1)
	Gdip_SetInterpolationMode(G1, 7)
	Gdip_DrawImage(G1, pBitmap, 0, 0, dWidth, dHeight, 0, 0, sWidth, sHeight)

	Gdip_DeleteGraphics(G1)

	pBitmap2 := Gdip_CreateBitmap(sWidth, sHeight)
	G2 := Gdip_GraphicsFromImage(pBitmap2)
	Gdip_SetInterpolationMode(G2, 7)
	Gdip_DrawImage(G2, pBitmap1, 0, 0, sWidth, sHeight, 0, 0, dWidth, dHeight)

	Gdip_DeleteGraphics(G2)
	Gdip_DisposeImage(pBitmap1)
	return pBitmap2
}

;#####################################################################################

; Function:     		Gdip_SaveBitmapToFile
; Description:  		Saves a bitmap to a file in any supported format onto disk
;
; pBitmap				Pointer to a bitmap
; sOutput      			The name of the file that the bitmap will be saved to. Supported extensions are: .BMP,.DIB,.RLE,.JPG,.JPEG,.JPE,.JFIF,.GIF,.TIF,.TIFF,.PNG
; Quality      			If saving as jpg (.JPG,.JPEG,.JPE,.JFIF) then quality can be 1-100 with default at maximum quality
;
; return      			If the function succeeds, the return value is zero, otherwise:
;						-1 = Extension supplied is not a supported file format
;						-2 = Could not get a list of encoders on system
;						-3 = Could not find matching encoder for specified file format
;						-4 = Could not get WideChar name of output file
;						-5 = Could not save file to disk
;
; notes					This function will use the extension supplied from the sOutput parameter to determine the output format

Gdip_SaveBitmapToFile(pBitmap, sOutput, Quality=75)
{
	Ptr := A_PtrSize ? "UPtr" : "UInt"

	SplitPath, sOutput,,, Extension
	if Extension not in BMP,DIB,RLE,JPG,JPEG,JPE,JFIF,GIF,TIF,TIFF,PNG
		return -1
	Extension := "." Extension

	DllCall("gdiplus\GdipGetImageEncodersSize", "uint*", nCount, "uint*", nSize)
	VarSetCapacity(ci, nSize)
	DllCall("gdiplus\GdipGetImageEncoders", "uint", nCount, "uint", nSize, Ptr, &ci)
	if !(nCount && nSize)
		return -2

	If (A_IsUnicode){
		StrGet_Name := "StrGet"
		Loop, %nCount%
		{
			sString := %StrGet_Name%(NumGet(ci, (idx := (48+7*A_PtrSize)*(A_Index-1))+32+3*A_PtrSize), "UTF-16")
			if !InStr(sString, "*" Extension)
				continue

			pCodec := &ci+idx
			break
		}
	} else {
		Loop, %nCount%
		{
			Location := NumGet(ci, 76*(A_Index-1)+44)
			nSize := DllCall("WideCharToMultiByte", "uint", 0, "uint", 0, "uint", Location, "int", -1, "uint", 0, "int",  0, "uint", 0, "uint", 0)
			VarSetCapacity(sString, nSize)
			DllCall("WideCharToMultiByte", "uint", 0, "uint", 0, "uint", Location, "int", -1, "str", sString, "int", nSize, "uint", 0, "uint", 0)
			if !InStr(sString, "*" Extension)
				continue

			pCodec := &ci+76*(A_Index-1)
			break
		}
	}

	if !pCodec
		return -3

	if (Quality != 75)
	{
		Quality := (Quality < 0) ? 0 : (Quality > 100) ? 100 : Quality
		if Extension in .JPG,.JPEG,.JPE,.JFIF
		{
			DllCall("gdiplus\GdipGetEncoderParameterListSize", Ptr, pBitmap, Ptr, pCodec, "uint*", nSize)
			VarSetCapacity(EncoderParameters, nSize, 0)
			DllCall("gdiplus\GdipGetEncoderParameterList", Ptr, pBitmap, Ptr, pCodec, "uint", nSize, Ptr, &EncoderParameters)
			Loop, % NumGet(EncoderParameters, "UInt")      ;%
			{
				elem := (24+(A_PtrSize ? A_PtrSize : 4))*(A_Index-1) + 4 + (pad := A_PtrSize = 8 ? 4 : 0)
				if (NumGet(EncoderParameters, elem+16, "UInt") = 1) && (NumGet(EncoderParameters, elem+20, "UInt") = 6)
				{
					p := elem+&EncoderParameters-pad-4
					NumPut(Quality, NumGet(NumPut(4, NumPut(1, p+0)+20, "UInt")), "UInt")
					break
				}
			}
		}
	}

	if (!A_IsUnicode)
	{
		nSize := DllCall("MultiByteToWideChar", "uint", 0, "uint", 0, Ptr, &sOutput, "int", -1, Ptr, 0, "int", 0)
		VarSetCapacity(wOutput, nSize*2)
		DllCall("MultiByteToWideChar", "uint", 0, "uint", 0, Ptr, &sOutput, "int", -1, Ptr, &wOutput, "int", nSize)
		VarSetCapacity(wOutput, -1)
		if !VarSetCapacity(wOutput)
			return -4
		E := DllCall("gdiplus\GdipSaveImageToFile", Ptr, pBitmap, Ptr, &wOutput, Ptr, pCodec, "uint", p ? p : 0)
	}
	else
		E := DllCall("gdiplus\GdipSaveImageToFile", Ptr, pBitmap, Ptr, &sOutput, Ptr, pCodec, "uint", p ? p : 0)
	return E ? -5 : 0
}

;#####################################################################################

; Function				Gdip_GetPixel
; Description			Gets the ARGB of a pixel in a bitmap
;
; pBitmap				Pointer to a bitmap
; x						x-coordinate of the pixel
; y						y-coordinate of the pixel
;
; return				Returns the ARGB value of the pixel

Gdip_GetPixel(pBitmap, x, y)
{
	DllCall("gdiplus\GdipBitmapGetPixel", A_PtrSize ? "UPtr" : "UInt", pBitmap, "int", x, "int", y, "uint*", ARGB)
	return ARGB
}

;#####################################################################################

; Function				Gdip_SetPixel
; Description			Sets the ARGB of a pixel in a bitmap
;
; pBitmap				Pointer to a bitmap
; x						x-coordinate of the pixel
; y						y-coordinate of the pixel
;
; return				status enumeration. 0 = success

Gdip_SetPixel(pBitmap, x, y, ARGB)
{
   return DllCall("gdiplus\GdipBitmapSetPixel", A_PtrSize ? "UPtr" : "UInt", pBitmap, "int", x, "int", y, "int", ARGB)
}

;#####################################################################################

; Function				Gdip_GetImageWidth
; Description			Gives the width of a bitmap
;
; pBitmap				Pointer to a bitmap
;
; return				Returns the width in pixels of the supplied bitmap

Gdip_GetImageWidth(pBitmap)
{
   DllCall("gdiplus\GdipGetImageWidth", A_PtrSize ? "UPtr" : "UInt", pBitmap, "uint*", Width)
   return Width
}

;#####################################################################################

; Function				Gdip_GetImageHeight
; Description			Gives the height of a bitmap
;
; pBitmap				Pointer to a bitmap
;
; return				Returns the height in pixels of the supplied bitmap

Gdip_GetImageHeight(pBitmap)
{
   DllCall("gdiplus\GdipGetImageHeight", A_PtrSize ? "UPtr" : "UInt", pBitmap, "uint*", Height)
   return Height
}

;#####################################################################################

; Function				Gdip_GetDimensions
; Description			Gives the width and height of a bitmap
;
; pBitmap				Pointer to a bitmap
; Width					ByRef variable. This variable will be set to the width of the bitmap
; Height				ByRef variable. This variable will be set to the height of the bitmap
;
; return				No return value
;						Gdip_GetDimensions(pBitmap, ThisWidth, ThisHeight) will set ThisWidth to the width and ThisHeight to the height

Gdip_GetImageDimensions(pBitmap, ByRef Width, ByRef Height)
{
	Ptr := A_PtrSize ? "UPtr" : "UInt"
	DllCall("gdiplus\GdipGetImageWidth", Ptr, pBitmap, "uint*", Width)
	DllCall("gdiplus\GdipGetImageHeight", Ptr, pBitmap, "uint*", Height)
}

;#####################################################################################

Gdip_GetDimensions(pBitmap, ByRef Width, ByRef Height)
{
	Gdip_GetImageDimensions(pBitmap, Width, Height)
}

;#####################################################################################

Gdip_GetImagePixelFormat(pBitmap)
{
	DllCall("gdiplus\GdipGetImagePixelFormat", A_PtrSize ? "UPtr" : "UInt", pBitmap, A_PtrSize ? "UPtr*" : "UInt*", Format)
	return Format
}

;#####################################################################################

; Function				Gdip_GetDpiX
; Description			Gives the horizontal dots per inch of the graphics of a bitmap
;
; pBitmap				Pointer to a bitmap
; Width					ByRef variable. This variable will be set to the width of the bitmap
; Height				ByRef variable. This variable will be set to the height of the bitmap
;
; return				No return value
;						Gdip_GetDimensions(pBitmap, ThisWidth, ThisHeight) will set ThisWidth to the width and ThisHeight to the height

Gdip_GetDpiX(pGraphics)
{
	DllCall("gdiplus\GdipGetDpiX", A_PtrSize ? "UPtr" : "uint", pGraphics, "float*", dpix)
	return Round(dpix)
}

;#####################################################################################

Gdip_GetDpiY(pGraphics)
{
	DllCall("gdiplus\GdipGetDpiY", A_PtrSize ? "UPtr" : "uint", pGraphics, "float*", dpiy)
	return Round(dpiy)
}

;#####################################################################################

Gdip_GetImageHorizontalResolution(pBitmap)
{
	DllCall("gdiplus\GdipGetImageHorizontalResolution", A_PtrSize ? "UPtr" : "uint", pBitmap, "float*", dpix)
	return Round(dpix)
}

;#####################################################################################

Gdip_GetImageVerticalResolution(pBitmap)
{
	DllCall("gdiplus\GdipGetImageVerticalResolution", A_PtrSize ? "UPtr" : "uint", pBitmap, "float*", dpiy)
	return Round(dpiy)
}

;#####################################################################################

Gdip_BitmapSetResolution(pBitmap, dpix, dpiy)
{
	return DllCall("gdiplus\GdipBitmapSetResolution", A_PtrSize ? "UPtr" : "uint", pBitmap, "float", dpix, "float", dpiy)
}

;#####################################################################################

Gdip_CreateBitmapFromFile(sFile, IconNumber=1, IconSize="")
{
	Ptr := A_PtrSize ? "UPtr" : "UInt"
	, PtrA := A_PtrSize ? "UPtr*" : "UInt*"

	SplitPath, sFile,,, ext
	if ext in exe,dll
	{
		Sizes := IconSize ? IconSize : 256 "|" 128 "|" 64 "|" 48 "|" 32 "|" 16
		BufSize := 16 + (2*(A_PtrSize ? A_PtrSize : 4))

		VarSetCapacity(buf, BufSize, 0)
		Loop, Parse, Sizes, |
		{
			DllCall("PrivateExtractIcons", "str", sFile, "int", IconNumber-1, "int", A_LoopField, "int", A_LoopField, PtrA, hIcon, PtrA, 0, "uint", 1, "uint", 0)

			if !hIcon
				continue

			if !DllCall("GetIconInfo", Ptr, hIcon, Ptr, &buf)
			{
				DestroyIcon(hIcon)
				continue
			}

			hbmMask  := NumGet(buf, 12 + ((A_PtrSize ? A_PtrSize : 4) - 4))
			hbmColor := NumGet(buf, 12 + ((A_PtrSize ? A_PtrSize : 4) - 4) + (A_PtrSize ? A_PtrSize : 4))
			if !(hbmColor && DllCall("GetObject", Ptr, hbmColor, "int", BufSize, Ptr, &buf))
			{
				DestroyIcon(hIcon)
				continue
			}
			break
		}
		if !hIcon
			return -1

		Width := NumGet(buf, 4, "int"), Height := NumGet(buf, 8, "int")
		hbm := CreateDIBSection(Width, -Height), hdc := CreateCompatibleDC(), obm := SelectObject(hdc, hbm)
		if !DllCall("DrawIconEx", Ptr, hdc, "int", 0, "int", 0, Ptr, hIcon, "uint", Width, "uint", Height, "uint", 0, Ptr, 0, "uint", 3)
		{
			DestroyIcon(hIcon)
			return -2
		}

		VarSetCapacity(dib, 104)
		DllCall("GetObject", Ptr, hbm, "int", A_PtrSize = 8 ? 104 : 84, Ptr, &dib) ; sizeof(DIBSECTION) = 76+2*(A_PtrSize=8?4:0)+2*A_PtrSize
		Stride := NumGet(dib, 12, "Int"), Bits := NumGet(dib, 20 + (A_PtrSize = 8 ? 4 : 0)) ; padding
		DllCall("gdiplus\GdipCreateBitmapFromScan0", "int", Width, "int", Height, "int", Stride, "int", 0x26200A, Ptr, Bits, PtrA, pBitmapOld)
		pBitmap := Gdip_CreateBitmap(Width, Height)
		G := Gdip_GraphicsFromImage(pBitmap)
		, Gdip_DrawImage(G, pBitmapOld, 0, 0, Width, Height, 0, 0, Width, Height)
		SelectObject(hdc, obm), DeleteObject(hbm), DeleteDC(hdc)
		Gdip_DeleteGraphics(G), Gdip_DisposeImage(pBitmapOld)
		DestroyIcon(hIcon)
	}
	else
	{
		if (!A_IsUnicode)
		{
			VarSetCapacity(wFile, 1024)
			DllCall("kernel32\MultiByteToWideChar", "uint", 0, "uint", 0, Ptr, &sFile, "int", -1, Ptr, &wFile, "int", 512)
			DllCall("gdiplus\GdipCreateBitmapFromFile", Ptr, &wFile, PtrA, pBitmap)
		}
		else
			DllCall("gdiplus\GdipCreateBitmapFromFile", Ptr, &sFile, PtrA, pBitmap)
	}

	return pBitmap
}

;#####################################################################################

Gdip_CreateBitmapFromHBITMAP(hBitmap, Palette=0)
{
	Ptr := A_PtrSize ? "UPtr" : "UInt"

	DllCall("gdiplus\GdipCreateBitmapFromHBITMAP", Ptr, hBitmap, Ptr, Palette, A_PtrSize ? "UPtr*" : "uint*", pBitmap)
	return pBitmap
}

;#####################################################################################

Gdip_CreateHBITMAPFromBitmap(pBitmap, Background=0xffffffff)
{
	DllCall("gdiplus\GdipCreateHBITMAPFromBitmap", A_PtrSize ? "UPtr" : "UInt", pBitmap, A_PtrSize ? "UPtr*" : "uint*", hbm, "int", Background)
	return hbm
}

;#####################################################################################

Gdip_CreateBitmapFromHICON(hIcon)
{
	DllCall("gdiplus\GdipCreateBitmapFromHICON", A_PtrSize ? "UPtr" : "UInt", hIcon, A_PtrSize ? "UPtr*" : "uint*", pBitmap)
	return pBitmap
}

;#####################################################################################

Gdip_CreateHICONFromBitmap(pBitmap)
{
	DllCall("gdiplus\GdipCreateHICONFromBitmap", A_PtrSize ? "UPtr" : "UInt", pBitmap, A_PtrSize ? "UPtr*" : "uint*", hIcon)
	return hIcon
}

;#####################################################################################

Gdip_CreateBitmap(Width, Height, Format=0x26200A)
{
    DllCall("gdiplus\GdipCreateBitmapFromScan0", "int", Width, "int", Height, "int", 0, "int", Format, A_PtrSize ? "UPtr" : "UInt", 0, A_PtrSize ? "UPtr*" : "uint*", pBitmap)
    Return pBitmap
}

;#####################################################################################

Gdip_CreateBitmapFromClipboard()
{
	Ptr := A_PtrSize ? "UPtr" : "UInt"

	if !DllCall("OpenClipboard", Ptr, 0)
		return -1
	if !DllCall("IsClipboardFormatAvailable", "uint", 8)
		return -2
	if !hBitmap := DllCall("GetClipboardData", "uint", 2, Ptr)
		return -3
	if !pBitmap := Gdip_CreateBitmapFromHBITMAP(hBitmap)
		return -4
	if !DllCall("CloseClipboard")
		return -5
	DeleteObject(hBitmap)
	return pBitmap
}

;#####################################################################################

Gdip_SetBitmapToClipboard(pBitmap)
{
	Ptr := A_PtrSize ? "UPtr" : "UInt"
	off1 := A_PtrSize = 8 ? 52 : 44, off2 := A_PtrSize = 8 ? 32 : 24
	hBitmap := Gdip_CreateHBITMAPFromBitmap(pBitmap)
	DllCall("GetObject", Ptr, hBitmap, "int", VarSetCapacity(oi, A_PtrSize = 8 ? 104 : 84, 0), Ptr, &oi)
	hdib := DllCall("GlobalAlloc", "uint", 2, Ptr, 40+NumGet(oi, off1, "UInt"), Ptr)
	pdib := DllCall("GlobalLock", Ptr, hdib, Ptr)
	DllCall("RtlMoveMemory", Ptr, pdib, Ptr, &oi+off2, Ptr, 40)
	DllCall("RtlMoveMemory", Ptr, pdib+40, Ptr, NumGet(oi, off2 - (A_PtrSize ? A_PtrSize : 4), Ptr), Ptr, NumGet(oi, off1, "UInt"))
	DllCall("GlobalUnlock", Ptr, hdib)
	DllCall("DeleteObject", Ptr, hBitmap)
	DllCall("OpenClipboard", Ptr, 0)
	DllCall("EmptyClipboard")
	DllCall("SetClipboardData", "uint", 8, Ptr, hdib)
	DllCall("CloseClipboard")
}

;#####################################################################################

Gdip_CloneBitmapArea(pBitmap, x, y, w, h, Format=0x26200A)
{
	DllCall("gdiplus\GdipCloneBitmapArea"
					, "float", x
					, "float", y
					, "float", w
					, "float", h
					, "int", Format
					, A_PtrSize ? "UPtr" : "UInt", pBitmap
					, A_PtrSize ? "UPtr*" : "UInt*", pBitmapDest)
	return pBitmapDest
}

;#####################################################################################
; Create resources
;#####################################################################################

Gdip_CreatePen(ARGB, w)
{
   DllCall("gdiplus\GdipCreatePen1", "UInt", ARGB, "float", w, "int", 2, A_PtrSize ? "UPtr*" : "UInt*", pPen)
   return pPen
}

;#####################################################################################

Gdip_CreatePenFromBrush(pBrush, w)
{
	DllCall("gdiplus\GdipCreatePen2", A_PtrSize ? "UPtr" : "UInt", pBrush, "float", w, "int", 2, A_PtrSize ? "UPtr*" : "UInt*", pPen)
	return pPen
}

;#####################################################################################

Gdip_BrushCreateSolid(ARGB=0xff000000)
{
	DllCall("gdiplus\GdipCreateSolidFill", "UInt", ARGB, A_PtrSize ? "UPtr*" : "UInt*", pBrush)
	return pBrush
}

;#####################################################################################

; HatchStyleHorizontal = 0
; HatchStyleVertical = 1
; HatchStyleForwardDiagonal = 2
; HatchStyleBackwardDiagonal = 3
; HatchStyleCross = 4
; HatchStyleDiagonalCross = 5
; HatchStyle05Percent = 6
; HatchStyle10Percent = 7
; HatchStyle20Percent = 8
; HatchStyle25Percent = 9
; HatchStyle30Percent = 10
; HatchStyle40Percent = 11
; HatchStyle50Percent = 12
; HatchStyle60Percent = 13
; HatchStyle70Percent = 14
; HatchStyle75Percent = 15
; HatchStyle80Percent = 16
; HatchStyle90Percent = 17
; HatchStyleLightDownwardDiagonal = 18
; HatchStyleLightUpwardDiagonal = 19
; HatchStyleDarkDownwardDiagonal = 20
; HatchStyleDarkUpwardDiagonal = 21
; HatchStyleWideDownwardDiagonal = 22
; HatchStyleWideUpwardDiagonal = 23
; HatchStyleLightVertical = 24
; HatchStyleLightHorizontal = 25
; HatchStyleNarrowVertical = 26
; HatchStyleNarrowHorizontal = 27
; HatchStyleDarkVertical = 28
; HatchStyleDarkHorizontal = 29
; HatchStyleDashedDownwardDiagonal = 30
; HatchStyleDashedUpwardDiagonal = 31
; HatchStyleDashedHorizontal = 32
; HatchStyleDashedVertical = 33
; HatchStyleSmallConfetti = 34
; HatchStyleLargeConfetti = 35
; HatchStyleZigZag = 36
; HatchStyleWave = 37
; HatchStyleDiagonalBrick = 38
; HatchStyleHorizontalBrick = 39
; HatchStyleWeave = 40
; HatchStylePlaid = 41
; HatchStyleDivot = 42
; HatchStyleDottedGrid = 43
; HatchStyleDottedDiamond = 44
; HatchStyleShingle = 45
; HatchStyleTrellis = 46
; HatchStyleSphere = 47
; HatchStyleSmallGrid = 48
; HatchStyleSmallCheckerBoard = 49
; HatchStyleLargeCheckerBoard = 50
; HatchStyleOutlinedDiamond = 51
; HatchStyleSolidDiamond = 52
; HatchStyleTotal = 53
Gdip_BrushCreateHatch(ARGBfront, ARGBback, HatchStyle=0)
{
	DllCall("gdiplus\GdipCreateHatchBrush", "int", HatchStyle, "UInt", ARGBfront, "UInt", ARGBback, A_PtrSize ? "UPtr*" : "UInt*", pBrush)
	return pBrush
}

;#####################################################################################

Gdip_CreateTextureBrush(pBitmap, WrapMode=1, x=0, y=0, w="", h="")
{
	Ptr := A_PtrSize ? "UPtr" : "UInt"
	, PtrA := A_PtrSize ? "UPtr*" : "UInt*"

	if !(w && h)
		DllCall("gdiplus\GdipCreateTexture", Ptr, pBitmap, "int", WrapMode, PtrA, pBrush)
	else
		DllCall("gdiplus\GdipCreateTexture2", Ptr, pBitmap, "int", WrapMode, "float", x, "float", y, "float", w, "float", h, PtrA, pBrush)
	return pBrush
}

;#####################################################################################

; WrapModeTile = 0
; WrapModeTileFlipX = 1
; WrapModeTileFlipY = 2
; WrapModeTileFlipXY = 3
; WrapModeClamp = 4
Gdip_CreateLineBrush(x1, y1, x2, y2, ARGB1, ARGB2, WrapMode=1)
{
	Ptr := A_PtrSize ? "UPtr" : "UInt"

	CreatePointF(PointF1, x1, y1), CreatePointF(PointF2, x2, y2)
	DllCall("gdiplus\GdipCreateLineBrush", Ptr, &PointF1, Ptr, &PointF2, "Uint", ARGB1, "Uint", ARGB2, "int", WrapMode, A_PtrSize ? "UPtr*" : "UInt*", LGpBrush)
	return LGpBrush
}

;#####################################################################################

; LinearGradientModeHorizontal = 0
; LinearGradientModeVertical = 1
; LinearGradientModeForwardDiagonal = 2
; LinearGradientModeBackwardDiagonal = 3
Gdip_CreateLineBrushFromRect(x, y, w, h, ARGB1, ARGB2, LinearGradientMode=1, WrapMode=1)
{
	CreateRectF(RectF, x, y, w, h)
	DllCall("gdiplus\GdipCreateLineBrushFromRect", A_PtrSize ? "UPtr" : "UInt", &RectF, "int", ARGB1, "int", ARGB2, "int", LinearGradientMode, "int", WrapMode, A_PtrSize ? "UPtr*" : "UInt*", LGpBrush)
	return LGpBrush
}

;#####################################################################################

Gdip_CloneBrush(pBrush)
{
	DllCall("gdiplus\GdipCloneBrush", A_PtrSize ? "UPtr" : "UInt", pBrush, A_PtrSize ? "UPtr*" : "UInt*", pBrushClone)
	return pBrushClone
}

;#####################################################################################
; Delete resources
;#####################################################################################

Gdip_DeletePen(pPen)
{
   return DllCall("gdiplus\GdipDeletePen", A_PtrSize ? "UPtr" : "UInt", pPen)
}

;#####################################################################################

Gdip_DeleteBrush(pBrush)
{
   return DllCall("gdiplus\GdipDeleteBrush", A_PtrSize ? "UPtr" : "UInt", pBrush)
}

;#####################################################################################

Gdip_DisposeImage(pBitmap)
{
   return DllCall("gdiplus\GdipDisposeImage", A_PtrSize ? "UPtr" : "UInt", pBitmap)
}

;#####################################################################################

Gdip_DeleteGraphics(pGraphics)
{
   return DllCall("gdiplus\GdipDeleteGraphics", A_PtrSize ? "UPtr" : "UInt", pGraphics)
}

;#####################################################################################

Gdip_DisposeImageAttributes(ImageAttr)
{
	return DllCall("gdiplus\GdipDisposeImageAttributes", A_PtrSize ? "UPtr" : "UInt", ImageAttr)
}

;#####################################################################################

Gdip_DeleteFont(hFont)
{
   return DllCall("gdiplus\GdipDeleteFont", A_PtrSize ? "UPtr" : "UInt", hFont)
}

;#####################################################################################

Gdip_DeleteStringFormat(hFormat)
{
   return DllCall("gdiplus\GdipDeleteStringFormat", A_PtrSize ? "UPtr" : "UInt", hFormat)
}

;#####################################################################################

Gdip_DeleteFontFamily(hFamily)
{
   return DllCall("gdiplus\GdipDeleteFontFamily", A_PtrSize ? "UPtr" : "UInt", hFamily)
}

;#####################################################################################

Gdip_DeleteMatrix(Matrix)
{
   return DllCall("gdiplus\GdipDeleteMatrix", A_PtrSize ? "UPtr" : "UInt", Matrix)
}

;#####################################################################################
; Text functions
;#####################################################################################

Gdip_TextToGraphics(pGraphics, Text, Options, Font="Arial", Width="", Height="", Measure=0)
{
	IWidth := Width, IHeight:= Height

	RegExMatch(Options, "i)X([\-\d\.]+)(p*)", xpos)
	RegExMatch(Options, "i)Y([\-\d\.]+)(p*)", ypos)
	RegExMatch(Options, "i)W([\-\d\.]+)(p*)", Width)
	RegExMatch(Options, "i)H([\-\d\.]+)(p*)", Height)
	RegExMatch(Options, "i)C(?!(entre|enter))([a-f\d]+)", Colour)
	RegExMatch(Options, "i)Top|Up|Bottom|Down|vCentre|vCenter", vPos)
	RegExMatch(Options, "i)NoWrap", NoWrap)
	RegExMatch(Options, "i)R(\d)", Rendering)
	RegExMatch(Options, "i)S(\d+)(p*)", Size)

	if !Gdip_DeleteBrush(Gdip_CloneBrush(Colour2))
		PassBrush := 1, pBrush := Colour2

	if !(IWidth && IHeight) && (xpos2 || ypos2 || Width2 || Height2 || Size2)
		return -1

	Style := 0, Styles := "Regular|Bold|Italic|BoldItalic|Underline|Strikeout"
	Loop, Parse, Styles, |
	{
		if RegExMatch(Options, "\b" A_loopField)
		Style |= (A_LoopField != "StrikeOut") ? (A_Index-1) : 8
	}

	Align := 0, Alignments := "Near|Left|Centre|Center|Far|Right"
	Loop, Parse, Alignments, |
	{
		if RegExMatch(Options, "\b" A_loopField)
			Align |= A_Index//2.1      ; 0|0|1|1|2|2
	}

	xpos := (xpos1 != "") ? xpos2 ? IWidth*(xpos1/100) : xpos1 : 0
	ypos := (ypos1 != "") ? ypos2 ? IHeight*(ypos1/100) : ypos1 : 0
	Width := Width1 ? Width2 ? IWidth*(Width1/100) : Width1 : IWidth
	Height := Height1 ? Height2 ? IHeight*(Height1/100) : Height1 : IHeight
	if !PassBrush
		Colour := "0x" (Colour2 ? Colour2 : "ff000000")
	Rendering := ((Rendering1 >= 0) && (Rendering1 <= 5)) ? Rendering1 : 4
	Size := (Size1 > 0) ? Size2 ? IHeight*(Size1/100) : Size1 : 12

	hFamily := Gdip_FontFamilyCreate(Font)
	hFont := Gdip_FontCreate(hFamily, Size, Style)
	FormatStyle := NoWrap ? 0x4000 | 0x1000 : 0x4000
	hFormat := Gdip_StringFormatCreate(FormatStyle)
	pBrush := PassBrush ? pBrush : Gdip_BrushCreateSolid(Colour)
	if !(hFamily && hFont && hFormat && pBrush && pGraphics)
		return !pGraphics ? -2 : !hFamily ? -3 : !hFont ? -4 : !hFormat ? -5 : !pBrush ? -6 : 0

	CreateRectF(RC, xpos, ypos, Width, Height)
	Gdip_SetStringFormatAlign(hFormat, Align)
	Gdip_SetTextRenderingHint(pGraphics, Rendering)
	ReturnRC := Gdip_MeasureString(pGraphics, Text, hFont, hFormat, RC)

	if vPos
	{
		StringSplit, ReturnRC, ReturnRC, |

		if (vPos = "vCentre") || (vPos = "vCenter")
			ypos += (Height-ReturnRC4)//2
		else if (vPos = "Top") || (vPos = "Up")
			ypos := 0
		else if (vPos = "Bottom") || (vPos = "Down")
			ypos := Height-ReturnRC4

		CreateRectF(RC, xpos, ypos, Width, ReturnRC4)
		ReturnRC := Gdip_MeasureString(pGraphics, Text, hFont, hFormat, RC)
	}

	if !Measure
		E := Gdip_DrawString(pGraphics, Text, hFont, hFormat, pBrush, RC)

	if !PassBrush
		Gdip_DeleteBrush(pBrush)
	Gdip_DeleteStringFormat(hFormat)
	Gdip_DeleteFont(hFont)
	Gdip_DeleteFontFamily(hFamily)
	return E ? E : ReturnRC
}

;#####################################################################################

Gdip_DrawString(pGraphics, sString, hFont, hFormat, pBrush, ByRef RectF)
{
	Ptr := A_PtrSize ? "UPtr" : "UInt"

	if (!A_IsUnicode)
	{
		nSize := DllCall("MultiByteToWideChar", "uint", 0, "uint", 0, Ptr, &sString, "int", -1, Ptr, 0, "int", 0)
		VarSetCapacity(wString, nSize*2)
		DllCall("MultiByteToWideChar", "uint", 0, "uint", 0, Ptr, &sString, "int", -1, Ptr, &wString, "int", nSize)
	}

	return DllCall("gdiplus\GdipDrawString"
					, Ptr, pGraphics
					, Ptr, A_IsUnicode ? &sString : &wString
					, "int", -1
					, Ptr, hFont
					, Ptr, &RectF
					, Ptr, hFormat
					, Ptr, pBrush)
}

;#####################################################################################

Gdip_MeasureString(pGraphics, sString, hFont, hFormat, ByRef RectF)
{
	Ptr := A_PtrSize ? "UPtr" : "UInt"

	VarSetCapacity(RC, 16)
	if !A_IsUnicode
	{
		nSize := DllCall("MultiByteToWideChar", "uint", 0, "uint", 0, Ptr, &sString, "int", -1, "uint", 0, "int", 0)
		VarSetCapacity(wString, nSize*2)
		DllCall("MultiByteToWideChar", "uint", 0, "uint", 0, Ptr, &sString, "int", -1, Ptr, &wString, "int", nSize)
	}

	DllCall("gdiplus\GdipMeasureString"
					, Ptr, pGraphics
					, Ptr, A_IsUnicode ? &sString : &wString
					, "int", -1
					, Ptr, hFont
					, Ptr, &RectF
					, Ptr, hFormat
					, Ptr, &RC
					, "uint*", Chars
					, "uint*", Lines)

	return &RC ? NumGet(RC, 0, "float") "|" NumGet(RC, 4, "float") "|" NumGet(RC, 8, "float") "|" NumGet(RC, 12, "float") "|" Chars "|" Lines : 0
}

; Near = 0
; Center = 1
; Far = 2
Gdip_SetStringFormatAlign(hFormat, Align)
{
   return DllCall("gdiplus\GdipSetStringFormatAlign", A_PtrSize ? "UPtr" : "UInt", hFormat, "int", Align)
}

; StringFormatFlagsDirectionRightToLeft    = 0x00000001
; StringFormatFlagsDirectionVertical       = 0x00000002
; StringFormatFlagsNoFitBlackBox           = 0x00000004
; StringFormatFlagsDisplayFormatControl    = 0x00000020
; StringFormatFlagsNoFontFallback          = 0x00000400
; StringFormatFlagsMeasureTrailingSpaces   = 0x00000800
; StringFormatFlagsNoWrap                  = 0x00001000
; StringFormatFlagsLineLimit               = 0x00002000
; StringFormatFlagsNoClip                  = 0x00004000
Gdip_StringFormatCreate(Format=0, Lang=0)
{
   DllCall("gdiplus\GdipCreateStringFormat", "int", Format, "int", Lang, A_PtrSize ? "UPtr*" : "UInt*", hFormat)
   return hFormat
}

; Regular = 0
; Bold = 1
; Italic = 2
; BoldItalic = 3
; Underline = 4
; Strikeout = 8
Gdip_FontCreate(hFamily, Size, Style=0)
{
   DllCall("gdiplus\GdipCreateFont", A_PtrSize ? "UPtr" : "UInt", hFamily, "float", Size, "int", Style, "int", 0, A_PtrSize ? "UPtr*" : "UInt*", hFont)
   return hFont
}

Gdip_FontFamilyCreate(Font)
{
	Ptr := A_PtrSize ? "UPtr" : "UInt"

	if (!A_IsUnicode)
	{
		nSize := DllCall("MultiByteToWideChar", "uint", 0, "uint", 0, Ptr, &Font, "int", -1, "uint", 0, "int", 0)
		VarSetCapacity(wFont, nSize*2)
		DllCall("MultiByteToWideChar", "uint", 0, "uint", 0, Ptr, &Font, "int", -1, Ptr, &wFont, "int", nSize)
	}

	DllCall("gdiplus\GdipCreateFontFamilyFromName"
					, Ptr, A_IsUnicode ? &Font : &wFont
					, "uint", 0
					, A_PtrSize ? "UPtr*" : "UInt*", hFamily)

	return hFamily
}

;#####################################################################################
; Matrix functions
;#####################################################################################

Gdip_CreateAffineMatrix(m11, m12, m21, m22, x, y)
{
   DllCall("gdiplus\GdipCreateMatrix2", "float", m11, "float", m12, "float", m21, "float", m22, "float", x, "float", y, A_PtrSize ? "UPtr*" : "UInt*", Matrix)
   return Matrix
}

Gdip_CreateMatrix()
{
   DllCall("gdiplus\GdipCreateMatrix", A_PtrSize ? "UPtr*" : "UInt*", Matrix)
   return Matrix
}

;#####################################################################################
; GraphicsPath functions
;#####################################################################################

; Alternate = 0
; Winding = 1
Gdip_CreatePath(BrushMode=0)
{
	DllCall("gdiplus\GdipCreatePath", "int", BrushMode, A_PtrSize ? "UPtr*" : "UInt*", Path)
	return Path
}

Gdip_AddPathEllipse(Path, x, y, w, h)
{
	return DllCall("gdiplus\GdipAddPathEllipse", A_PtrSize ? "UPtr" : "UInt", Path, "float", x, "float", y, "float", w, "float", h)
}

Gdip_AddPathPolygon(Path, Points)
{
	Ptr := A_PtrSize ? "UPtr" : "UInt"

	StringSplit, Points, Points, |
	VarSetCapacity(PointF, 8*Points0)
	Loop, %Points0%
	{
		StringSplit, Coord, Points%A_Index%, `,
		NumPut(Coord1, PointF, 8*(A_Index-1), "float"), NumPut(Coord2, PointF, (8*(A_Index-1))+4, "float")
	}

	return DllCall("gdiplus\GdipAddPathPolygon", Ptr, Path, Ptr, &PointF, "int", Points0)
}

Gdip_DeletePath(Path)
{
	return DllCall("gdiplus\GdipDeletePath", A_PtrSize ? "UPtr" : "UInt", Path)
}

;#####################################################################################
; Quality functions
;#####################################################################################

; SystemDefault = 0
; SingleBitPerPixelGridFit = 1
; SingleBitPerPixel = 2
; AntiAliasGridFit = 3
; AntiAlias = 4
Gdip_SetTextRenderingHint(pGraphics, RenderingHint)
{
	return DllCall("gdiplus\GdipSetTextRenderingHint", A_PtrSize ? "UPtr" : "UInt", pGraphics, "int", RenderingHint)
}

; Default = 0
; LowQuality = 1
; HighQuality = 2
; Bilinear = 3
; Bicubic = 4
; NearestNeighbor = 5
; HighQualityBilinear = 6
; HighQualityBicubic = 7
Gdip_SetInterpolationMode(pGraphics, InterpolationMode)
{
   return DllCall("gdiplus\GdipSetInterpolationMode", A_PtrSize ? "UPtr" : "UInt", pGraphics, "int", InterpolationMode)
}

; Default = 0
; HighSpeed = 1
; HighQuality = 2
; None = 3
; AntiAlias = 4
Gdip_SetSmoothingMode(pGraphics, SmoothingMode)
{
   return DllCall("gdiplus\GdipSetSmoothingMode", A_PtrSize ? "UPtr" : "UInt", pGraphics, "int", SmoothingMode)
}

; CompositingModeSourceOver = 0 (blended)
; CompositingModeSourceCopy = 1 (overwrite)
Gdip_SetCompositingMode(pGraphics, CompositingMode=0)
{
   return DllCall("gdiplus\GdipSetCompositingMode", A_PtrSize ? "UPtr" : "UInt", pGraphics, "int", CompositingMode)
}

;#####################################################################################
; Extra functions
;#####################################################################################

Gdip_Startup()
{
	Ptr := A_PtrSize ? "UPtr" : "UInt"

	if !DllCall("GetModuleHandle", "str", "gdiplus", Ptr)
		DllCall("LoadLibrary", "str", "gdiplus")
	VarSetCapacity(si, A_PtrSize = 8 ? 24 : 16, 0), si := Chr(1)
	DllCall("gdiplus\GdiplusStartup", A_PtrSize ? "UPtr*" : "uint*", pToken, Ptr, &si, Ptr, 0)
	return pToken
}

Gdip_Shutdown(pToken)
{
	Ptr := A_PtrSize ? "UPtr" : "UInt"

	DllCall("gdiplus\GdiplusShutdown", Ptr, pToken)
	if hModule := DllCall("GetModuleHandle", "str", "gdiplus", Ptr)
		DllCall("FreeLibrary", Ptr, hModule)
	return 0
}

; Prepend = 0; The new operation is applied before the old operation.
; Append = 1; The new operation is applied after the old operation.
Gdip_RotateWorldTransform(pGraphics, Angle, MatrixOrder=0)
{
	return DllCall("gdiplus\GdipRotateWorldTransform", A_PtrSize ? "UPtr" : "UInt", pGraphics, "float", Angle, "int", MatrixOrder)
}

Gdip_ScaleWorldTransform(pGraphics, x, y, MatrixOrder=0)
{
	return DllCall("gdiplus\GdipScaleWorldTransform", A_PtrSize ? "UPtr" : "UInt", pGraphics, "float", x, "float", y, "int", MatrixOrder)
}

Gdip_TranslateWorldTransform(pGraphics, x, y, MatrixOrder=0)
{
	return DllCall("gdiplus\GdipTranslateWorldTransform", A_PtrSize ? "UPtr" : "UInt", pGraphics, "float", x, "float", y, "int", MatrixOrder)
}

Gdip_ResetWorldTransform(pGraphics)
{
	return DllCall("gdiplus\GdipResetWorldTransform", A_PtrSize ? "UPtr" : "UInt", pGraphics)
}

Gdip_GetRotatedTranslation(Width, Height, Angle, ByRef xTranslation, ByRef yTranslation)
{
	pi := 3.14159, TAngle := Angle*(pi/180)

	Bound := (Angle >= 0) ? Mod(Angle, 360) : 360-Mod(-Angle, -360)
	if ((Bound >= 0) && (Bound <= 90))
		xTranslation := Height*Sin(TAngle), yTranslation := 0
	else if ((Bound > 90) && (Bound <= 180))
		xTranslation := (Height*Sin(TAngle))-(Width*Cos(TAngle)), yTranslation := -Height*Cos(TAngle)
	else if ((Bound > 180) && (Bound <= 270))
		xTranslation := -(Width*Cos(TAngle)), yTranslation := -(Height*Cos(TAngle))-(Width*Sin(TAngle))
	else if ((Bound > 270) && (Bound <= 360))
		xTranslation := 0, yTranslation := -Width*Sin(TAngle)
}

Gdip_GetRotatedDimensions(Width, Height, Angle, ByRef RWidth, ByRef RHeight)
{
	pi := 3.14159, TAngle := Angle*(pi/180)
	if !(Width && Height)
		return -1
	RWidth := Ceil(Abs(Width*Cos(TAngle))+Abs(Height*Sin(TAngle)))
	RHeight := Ceil(Abs(Width*Sin(TAngle))+Abs(Height*Cos(Tangle)))
}

; RotateNoneFlipNone   = 0
; Rotate90FlipNone     = 1
; Rotate180FlipNone    = 2
; Rotate270FlipNone    = 3
; RotateNoneFlipX      = 4
; Rotate90FlipX        = 5
; Rotate180FlipX       = 6
; Rotate270FlipX       = 7
; RotateNoneFlipY      = Rotate180FlipX
; Rotate90FlipY        = Rotate270FlipX
; Rotate180FlipY       = RotateNoneFlipX
; Rotate270FlipY       = Rotate90FlipX
; RotateNoneFlipXY     = Rotate180FlipNone
; Rotate90FlipXY       = Rotate270FlipNone
; Rotate180FlipXY      = RotateNoneFlipNone
; Rotate270FlipXY      = Rotate90FlipNone

Gdip_ImageRotateFlip(pBitmap, RotateFlipType=1)
{
	return DllCall("gdiplus\GdipImageRotateFlip", A_PtrSize ? "UPtr" : "UInt", pBitmap, "int", RotateFlipType)
}

; Replace = 0
; Intersect = 1
; Union = 2
; Xor = 3
; Exclude = 4
; Complement = 5
Gdip_SetClipRect(pGraphics, x, y, w, h, CombineMode=0)
{
   return DllCall("gdiplus\GdipSetClipRect",  A_PtrSize ? "UPtr" : "UInt", pGraphics, "float", x, "float", y, "float", w, "float", h, "int", CombineMode)
}

Gdip_SetClipPath(pGraphics, Path, CombineMode=0)
{
	Ptr := A_PtrSize ? "UPtr" : "UInt"
	return DllCall("gdiplus\GdipSetClipPath", Ptr, pGraphics, Ptr, Path, "int", CombineMode)
}

Gdip_ResetClip(pGraphics)
{
   return DllCall("gdiplus\GdipResetClip", A_PtrSize ? "UPtr" : "UInt", pGraphics)
}

Gdip_GetClipRegion(pGraphics)
{
	Region := Gdip_CreateRegion()
	DllCall("gdiplus\GdipGetClip", A_PtrSize ? "UPtr" : "UInt", pGraphics, "UInt*", Region)
	return Region
}

Gdip_SetClipRegion(pGraphics, Region, CombineMode=0)
{
	Ptr := A_PtrSize ? "UPtr" : "UInt"

	return DllCall("gdiplus\GdipSetClipRegion", Ptr, pGraphics, Ptr, Region, "int", CombineMode)
}

Gdip_CreateRegion()
{
	DllCall("gdiplus\GdipCreateRegion", "UInt*", Region)
	return Region
}

Gdip_DeleteRegion(Region)
{
	return DllCall("gdiplus\GdipDeleteRegion", A_PtrSize ? "UPtr" : "UInt", Region)
}

;#####################################################################################
; BitmapLockBits
;#####################################################################################

Gdip_LockBits(pBitmap, x, y, w, h, ByRef Stride, ByRef Scan0, ByRef BitmapData, LockMode = 3, PixelFormat = 0x26200a)
{
	Ptr := A_PtrSize ? "UPtr" : "UInt"

	CreateRect(Rect, x, y, w, h)
	VarSetCapacity(BitmapData, 16+2*(A_PtrSize ? A_PtrSize : 4), 0)
	E := DllCall("Gdiplus\GdipBitmapLockBits", Ptr, pBitmap, Ptr, &Rect, "uint", LockMode, "int", PixelFormat, Ptr, &BitmapData)
	Stride := NumGet(BitmapData, 8, "Int")
	Scan0 := NumGet(BitmapData, 16, Ptr)
	return E
}

;#####################################################################################

Gdip_UnlockBits(pBitmap, ByRef BitmapData)
{
	Ptr := A_PtrSize ? "UPtr" : "UInt"

	return DllCall("Gdiplus\GdipBitmapUnlockBits", Ptr, pBitmap, Ptr, &BitmapData)
}

;#####################################################################################

Gdip_SetLockBitPixel(ARGB, Scan0, x, y, Stride)
{
	Numput(ARGB, Scan0+0, (x*4)+(y*Stride), "UInt")
}

;#####################################################################################

Gdip_GetLockBitPixel(Scan0, x, y, Stride)
{
	return NumGet(Scan0+0, (x*4)+(y*Stride), "UInt")
}

;#####################################################################################

Gdip_PixelateBitmap(pBitmap, ByRef pBitmapOut, BlockSize)
{
	static PixelateBitmap

	Ptr := A_PtrSize ? "UPtr" : "UInt"

	if (!PixelateBitmap)
	{
		if A_PtrSize != 8 ; x86 machine code
		MCode_PixelateBitmap =
		(LTrim Join
		558BEC83EC3C8B4514538B5D1C99F7FB56578BC88955EC894DD885C90F8E830200008B451099F7FB8365DC008365E000894DC88955F08945E833FF897DD4
		397DE80F8E160100008BCB0FAFCB894DCC33C08945F88945FC89451C8945143BD87E608B45088D50028BC82BCA8BF02BF2418945F48B45E02955F4894DC4
		8D0CB80FAFCB03CA895DD08BD1895DE40FB64416030145140FB60201451C8B45C40FB604100145FC8B45F40FB604020145F883C204FF4DE475D6034D18FF
		4DD075C98B4DCC8B451499F7F98945148B451C99F7F989451C8B45FC99F7F98945FC8B45F899F7F98945F885DB7E648B450C8D50028BC82BCA83C103894D
		C48BC82BCA41894DF48B4DD48945E48B45E02955E48D0C880FAFCB03CA895DD08BD18BF38A45148B7DC48804178A451C8B7DF488028A45FC8804178A45F8
		8B7DE488043A83C2044E75DA034D18FF4DD075CE8B4DCC8B7DD447897DD43B7DE80F8CF2FEFFFF837DF0000F842C01000033C08945F88945FC89451C8945
		148945E43BD87E65837DF0007E578B4DDC034DE48B75E80FAF4D180FAFF38B45088D500203CA8D0CB18BF08BF88945F48B45F02BF22BFA2955F48945CC0F
		B6440E030145140FB60101451C0FB6440F010145FC8B45F40FB604010145F883C104FF4DCC75D8FF45E4395DE47C9B8B4DF00FAFCB85C9740B8B451499F7
		F9894514EB048365140033F63BCE740B8B451C99F7F989451CEB0389751C3BCE740B8B45FC99F7F98945FCEB038975FC3BCE740B8B45F899F7F98945F8EB
		038975F88975E43BDE7E5A837DF0007E4C8B4DDC034DE48B75E80FAF4D180FAFF38B450C8D500203CA8D0CB18BF08BF82BF22BFA2BC28B55F08955CC8A55
		1488540E038A551C88118A55FC88540F018A55F888140183C104FF4DCC75DFFF45E4395DE47CA68B45180145E0015DDCFF4DC80F8594FDFFFF8B451099F7
		FB8955F08945E885C00F8E450100008B45EC0FAFC38365DC008945D48B45E88945CC33C08945F88945FC89451C8945148945103945EC7E6085DB7E518B4D
		D88B45080FAFCB034D108D50020FAF4D18034DDC8BF08BF88945F403CA2BF22BFA2955F4895DC80FB6440E030145140FB60101451C0FB6440F010145FC8B
		45F40FB604080145F883C104FF4DC875D8FF45108B45103B45EC7CA08B4DD485C9740B8B451499F7F9894514EB048365140033F63BCE740B8B451C99F7F9
		89451CEB0389751C3BCE740B8B45FC99F7F98945FCEB038975FC3BCE740B8B45F899F7F98945F8EB038975F88975103975EC7E5585DB7E468B4DD88B450C
		0FAFCB034D108D50020FAF4D18034DDC8BF08BF803CA2BF22BFA2BC2895DC88A551488540E038A551C88118A55FC88540F018A55F888140183C104FF4DC8
		75DFFF45108B45103B45EC7CAB8BC3C1E0020145DCFF4DCC0F85CEFEFFFF8B4DEC33C08945F88945FC89451C8945148945103BC87E6C3945F07E5C8B4DD8
		8B75E80FAFCB034D100FAFF30FAF4D188B45088D500203CA8D0CB18BF08BF88945F48B45F02BF22BFA2955F48945C80FB6440E030145140FB60101451C0F
		B6440F010145FC8B45F40FB604010145F883C104FF4DC875D833C0FF45108B4DEC394D107C940FAF4DF03BC874068B451499F7F933F68945143BCE740B8B
		451C99F7F989451CEB0389751C3BCE740B8B45FC99F7F98945FCEB038975FC3BCE740B8B45F899F7F98945F8EB038975F88975083975EC7E63EB0233F639
		75F07E4F8B4DD88B75E80FAFCB034D080FAFF30FAF4D188B450C8D500203CA8D0CB18BF08BF82BF22BFA2BC28B55F08955108A551488540E038A551C8811
		8A55FC88540F018A55F888140883C104FF4D1075DFFF45088B45083B45EC7C9F5F5E33C05BC9C21800
		)
		else ; x64 machine code
		MCode_PixelateBitmap =
		(LTrim Join
		4489442418488954241048894C24085355565741544155415641574883EC28418BC1448B8C24980000004C8BDA99488BD941F7F9448BD0448BFA8954240C
		448994248800000085C00F8E9D020000418BC04533E4458BF299448924244C8954241041F7F933C9898C24980000008BEA89542404448BE889442408EB05
		4C8B5C24784585ED0F8E1A010000458BF1418BFD48897C2418450FAFF14533D233F633ED4533E44533ED4585C97E5B4C63BC2490000000418D040A410FAF
		C148984C8D441802498BD9498BD04D8BD90FB642010FB64AFF4403E80FB60203E90FB64AFE4883C2044403E003F149FFCB75DE4D03C748FFCB75D0488B7C
		24188B8C24980000004C8B5C2478418BC59941F7FE448BE8418BC49941F7FE448BE08BC59941F7FE8BE88BC69941F7FE8BF04585C97E4048639C24900000
		004103CA4D8BC1410FAFC94863C94A8D541902488BCA498BC144886901448821408869FF408871FE4883C10448FFC875E84803D349FFC875DA8B8C249800
		0000488B5C24704C8B5C24784183C20448FFCF48897C24180F850AFFFFFF8B6C2404448B2424448B6C24084C8B74241085ED0F840A01000033FF33DB4533
		DB4533D24533C04585C97E53488B74247085ED7E42438D0C04418BC50FAF8C2490000000410FAFC18D04814863C8488D5431028BCD0FB642014403D00FB6
		024883C2044403D80FB642FB03D80FB642FA03F848FFC975DE41FFC0453BC17CB28BCD410FAFC985C9740A418BC299F7F98BF0EB0233F685C9740B418BC3
		99F7F9448BD8EB034533DB85C9740A8BC399F7F9448BD0EB034533D285C9740A8BC799F7F9448BC0EB034533C033D24585C97E4D4C8B74247885ED7E3841
		8D0C14418BC50FAF8C2490000000410FAFC18D04814863C84A8D4431028BCD40887001448818448850FF448840FE4883C00448FFC975E8FFC2413BD17CBD
		4C8B7424108B8C2498000000038C2490000000488B5C24704503E149FFCE44892424898C24980000004C897424100F859EFDFFFF448B7C240C448B842480
		000000418BC09941F7F98BE8448BEA89942498000000896C240C85C00F8E3B010000448BAC2488000000418BCF448BF5410FAFC9898C248000000033FF33
		ED33F64533DB4533D24533C04585FF7E524585C97E40418BC5410FAFC14103C00FAF84249000000003C74898488D541802498BD90FB642014403D00FB602
		4883C2044403D80FB642FB03F00FB642FA03E848FFCB75DE488B5C247041FFC0453BC77CAE85C9740B418BC299F7F9448BE0EB034533E485C9740A418BC3
		99F7F98BD8EB0233DB85C9740A8BC699F7F9448BD8EB034533DB85C9740A8BC599F7F9448BD0EB034533D24533C04585FF7E4E488B4C24784585C97E3541
		8BC5410FAFC14103C00FAF84249000000003C74898488D540802498BC144886201881A44885AFF448852FE4883C20448FFC875E941FFC0453BC77CBE8B8C
		2480000000488B5C2470418BC1C1E00203F849FFCE0F85ECFEFFFF448BAC24980000008B6C240C448BA4248800000033FF33DB4533DB4533D24533C04585
		FF7E5A488B7424704585ED7E48418BCC8BC5410FAFC94103C80FAF8C2490000000410FAFC18D04814863C8488D543102418BCD0FB642014403D00FB60248
		83C2044403D80FB642FB03D80FB642FA03F848FFC975DE41FFC0453BC77CAB418BCF410FAFCD85C9740A418BC299F7F98BF0EB0233F685C9740B418BC399
		F7F9448BD8EB034533DB85C9740A8BC399F7F9448BD0EB034533D285C9740A8BC799F7F9448BC0EB034533C033D24585FF7E4E4585ED7E42418BCC8BC541
		0FAFC903CA0FAF8C2490000000410FAFC18D04814863C8488B442478488D440102418BCD40887001448818448850FF448840FE4883C00448FFC975E8FFC2
		413BD77CB233C04883C428415F415E415D415C5F5E5D5BC3
		)

		VarSetCapacity(PixelateBitmap, StrLen(MCode_PixelateBitmap)//2)
		Loop % StrLen(MCode_PixelateBitmap)//2		;%
			NumPut("0x" SubStr(MCode_PixelateBitmap, (2*A_Index)-1, 2), PixelateBitmap, A_Index-1, "UChar")
		DllCall("VirtualProtect", Ptr, &PixelateBitmap, Ptr, VarSetCapacity(PixelateBitmap), "uint", 0x40, A_PtrSize ? "UPtr*" : "UInt*", 0)
	}

	Gdip_GetImageDimensions(pBitmap, Width, Height)

	if (Width != Gdip_GetImageWidth(pBitmapOut) || Height != Gdip_GetImageHeight(pBitmapOut))
		return -1
	if (BlockSize > Width || BlockSize > Height)
		return -2

	E1 := Gdip_LockBits(pBitmap, 0, 0, Width, Height, Stride1, Scan01, BitmapData1)
	E2 := Gdip_LockBits(pBitmapOut, 0, 0, Width, Height, Stride2, Scan02, BitmapData2)
	if (E1 || E2)
		return -3

	E := DllCall(&PixelateBitmap, Ptr, Scan01, Ptr, Scan02, "int", Width, "int", Height, "int", Stride1, "int", BlockSize)

	Gdip_UnlockBits(pBitmap, BitmapData1), Gdip_UnlockBits(pBitmapOut, BitmapData2)
	return 0
}

;#####################################################################################

Gdip_ToARGB(A, R, G, B)
{
	return (A << 24) | (R << 16) | (G << 8) | B
}

;#####################################################################################

Gdip_FromARGB(ARGB, ByRef A, ByRef R, ByRef G, ByRef B)
{
	A := (0xff000000 & ARGB) >> 24
	R := (0x00ff0000 & ARGB) >> 16
	G := (0x0000ff00 & ARGB) >> 8
	B := 0x000000ff & ARGB
}

;#####################################################################################

Gdip_AFromARGB(ARGB)
{
	return (0xff000000 & ARGB) >> 24
}

;#####################################################################################

Gdip_RFromARGB(ARGB)
{
	return (0x00ff0000 & ARGB) >> 16
}

;#####################################################################################

Gdip_GFromARGB(ARGB)
{
	return (0x0000ff00 & ARGB) >> 8
}

;#####################################################################################

Gdip_BFromARGB(ARGB)
{
	return 0x000000ff & ARGB
}

;#####################################################################################

StrGetB(Address, Length=-1, Encoding=0)
{
	; Flexible parameter handling:
	if Length is not integer
	Encoding := Length,  Length := -1

	; Check for obvious errors.
	if (Address+0 < 1024)
		return

	; Ensure 'Encoding' contains a numeric identifier.
	if Encoding = UTF-16
		Encoding = 1200
	else if Encoding = UTF-8
		Encoding = 65001
	else if SubStr(Encoding,1,2)="CP"
		Encoding := SubStr(Encoding,3)

	if !Encoding ; "" or 0
	{
		; No conversion necessary, but we might not want the whole string.
		if (Length == -1)
			Length := DllCall("lstrlen", "uint", Address)
		VarSetCapacity(String, Length)
		DllCall("lstrcpyn", "str", String, "uint", Address, "int", Length + 1)
	}
	else if Encoding = 1200 ; UTF-16
	{
		char_count := DllCall("WideCharToMultiByte", "uint", 0, "uint", 0x400, "uint", Address, "int", Length, "uint", 0, "uint", 0, "uint", 0, "uint", 0)
		VarSetCapacity(String, char_count)
		DllCall("WideCharToMultiByte", "uint", 0, "uint", 0x400, "uint", Address, "int", Length, "str", String, "int", char_count, "uint", 0, "uint", 0)
	}
	else if Encoding is integer
	{
		; Convert from target encoding to UTF-16 then to the active code page.
		char_count := DllCall("MultiByteToWideChar", "uint", Encoding, "uint", 0, "uint", Address, "int", Length, "uint", 0, "int", 0)
		VarSetCapacity(String, char_count * 2)
		char_count := DllCall("MultiByteToWideChar", "uint", Encoding, "uint", 0, "uint", Address, "int", Length, "uint", &String, "int", char_count * 2)
		String := StrGetB(&String, char_count, 1200)
	}

	return String
}

class GlobalContainer  {
   __New(name)  {
      for wnd in ComObjCreate("Shell.Application").Windows  {
         if (wnd.GetProperty("container_name") = name)
            this._obj := wnd
      }
      if !this._obj  {
         this._obj := ComObjGet("new:{C08AFD90-F2A1-11D1-8455-00A0C91F3880}")
         this._obj.PutProperty("container_name", name)
      }
   }

   __Set(prop, value)  {
      if prop not in _obj,_userFunc
        try
          Return this._obj.PutProperty(prop, value)
        catch{}
   }

   __Get(prop)  {
      if (prop != "_obj")
        try
           Return this._obj.GetProperty(prop)
        catch{}
   }

   Connect(userFunc)  {
      this._userFunc := userFunc.Bind(this)
      ComObjConnect(this._obj, this)
   }

   PropertyChange(prop, obj)  {
      this._userFunc.Call(prop)
   }

   Quit()  {
        try
          this._obj.Quit()
        catch{}
   }
}

/*
    A basic memory class by RHCP:
https://github.com/Kalamity/classMemory
    This is a wrapper for commonly used read and write memory functions.
    It also contains a variety of pattern scan functions.
    This class allows scripts to read/write integers and strings of various types.
    Pointer addresses can easily be read/written by passing the base address and offsets to the various read/write functions.

    Process handles are kept open between reads. This increases speed.
    However, if a program closes/restarts then the process handle will become invalid
    and you will need to re-open another handle (blank/destroy the object and recreate it)
    isHandleValid() can be used to check if a handle is still active/valid.

    read(), readString(), write(), and writeString() can be used to read and write memory addresses respectively.

    readRaw() can be used to dump large chunks of memory, this is considerably faster when
    reading data from a large structure compared to repeated calls to read().
    For example, reading a single UInt takes approximately the same amount of time as reading 1000 bytes via readRaw().
    Although, most people wouldn't notice the performance difference. This does however require you
    to retrieve the values using AHK's numget()/strGet() from the dumped memory.

    In a similar fashion writeRaw() allows a buffer to be be written in a single operation.

    When the new operator is used this class returns an object which can be used to read that process's
    memory space.To read another process simply create another object.

    Process handles are automatically closed when the script exits/restarts or when you free the object.

    **Notes:
        This was initially written for 32 bit target processes, however the various read/write functions
        should now completely support pointers in 64 bit target applications. The only caveat is that the AHK exe must also be 64 bit.
        If AHK is 32 bit and the target application is 64 bit you can still read, write, and use pointers, so long as the addresses
        fit inside a 4 byte pointer, i.e. The maximum address is limited to the 32 bit range.

        The various pattern scan functions are intended to be used on 32 bit target applications, however:
            - A 32 bit AHK script can perform pattern scans on a 32 bit target application.
            - A 32 bit AHK script may be able to perform pattern scans on a 64 bit process, providing the addresses fall within the 32 bit range.
            - A 64 bit AHK script should be able to perform pattern scans on a 32 or 64 bit target application without issue.

        If the target process has admin privileges, then the AHK script will also require admin privileges.

        AHK doesn't support unsigned 64bit ints, you can however read them as Int64 and interpret negative values as large numbers.


    Commonly used methods:
        read()
        readString()
        readRaw()
        write()
        writeString()
        writeBytes()
        writeRaw()
        isHandleValid()
        getModuleBaseAddress()

    Less commonly used methods:
        getProcessBaseAddress()
        hexStringToPattern()
        stringToPattern()
        modulePatternScan()
        processPatternScan()
        addressPatternScan()
        rawPatternScan()
        getModules()
        numberOfBytesRead()
        numberOfBytesWritten()
        suspend()
        resume()

    Internal methods: (some may be useful when directly called)
        getAddressFromOffsets() ; This will return the final memory address of a pointer. This is useful if the pointed address only changes on startup or map/level change and you want to eliminate the overhead associated with pointers.
        isTargetProcess64Bit()
        pointer()
        GetModuleFileNameEx()
        EnumProcessModulesEx()
        GetModuleInformation()
        getNeedleFromAOBPattern()
        virtualQueryEx()
        patternScan()
        bufferScanForMaskedPattern()
        openProcess()
        closeHandle()

    Useful properties:  (Do not modify the values of these properties - they are set automatically)
        baseAddress             ; The base address of the target process
        hProcess                ; The handle to the target process
        PID                     ; The PID of the target process
        currentProgram          ; The string the user used to identify the target process e.g. "ahk_exe calc.exe"
        isTarget64bit           ; True if target process is 64 bit, otherwise false
        readStringLastError     ; Used to check for success/failure when reading a string

     Useful editable properties:
        insertNullTerminator    ; Determines if a null terminator is inserted when writing strings.


    Usage:

        ; **Note: If you wish to try this calc example, consider using the 32 bit version of calc.exe -
        ;         which is in C:\Windows\SysWOW64\calc.exe on win7 64 bit systems.

        ; The contents of this file can be copied directly into your script. Alternately, you can copy the classMemory.ahk file into your library folder,
        ; in which case you will need to use the #include directive in your script i.e.
            #Include <classMemory>

        ; You can use this code to check if you have installed the class correctly.
            if (_ClassMemory.__Class != "_ClassMemory")
            {
                msgbox class memory not correctly installed. Or the (global class) variable "_ClassMemory" has been overwritten
                ExitApp
            }

        ; Open a process with sufficient access to read and write memory addresses (this is required before you can use the other functions)
        ; You only need to do this once. But if the process closes/restarts, then you will need to perform this step again. Refer to the notes section below.
        ; Also, if the target process is running as admin, then the script will also require admin rights!
        ; Note: The program identifier can be any AHK windowTitle i.e.ahk_exe, ahk_class, ahk_pid, or simply the window title.
        ; hProcessCopy is an optional variable in which the opened handled is stored.

            calc := new _ClassMemory("ahk_exe calc.exe", "", hProcessCopy)

        ; Check if the above method was successful.
            if !isObject(calc)
            {
                msgbox failed to open a handle
                if (hProcessCopy = 0)
                    msgbox The program isn't running (not found) or you passed an incorrect program identifier parameter. In some cases _ClassMemory.setSeDebugPrivilege() may be required.
                else if (hProcessCopy = "")
                    msgbox OpenProcess failed. If the target process has admin rights, then the script also needs to be ran as admin. _ClassMemory.setSeDebugPrivilege() may also be required. Consult A_LastError for more information.
                ExitApp
            }

        ; Get the process's base address.
        ; When using the new operator this property is automatically set to the result of getModuleBaseAddress() or getProcessBaseAddress();
        ; the specific method used depends on the bitness of the target application and AHK.
        ; If the returned address is incorrect and the target application is 64 bit, but AHK is 32 bit, try using the 64 bit version of AHK.
            msgbox % calc.BaseAddress

        ; Get the base address of a specific module.
            msgbox % calc.getModuleBaseAddress("GDI32.dll")

        ; The rest of these examples are just for illustration (the addresses specified are probably not valid).
        ; You can use cheat engine to find real addresses to read and write for testing purposes.

        ; Write 1234 as a UInt at address 0x0016CB60.
            calc.write(0x0016CB60, 1234, "UInt")

        ; Read a UInt.
            value := calc.read(0x0016CB60, "UInt")

        ; Read a pointer with offsets 0x20 and 0x15C which points to a UChar.
            value := calc.read(pointerBase, "UChar", 0x20, 0x15C)

        ; Note: read(), readString(), readRaw(), write(), writeString(), and writeRaw() all support pointers/offsets.
        ; An array of pointers can be passed directly, i.e.
            arrayPointerOffsets := [0x20, 0x15C]
            value := calc.read(pointerBase, "UChar", arrayPointerOffsets*)
        ; Or they can be entered manually.
            value := calc.read(pointerBase, "UChar", 0x20, 0x15C)
        ; You can also pass all the parameters directly, i.e.
            aMyPointer := [pointerBase, "UChar", 0x20, 0x15C]
            value := calc.read(aMyPointer*)


        ; Read a utf-16 null terminated string of unknown size at address 0x1234556 - the function will read until the null terminator is found or something goes wrong.
            string := calc.readString(0x1234556, length := 0, encoding := "utf-16")

        ; Read a utf-8 encoded string which is 12 bytes long at address 0x1234556.
            string := calc.readString(0x1234556, 12)

        ; By default a null terminator is included at the end of written strings for writeString().
        ; The nullterminator property can be used to change this.
            _ClassMemory.insertNullTerminator := False ; This will change the property for all processes
            calc.insertNullTerminator := False ; Changes the property for just this process


    Notes:
        If the target process exits and then starts again (or restarts) you will need to free the derived object and then use the new operator to create a new object i.e.
        calc := [] ; or calc := "" ; free the object. This is actually optional if using the line below, as the line below would free the previous derived object calc prior to initialising the new copy.
        calc := new _ClassMemory("ahk_exe calc.exe") ; Create a new derived object to read calc's memory.
        isHandleValid() can be used to check if a target process has closed or restarted.
*/

class _ClassMemory
{
    ; List of useful accessible values. Some of these inherited values (the non objects) are set when the new operator is used.
    static baseAddress, hProcess, PID, currentProgram
    , insertNullTerminator := True
    , readStringLastError := False
    , isTarget64bit := False
    , ptrType := "UInt"
    , aTypeSize := {    "UChar":    1,  "Char":     1
                    ,   "UShort":   2,  "Short":    2
                    ,   "UInt":     4,  "Int":      4
                    ,   "UFloat":   4,  "Float":    4
                    ,   "Int64":    8,  "Double":   8}
    , aRights := {  "PROCESS_ALL_ACCESS": 0x001F0FFF
                ,   "PROCESS_CREATE_PROCESS": 0x0080
                ,   "PROCESS_CREATE_THREAD": 0x0002
                ,   "PROCESS_DUP_HANDLE": 0x0040
                ,   "PROCESS_QUERY_INFORMATION": 0x0400
                ,   "PROCESS_QUERY_LIMITED_INFORMATION": 0x1000
                ,   "PROCESS_SET_INFORMATION": 0x0200
                ,   "PROCESS_SET_QUOTA": 0x0100
                ,   "PROCESS_SUSPEND_RESUME": 0x0800
                ,   "PROCESS_TERMINATE": 0x0001
                ,   "PROCESS_VM_OPERATION": 0x0008
                ,   "PROCESS_VM_READ": 0x0010
                ,   "PROCESS_VM_WRITE": 0x0020
                ,   "SYNCHRONIZE": 0x00100000}


    ; Method:    __new(program, dwDesiredAccess := "", byRef handle := "", windowMatchMode := 3)
    ; Example:  derivedObject := new _ClassMemory("ahk_exe calc.exe")
    ;           This is the first method which should be called when trying to access a program's memory.
    ;           If the process is successfully opened, an object is returned which can be used to read that processes memory space.
    ;           [derivedObject].hProcess stores the opened handle.
    ;           If the target process closes and re-opens, simply free the derived object and use the new operator again to open a new handle.
    ; Parameters:
    ;   program             The program to be opened. This can be any AHK windowTitle identifier, such as
    ;                       ahk_exe, ahk_class, ahk_pid, or simply the window title. e.g. "ahk_exe calc.exe" or "Calculator".
    ;                       It's safer not to use the window title, as some things can have the same window title e.g. an open folder called "Starcraft II"
    ;                       would have the same window title as the game itself.
    ;                       *'DetectHiddenWindows, On' is required for hidden windows*
    ;   dwDesiredAccess     The access rights requested when opening the process.
    ;                       If this parameter is null the process will be opened with the following rights
    ;                       PROCESS_QUERY_INFORMATION, PROCESS_VM_OPERATION, PROCESS_VM_READ, PROCESS_VM_WRITE, & SYNCHRONIZE
    ;                       This access level is sufficient to allow all of the methods in this class to work.
    ;                       Specific process access rights are listed here http://msdn.microsoft.com/en-us/library/windows/desktop/ms684880(v=vs.85).aspx
    ;   handle (Output)     Optional variable in which a copy of the opened processes handle will be stored.
    ;                       Values:
    ;                           Null    OpenProcess failed. The script may need to be run with admin rights admin,
    ;                                   and/or with the use of _ClassMemory.setSeDebugPrivilege(). Consult A_LastError for more information.
    ;                           0       The program isn't running (not found) or you passed an incorrect program identifier parameter.
    ;                                   In some cases _ClassMemory.setSeDebugPrivilege() may be required.
    ;                           Positive Integer    A handle to the process. (Success)
    ;   windowMatchMode -   Determines the matching mode used when finding the program (windowTitle).
    ;                       The default value is 3 i.e. an exact match. Refer to AHK's setTitleMathMode for more information.
    ; Return Values:
    ;   Object  On success an object is returned which can be used to read the processes memory.
    ;   Null    Failure. A_LastError and the optional handle parameter can be consulted for more information.


    __new(program, dwDesiredAccess := "", byRef handle := "", windowMatchMode := 3)
    {
        if this.PID := handle := this.findPID(program, windowMatchMode) ; set handle to 0 if program not found
        {
            ; This default access level is sufficient to read and write memory addresses, and to perform pattern scans.
            ; if the program is run using admin privileges, then this script will also need admin privileges
            if dwDesiredAccess is not integer
                dwDesiredAccess := this.aRights.PROCESS_QUERY_INFORMATION | this.aRights.PROCESS_VM_OPERATION | this.aRights.PROCESS_VM_READ | this.aRights.PROCESS_VM_WRITE
            dwDesiredAccess |= this.aRights.SYNCHRONIZE ; add SYNCHRONIZE to all handles to allow isHandleValid() to work

            if this.hProcess := handle := this.OpenProcess(this.PID, dwDesiredAccess) ; NULL/Blank if failed to open process for some reason
            {
                this.pNumberOfBytesRead := DllCall("GlobalAlloc", "UInt", 0x0040, "Ptr", A_PtrSize, "Ptr") ; 0x0040 initialise to 0
                this.pNumberOfBytesWritten := DllCall("GlobalAlloc", "UInt", 0x0040, "Ptr", A_PtrSize, "Ptr") ; initialise to 0

                this.readStringLastError := False
                this.currentProgram := program
                if this.isTarget64bit := this.isTargetProcess64Bit(this.PID, this.hProcess, dwDesiredAccess)
                    this.ptrType := "Int64"
                else this.ptrType := "UInt" ; If false or Null (fails) assume 32bit

                ; if script is 64 bit, getModuleBaseAddress() should always work
                ; if target app is truly 32 bit, then getModuleBaseAddress()
                ; will work when script is 32 bit
                if (A_PtrSize != 4 || !this.isTarget64bit)
                    this.BaseAddress := this.getModuleBaseAddress()

                ; If the above failed or wasn't called, fall back to alternate method
                if this.BaseAddress < 0 || !this.BaseAddress
                    this.BaseAddress := this.getProcessBaseAddress(program, windowMatchMode)

                return this
            }
        }
        return
    }

    __delete()
    {
        this.closeHandle(this.hProcess)
        if this.pNumberOfBytesRead
            DllCall("GlobalFree", "Ptr", this.pNumberOfBytesRead)
        if this.pNumberOfBytesWritten
            DllCall("GlobalFree", "Ptr", this.pNumberOfBytesWritten)
        return
    }

    version()
    {
        return 2.92
    }

    findPID(program, windowMatchMode := "3")
    {
        ; If user passes an AHK_PID, don't bother searching. There are cases where searching windows for PIDs
        ; wont work - console apps
        if RegExMatch(program, "i)\s*AHK_PID\s+(0x[[:xdigit:]]+|\d+)", pid)
            return pid1
        if windowMatchMode
        {
            ; This is a string and will not contain the 0x prefix
            mode := A_TitleMatchMode
            ; remove hex prefix as SetTitleMatchMode will throw a run time error. This will occur if integer mode is set to hex and user passed an int (unquoted)
            StringReplace, windowMatchMode, windowMatchMode, 0x
            SetTitleMatchMode, %windowMatchMode%
        }
        WinGet, pid, pid, %program%
        if windowMatchMode
            SetTitleMatchMode, %mode%    ; In case executed in autoexec

        ; If use 'ahk_exe test.exe' and winget fails (which can happen when setSeDebugPrivilege is required),
        ; try using the process command. When it fails due to setSeDebugPrivilege, setSeDebugPrivilege will still be required to openProcess
        ; This should also work for apps without windows.
        if (!pid && RegExMatch(program, "i)\bAHK_EXE\b\s*(.*)", fileName))
        {
            ; remove any trailing AHK_XXX arguments
            filename := RegExReplace(filename1, "i)\bahk_(class|id|pid|group)\b.*", "")
            filename := trim(filename)    ; extra spaces will make process command fail
            ; AHK_EXE can be the full path, so just get filename
            SplitPath, fileName , fileName
            if (fileName) ; if filename blank, scripts own pid is returned
            {
                process, Exist, %fileName%
                pid := ErrorLevel
            }
        }

        return pid ? pid : 0 ; PID is null on fail, return 0
    }
    ; Method:   isHandleValid()
    ;           This method provides a means to check if the internal process handle is still valid
    ;           or in other words, the specific target application instance (which you have been reading from)
    ;           has closed or restarted.
    ;           For example, if the target application closes or restarts the handle will become invalid
    ;           and subsequent calls to this method will return false.
    ;
    ; Return Values:
    ;   True    The handle is valid.
    ;   False   The handle is not valid.
    ;
    ; Notes:
    ;   This operation requires a handle with SYNCHRONIZE access rights.
    ;   All handles, even user specified ones are opened with the SYNCHRONIZE access right.

    isHandleValid()
    {
        return 0x102 = DllCall("WaitForSingleObject", "Ptr", this.hProcess, "UInt", 0)
        ; WaitForSingleObject return values
        ; -1 if called with null hProcess (sets lastError to 6 - invalid handle)
        ; 258 / 0x102 WAIT_TIMEOUT - if handle is valid (process still running)
        ; 0  WAIT_OBJECT_0 - if process has terminated
    }

    ; Method:   openProcess(PID, dwDesiredAccess)
    ;           ***Note:    This is an internal method which shouldn't be called directly unless you absolutely know what you are doing.
    ;                       This is because the new operator, in addition to calling this method also sets other values
    ;                       which are required for the other methods to work correctly.
    ; Parameters:
    ;   PID                 The Process ID of the target process.
    ;   dwDesiredAccess     The access rights requested when opening the process.
    ;                       Specific process access rights are listed here http://msdn.microsoft.com/en-us/library/windows/desktop/ms684880(v=vs.85).aspx
    ; Return Values:
    ;   Null/blank          OpenProcess failed. If the target process has admin rights, then the script also needs to be ran as admin.
    ;                       _ClassMemory.setSeDebugPrivilege() may also be required.
    ;   Positive integer    A handle to the process.

    openProcess(PID, dwDesiredAccess)
    {
        r := DllCall("OpenProcess", "UInt", dwDesiredAccess, "Int", False, "UInt", PID, "Ptr")
        ; if it fails with 0x5 ERROR_ACCESS_DENIED, try enabling privilege ... lots of users never try this.
        ; there may be other errors which also require DebugPrivilege....
        if (!r && A_LastError = 5)
        {
            this.setSeDebugPrivilege(true) ; no harm in enabling it if it is already enabled by user
            if (r2 := DllCall("OpenProcess", "UInt", dwDesiredAccess, "Int", False, "UInt", PID, "Ptr"))
                return r2
            DllCall("SetLastError", "UInt", 5) ; restore original error if it doesnt work
        }
        ; If fails with 0x5 ERROR_ACCESS_DENIED (when setSeDebugPrivilege() is req.), the func. returns 0 rather than null!! Set it to null.
        ; If fails for another reason, then it is null.
        return r ? r : ""
    }

    ; Method:   closeHandle(hProcess)
    ;           Note:   This is an internal method which is automatically called when the script exits or the derived object is freed/destroyed.
    ;                   There is no need to call this method directly. If you wish to close the handle simply free the derived object.
    ;                   i.e. derivedObject := [] ; or derivedObject := ""
    ; Parameters:
    ;   hProcess        The handle to the process, as returned by openProcess().
    ; Return Values:
    ;   Non-Zero        Success
    ;   0               Failure

    closeHandle(hProcess)
    {
        return DllCall("CloseHandle", "Ptr", hProcess)
    }

    ; Methods:      numberOfBytesRead() / numberOfBytesWritten()
    ;               Returns the number of bytes read or written by the last ReadProcessMemory or WriteProcessMemory operation.
    ;
    ; Return Values:
    ;   zero or positive value      Number of bytes read/written
    ;   -1                          Failure. Shouldn't occur

    numberOfBytesRead()
    {
        return !this.pNumberOfBytesRead ? -1 : NumGet(this.pNumberOfBytesRead+0, "Ptr")
    }
    numberOfBytesWritten()
    {
        return !this.pNumberOfBytesWritten ? -1 : NumGet(this.pNumberOfBytesWritten+0, "Ptr")
    }


    ; Method:   read(address, type := "UInt", aOffsets*)
    ;           Reads various integer type values
    ; Parameters:
    ;       address -   The memory address of the value or if using the offset parameter,
    ;                   the base address of the pointer.
    ;       type    -   The integer type.
    ;                   Valid types are UChar, Char, UShort, Short, UInt, Int, Float, Int64 and Double.
    ;                   Note: Types must not contain spaces i.e. " UInt" or "UInt " will not work.
    ;                   When an invalid type is passed the method returns NULL and sets ErrorLevel to -2
    ;       aOffsets* - A variadic list of offsets. When using offsets the address parameter should equal the base address of the pointer.
    ;                   The address (base address) and offsets should point to the memory address which holds the integer.
    ; Return Values:
    ;       integer -   Indicates success.
    ;       Null    -   Indicates failure. Check ErrorLevel and A_LastError for more information.
    ;       Note:       Since the returned integer value may be 0, to check for success/failure compare the result
    ;                   against null i.e. if (result = "") then an error has occurred.
    ;                   When reading doubles, adjusting "SetFormat, float, totalWidth.DecimalPlaces"
    ;                   may be required depending on your requirements.

    read(address, type := "UInt", aOffsets*)
    {
        ; If invalid type RPM() returns success (as bytes to read resolves to null in dllCall())
        ; so set errorlevel to invalid parameter for DLLCall() i.e. -2
        if !this.aTypeSize.hasKey(type)
            return "", ErrorLevel := -2
        if DllCall("ReadProcessMemory", "Ptr", this.hProcess, "Ptr", aOffsets.maxIndex() ? this.getAddressFromOffsets(address, aOffsets*) : address, type "*", result, "Ptr", this.aTypeSize[type], "Ptr", this.pNumberOfBytesRead)
            return result
        return
    }

    ; Method:   readRaw(address, byRef buffer, bytes := 4, aOffsets*)
    ;           Reads an area of the processes memory and stores it in the buffer variable
    ; Parameters:
    ;       address  -  The memory address of the area to read or if using the offsets parameter
    ;                   the base address of the pointer which points to the memory region.
    ;       buffer   -  The unquoted variable name for the buffer. This variable will receive the contents from the address space.
    ;                   This method calls varsetCapcity() to ensure the variable has an adequate size to perform the operation.
    ;                   If the variable already has a larger capacity (from a previous call to varsetcapcity()), then it will not be shrunk.
    ;                   Therefore it is the callers responsibility to ensure that any subsequent actions performed on the buffer variable
    ;                   do not exceed the bytes which have been read - as these remaining bytes could contain anything.
    ;       bytes   -   The number of bytes to be read.
    ;       aOffsets* - A variadic list of offsets. When using offsets the address parameter should equal the base address of the pointer.
    ;                   The address (base address) and offsets should point to the memory address which is to be read
    ; Return Values:
    ;       Non Zero -   Indicates success.
    ;       Zero     -   Indicates failure. Check errorLevel and A_LastError for more information
    ;
    ; Notes:            The contents of the buffer may then be retrieved using AHK's NumGet() and StrGet() functions.
    ;                   This method offers significant (~30% and up) performance boost when reading large areas of memory.
    ;                   As calling ReadProcessMemory for four bytes takes a similar amount of time as it does for 1,000 bytes.

    readRaw(address, byRef buffer, bytes := 4, aOffsets*)
    {
        VarSetCapacity(buffer, bytes)
        return DllCall("ReadProcessMemory", "Ptr", this.hProcess, "Ptr", aOffsets.maxIndex() ? this.getAddressFromOffsets(address, aOffsets*) : address, "Ptr", &buffer, "Ptr", bytes, "Ptr", this.pNumberOfBytesRead)
    }

    ; Method:   readString(address, sizeBytes := 0, encoding := "utf-8", aOffsets*)
    ;           Reads string values of various encoding types
    ; Parameters:
    ;       address -   The memory address of the value or if using the offset parameter,
    ;                   the base address of the pointer.
    ;       sizeBytes - The size (in bytes) of the string to be read.
    ;                   If zero is passed, then the function will read each character until a null terminator is found
    ;                   and then returns the entire string.
    ;       encoding -  This refers to how the string is stored in the program's memory.
    ;                   UTF-8 and UTF-16 are common. Refer to the AHK manual for other encoding types.
    ;       aOffsets* - A variadic list of offsets. When using offsets the address parameter should equal the base address of the pointer.
    ;                   The address (base address) and offsets should point to the memory address which holds the string.
    ;
    ;  Return Values:
    ;       String -    On failure an empty (null) string is always returned. Since it's possible for the actual string
    ;                   being read to be null (empty), then a null return value should not be used to determine failure of the method.
    ;                   Instead the property [derivedObject].ReadStringLastError can be used to check for success/failure.
    ;                   This property is set to 0 on success and 1 on failure. On failure ErrorLevel and A_LastError should be consulted
    ;                   for more information.
    ; Notes:
    ;       For best performance use the sizeBytes parameter to specify the exact size of the string.
    ;       If the exact size is not known and the string is null terminated, then specifying the maximum
    ;       possible size of the string will yield the same performance.
    ;       If neither the actual or maximum size is known and the string is null terminated, then specifying
    ;       zero for the sizeBytes parameter is fine. Generally speaking for all intents and purposes the performance difference is
    ;       inconsequential.

    readString(address, sizeBytes := 0, encoding := "UTF-8", aOffsets*)
    {
        bufferSize := VarSetCapacity(buffer, sizeBytes ? sizeBytes : 100, 0)
        this.ReadStringLastError := False
        if aOffsets.maxIndex()
            address := this.getAddressFromOffsets(address, aOffsets*)
        if !sizeBytes  ; read until null terminator is found or something goes wrong
        {
            ; Even if there are multi-byte-characters (bigger than the encodingSize i.e. surrogates) in the string, when reading in encodingSize byte chunks they will never register as null (as they will have bits set on those bytes)
            if (encoding = "utf-16" || encoding = "cp1200")
                encodingSize := 2, charType := "UShort", loopCount := 2
            else encodingSize := 1, charType := "Char", loopCount := 4
            Loop
            {   ; Lets save a few reads by reading in 4 byte chunks
                if !DllCall("ReadProcessMemory", "Ptr", this.hProcess, "Ptr", address + ((outterIndex := A_index) - 1) * 4, "Ptr", &buffer, "Ptr", 4, "Ptr", this.pNumberOfBytesRead) || ErrorLevel
                    return "", this.ReadStringLastError := True
                else loop, %loopCount%
                {
                    if NumGet(buffer, (A_Index - 1) * encodingSize, charType) = 0 ; NULL terminator
                    {
                        if (bufferSize < sizeBytes := outterIndex * 4 - (4 - A_Index * encodingSize))
                            VarSetCapacity(buffer, sizeBytes)
                        break, 2
                    }
                }
            }
        }
        if DllCall("ReadProcessMemory", "Ptr", this.hProcess, "Ptr", address, "Ptr", &buffer, "Ptr", sizeBytes, "Ptr", this.pNumberOfBytesRead)
            return StrGet(&buffer,, encoding)
        return "", this.ReadStringLastError := True
    }

    ; Method:  writeString(address, string, encoding := "utf-8", aOffsets*)
    ;          Encodes and then writes a string to the process.
    ; Parameters:
    ;       address -   The memory address to which data will be written or if using the offset parameter,
    ;                   the base address of the pointer.
    ;       string -    The string to be written.
    ;       encoding -  This refers to how the string is to be stored in the program's memory.
    ;                   UTF-8 and UTF-16 are common. Refer to the AHK manual for other encoding types.
    ;       aOffsets* - A variadic list of offsets. When using offsets the address parameter should equal the base address of the pointer.
    ;                   The address (base address) and offsets should point to the memory address which is to be written to.
    ; Return Values:
    ;       Non Zero -   Indicates success.
    ;       Zero     -   Indicates failure. Check errorLevel and A_LastError for more information
    ; Notes:
    ;       By default a null terminator is included at the end of written strings.
    ;       This behaviour is determined by the property [derivedObject].insertNullTerminator
    ;       If this property is true, then a null terminator will be included.

    writeString(address, string, encoding := "utf-8", aOffsets*)
    {
        encodingSize := (encoding = "utf-16" || encoding = "cp1200") ? 2 : 1
        requiredSize := StrPut(string, encoding) * encodingSize - (this.insertNullTerminator ? 0 : encodingSize)
        VarSetCapacity(buffer, requiredSize)
        StrPut(string, &buffer, StrLen(string) + (this.insertNullTerminator ?  1 : 0), encoding)
        return DllCall("WriteProcessMemory", "Ptr", this.hProcess, "Ptr", aOffsets.maxIndex() ? this.getAddressFromOffsets(address, aOffsets*) : address, "Ptr", &buffer, "Ptr", requiredSize, "Ptr", this.pNumberOfBytesWritten)
    }

    ; Method:   write(address, value, type := "Uint", aOffsets*)
    ;           Writes various integer type values to the process.
    ; Parameters:
    ;       address -   The memory address to which data will be written or if using the offset parameter,
    ;                   the base address of the pointer.
    ;       type    -   The integer type.
    ;                   Valid types are UChar, Char, UShort, Short, UInt, Int, Float, Int64 and Double.
    ;                   Note: Types must not contain spaces i.e. " UInt" or "UInt " will not work.
    ;                   When an invalid type is passed the method returns NULL and sets ErrorLevel to -2
    ;       aOffsets* - A variadic list of offsets. When using offsets the address parameter should equal the base address of the pointer.
    ;                   The address (base address) and offsets should point to the memory address which is to be written to.
    ; Return Values:
    ;       Non Zero -  Indicates success.
    ;       Zero     -  Indicates failure. Check errorLevel and A_LastError for more information
    ;       Null    -   An invalid type was passed. Errorlevel is set to -2

    write(address, value, type := "Uint", aOffsets*)
    {
        if !this.aTypeSize.hasKey(type)
            return "", ErrorLevel := -2
        return DllCall("WriteProcessMemory", "Ptr", this.hProcess, "Ptr", aOffsets.maxIndex() ? this.getAddressFromOffsets(address, aOffsets*) : address, type "*", value, "Ptr", this.aTypeSize[type], "Ptr", this.pNumberOfBytesWritten)
    }

    ; Method:   writeRaw(address, pBuffer, sizeBytes, aOffsets*)
    ;           Writes a buffer to the process.
    ; Parameters:
    ;   address -       The memory address to which the contents of the buffer will be written
    ;                   or if using the offset parameter, the base address of the pointer.
    ;   pBuffer -       A pointer to the buffer which is to be written.
    ;                   This does not necessarily have to be the beginning of the buffer itself e.g. pBuffer := &buffer + offset
    ;   sizeBytes -     The number of bytes which are to be written from the buffer.
    ;   aOffsets* -     A variadic list of offsets. When using offsets the address parameter should equal the base address of the pointer.
    ;                   The address (base address) and offsets should point to the memory address which is to be written to.
    ; Return Values:
    ;       Non Zero -  Indicates success.
    ;       Zero     -  Indicates failure. Check errorLevel and A_LastError for more information

    writeRaw(address, pBuffer, sizeBytes, aOffsets*)
    {
        return DllCall("WriteProcessMemory", "Ptr", this.hProcess, "Ptr", aOffsets.maxIndex() ? this.getAddressFromOffsets(address, aOffsets*) : address, "Ptr", pBuffer, "Ptr", sizeBytes, "Ptr", this.pNumberOfBytesWritten)
    }

    ; Method:   writeBytes(address, hexStringOrByteArray, aOffsets*)
    ;           Writes a sequence of byte values to the process.
    ; Parameters:
    ;   address -       The memory address to where the bytes will be written
    ;                   or if using the offset parameter, the base address of the pointer.
    ;   hexStringOrByteArray -  This can either be either a string (A) or an object/array (B) containing the values to be written.
    ;
    ;               A) HexString -      A string of hex bytes.  The '0x' hex prefix is optional.
    ;                                   Bytes can optionally be separated using the space or tab characters.
    ;                                   Each byte must be two characters in length i.e. '04' or '0x04' (not '4' or '0x4')
    ;               B) Object/Array -   An array containing hex or decimal byte values e.g. array := [10, 29, 0xA]
    ;
    ;   aOffsets* -     A variadic list of offsets. When using offsets the address parameter should equal the base address of the pointer.
    ;                   The address (base address) and offsets should point to the memory address which is to be written to.
    ; Return Values:
    ;       -1, -2, -3, -4  - Error with the hexstring. Refer to hexStringToPattern() for details.
    ;       Other Non Zero  - Indicates success.
    ;       Zero            - Indicates write failure. Check errorLevel and A_LastError for more information
    ;
    ;   Examples:
    ;                   writeBytes(0xAABBCC11, "DEADBEEF")          ; Writes the bytes DE AD BE EF starting at address  0xAABBCC11
    ;                   writeBytes(0xAABBCC11, [10, 20, 0xA, 2])

    writeBytes(address, hexStringOrByteArray, aOffsets*)
    {
        if !IsObject(hexStringOrByteArray)
        {
            if !IsObject(hexStringOrByteArray := this.hexStringToPattern(hexStringOrByteArray))
                return hexStringOrByteArray
        }
        sizeBytes := this.getNeedleFromAOBPattern("", buffer, hexStringOrByteArray*)
        return this.writeRaw(address, &buffer, sizeBytes, aOffsets*)
    }

    ; Method:           pointer(address, finalType := "UInt", offsets*)
    ;                   This is an internal method. Since the other various methods all offer this functionality, they should be used instead.
    ;                   This will read integer values of both pointers and non-pointers (i.e. a single memory address)
    ; Parameters:
    ;   address -       The base address of the pointer or the memory address for a non-pointer.
    ;   finalType -     The type of integer stored at the final address.
    ;                   Valid types are UChar, Char, UShort, Short, UInt, Int, Float, Int64 and Double.
    ;                   Note: Types must not contain spaces i.e. " UInt" or "UInt " will not work.
    ;                   When an invalid type is passed the method returns NULL and sets ErrorLevel to -2
    ;   aOffsets* -     A variadic list of offsets used to calculate the pointers final address.
    ; Return Values: (The same as the read() method)
    ;       integer -   Indicates success.
    ;       Null    -   Indicates failure. Check ErrorLevel and A_LastError for more information.
    ;       Note:       Since the returned integer value may be 0, to check for success/failure compare the result
    ;                   against null i.e. if (result = "") then an error has occurred.
    ;                   If the target application is 64bit the pointers are read as an 8 byte Int64 (this.PtrType)

    pointer(address, finalType := "UInt", offsets*)
    {
        For index, offset in offsets
            address := this.Read(address, this.ptrType) + offset
        Return this.Read(address, finalType)
    }

    ; Method:               getAddressFromOffsets(address, aOffsets*)
    ;                       Returns the final address of a pointer.
    ;                       This is an internal method used by various methods however, this method may be useful if you are
    ;                       looking to eliminate the overhead overhead associated with reading pointers which only change
    ;                       on startup or map/level change. In other words you can cache the final address and
    ;                       read from this address directly.
    ; Parameters:
    ;   address             The base address of the pointer.
    ;   aOffsets*           A variadic list of offsets used to calculate the pointers final address.
    ;                       At least one offset must be present.
    ; Return Values:
    ;   Positive integer    The final memory address pointed to by the pointer.
    ;   Negative integer    Failure
    ;   Null                Failure
    ; Note:                 If the target application is 64bit the pointers are read as an 8 byte Int64 (this.PtrType)

    getAddressFromOffsets(address, aOffsets*)
    {
        return  aOffsets.Remove() + this.pointer(address, this.ptrType, aOffsets*) ; remove the highest key so can use pointer() to find final memory address (minus the last offset)
    }

    ; Interesting note:
    ; Although handles are 64-bit pointers, only the less significant 32 bits are employed in them for the purpose
    ; of better compatibility (for example, to enable 32-bit and 64-bit processes interact with each other)
    ; Here are examples of such types: HANDLE, HWND, HMENU, HPALETTE, HBITMAP, etc.
    ; http://www.viva64.com/en/k/0005/



    ; Method:   getProcessBaseAddress(WindowTitle, windowMatchMode := 3)
    ;           Returns the base address of a process. In most cases this will provide the same result as calling getModuleBaseAddress() (when passing
    ;           a null value as the module parameter), however getProcessBaseAddress() will usually work regardless of the bitness
    ;           of both the AHK exe and the target process.
    ;           *This method relies on the target process having a window and will not work for console apps*
    ;           *'DetectHiddenWindows, On' is required for hidden windows*
    ;           ***If this returns an incorrect value, try using (the MORE RELIABLE) getModuleBaseAddress() instead.***
    ; Parameters:
    ;   windowTitle         This can be any AHK windowTitle identifier, such as
    ;                       ahk_exe, ahk_class, ahk_pid, or simply the window title. e.g. "ahk_exe calc.exe" or "Calculator".
    ;                       It's safer not to use the window title, as some things can have the same window title e.g. an open folder called "Starcraft II"
    ;                       would have the same window title as the game itself.
    ;   windowMatchMode     Determines the matching mode used when finding the program's window (windowTitle).
    ;                       The default value is 3 i.e. an exact match. The current matchmode will be used if the parameter is null or 0.
    ;                       Refer to AHK's setTitleMathMode for more information.
    ; Return Values:
    ;   Positive integer    The base address of the process (success).
    ;   Null                The process's window couldn't be found.
    ;   0                   The GetWindowLong or GetWindowLongPtr call failed. Try getModuleBaseAddress() instead.


    getProcessBaseAddress(windowTitle, windowMatchMode := "3")
    {
        if (windowMatchMode && A_TitleMatchMode != windowMatchMode)
        {
            mode := A_TitleMatchMode ; This is a string and will not contain the 0x prefix
            StringReplace, windowMatchMode, windowMatchMode, 0x ; remove hex prefix as SetTitleMatchMode will throw a run time error. This will occur if integer mode is set to hex and matchmode param is passed as an number not a string.
            SetTitleMatchMode, %windowMatchMode%    ;mode 3 is an exact match
        }
        WinGet, hWnd, ID, %WindowTitle%
        if mode
            SetTitleMatchMode, %mode%    ; In case executed in autoexec
        if !hWnd
            return ; return blank failed to find window
       ; GetWindowLong returns a Long (Int) and GetWindowLongPtr return a Long_Ptr
        return DllCall(A_PtrSize = 4     ; If DLL call fails, returned value will = 0
            ? "GetWindowLong"
            : "GetWindowLongPtr"
            , "Ptr", hWnd, "Int", -6, A_Is64bitOS ? "Int64" : "UInt")
            ; For the returned value when the OS is 64 bit use Int64 to prevent negative overflow when AHK is 32 bit and target process is 64bit
            ; however if the OS is 32 bit, must use UInt, otherwise the number will be huge (however it will still work as the lower 4 bytes are correct)
            ; Note - it's the OS bitness which matters here, not the scripts/AHKs
    }

    ; http://winprogger.com/getmodulefilenameex-enumprocessmodulesex-failures-in-wow64/
    ; http://stackoverflow.com/questions/3801517/how-to-enum-modules-in-a-64bit-process-from-a-32bit-wow-process

    ; Method:            getModuleBaseAddress(module := "", byRef aModuleInfo := "")
    ; Parameters:
    ;   moduleName -    The file name of the module/dll to find e.g. "calc.exe", "GDI32.dll", "Bass.dll" etc
    ;                   If no module (null) is specified, the address of the base module - main()/process will be returned
    ;                   e.g. for calc.exe the following two method calls are equivalent getModuleBaseAddress() and getModuleBaseAddress("calc.exe")
    ;   aModuleInfo -   (Optional) A module Info object is returned in this variable. If method fails this variable is made blank.
    ;                   This object contains the keys: name, fileName, lpBaseOfDll, SizeOfImage, and EntryPoint
    ; Return Values:
    ;   Positive integer - The module's base/load address (success).
    ;   -1 - Module not found
    ;   -3 - EnumProcessModulesEx failed
    ;   -4 - The AHK script is 32 bit and you are trying to access the modules of a 64 bit target process. Or the target process has been closed.
    ; Notes:    A 64 bit AHK can enumerate the modules of a target 64 or 32 bit process.
    ;           A 32 bit AHK can only enumerate the modules of a 32 bit process
    ;           This method requires PROCESS_QUERY_INFORMATION + PROCESS_VM_READ access rights. These are included by default with this class.

    getModuleBaseAddress(moduleName := "", byRef aModuleInfo := "")
    {
        aModuleInfo := ""
        if (moduleName = "")
            moduleName := this.GetModuleFileNameEx(0, True) ; main executable module of the process - get just fileName no path
        if r := this.getModules(aModules, True) < 0
            return r ; -4, -3
        return aModules.HasKey(moduleName) ? (aModules[moduleName].lpBaseOfDll, aModuleInfo := aModules[moduleName]) : -1
        ; no longer returns -5 for failed to get module info
    }


    ; Method:                   getModuleFromAddress(address, byRef aModuleInfo)
    ;                           Finds the module in which the address resides.
    ; Parameters:
    ;   address                 The address of interest.
    ;
    ;   aModuleInfo             (Optional) An unquoted variable name. If the module associated with the address is found,
    ;                           a moduleInfo object will be stored in this variable. This object has the
    ;                           following keys: name, fileName, lpBaseOfDll, SizeOfImage, and EntryPoint.
    ;                           If the address is not found to reside inside a module, the passed variable is
    ;                           made blank/null.
    ;   offsetFromModuleBase    (Optional) Stores the relative offset from the module base address
    ;                           to the specified address. If the method fails then the passed variable is set to blank/empty.
    ; Return Values:
    ;   1                       Success - The address is contained within a module.
    ;   -1                      The specified address does not reside within a loaded module.
    ;   -3                      EnumProcessModulesEx failed.
    ;   -4                      The AHK script is 32 bit and you are trying to access the modules of a 64 bit target process.

    getModuleFromAddress(address, byRef aModuleInfo, byRef offsetFromModuleBase := "")
    {
        aModuleInfo := offsetFromModule := ""
        if result := this.getmodules(aModules) < 0
            return result ; error -3, -4
        for k, module in aModules
        {
            if (address >= module.lpBaseOfDll && address < module.lpBaseOfDll + module.SizeOfImage)
                return 1, aModuleInfo := module, offsetFromModuleBase := address - module.lpBaseOfDll
        }
        return -1
    }

    ; SeDebugPrivileges is required to read/write memory in some programs.
    ; This only needs to be called once when the script starts,
    ; regardless of the number of programs being read (or if the target programs restart)
    ; Call this before attempting to call any other methods in this class
    ; i.e. call _ClassMemory.setSeDebugPrivilege() at the very start of the script.

    setSeDebugPrivilege(enable := True)
    {
        h := DllCall("OpenProcess", "UInt", 0x0400, "Int", false, "UInt", DllCall("GetCurrentProcessId"), "Ptr")
        ; Open an adjustable access token with this process (TOKEN_ADJUST_PRIVILEGES = 32)
        DllCall("Advapi32.dll\OpenProcessToken", "Ptr", h, "UInt", 32, "PtrP", t)
        VarSetCapacity(ti, 16, 0)  ; structure of privileges
        NumPut(1, ti, 0, "UInt")  ; one entry in the privileges array...
        ; Retrieves the locally unique identifier of the debug privilege:
        DllCall("Advapi32.dll\LookupPrivilegeValue", "Ptr", 0, "Str", "SeDebugPrivilege", "Int64P", luid)
        NumPut(luid, ti, 4, "Int64")
        if enable
            NumPut(2, ti, 12, "UInt")  ; enable this privilege: SE_PRIVILEGE_ENABLED = 2
        ; Update the privileges of this process with the new access token:
        r := DllCall("Advapi32.dll\AdjustTokenPrivileges", "Ptr", t, "Int", false, "Ptr", &ti, "UInt", 0, "Ptr", 0, "Ptr", 0)
        DllCall("CloseHandle", "Ptr", t)  ; close this access token handle to save memory
        DllCall("CloseHandle", "Ptr", h)  ; close this process handle to save memory
        return r
    }


    ; Method:  isTargetProcess64Bit(PID, hProcess := "", currentHandleAccess := "")
    ;          Determines if a process is 64 bit.
    ; Parameters:
    ;   PID                     The Process ID of the target process. If required this is used to open a temporary process handle.
    ;   hProcess                (Optional) A handle to the process, as returned by openProcess() i.e. [derivedObject].hProcess
    ;   currentHandleAccess     (Optional) The dwDesiredAccess value used when opening the process handle which has been
    ;                           passed as the hProcess parameter. If specifying hProcess, you should also specify this value.
    ; Return Values:
    ;   True    The target application is 64 bit.
    ;   False   The target application is 32 bit.
    ;   Null    The method failed.
    ; Notes:
    ;   This is an internal method which is called when the new operator is used. It is used to set the pointer type for 32/64 bit applications so the pointer methods will work.
    ;   This operation requires a handle with PROCESS_QUERY_INFORMATION or PROCESS_QUERY_LIMITED_INFORMATION access rights.
    ;   If the currentHandleAccess parameter does not contain these rights (or not passed) or if the hProcess (process handle) is invalid (or not passed)
    ;   a temporary handle is opened to perform this operation. Otherwise if hProcess and currentHandleAccess appear valid
    ;   the passed hProcess is used to perform the operation.

    isTargetProcess64Bit(PID, hProcess := "", currentHandleAccess := "")
    {
        if !A_Is64bitOS
            return False
        ; If insufficient rights, open a temporary handle
        else if !hProcess || !(currentHandleAccess & (this.aRights.PROCESS_QUERY_INFORMATION | this.aRights.PROCESS_QUERY_LIMITED_INFORMATION))
            closeHandle := hProcess := this.openProcess(PID, this.aRights.PROCESS_QUERY_INFORMATION)
        if (hProcess && DllCall("IsWow64Process", "Ptr", hProcess, "Int*", Wow64Process))
            result := !Wow64Process
        return result, closeHandle ? this.CloseHandle(hProcess) : ""
    }
    /*
        _Out_  PBOOL Wow64Proces value set to:
        True if the process is running under WOW64 - 32bit app on 64bit OS.
        False if the process is running under 32-bit Windows!
        False if the process is a 64-bit application running under 64-bit Windows.
    */

    ; Method: suspend() / resume()
    ; Notes:
    ;   These are undocumented Windows functions which suspend and resume the process. Here be dragons.
    ;   The process handle must have PROCESS_SUSPEND_RESUME access rights.
    ;   That is, you must specify this when using the new operator, as it is not included.
    ;   Some people say it requires more rights and just use PROCESS_ALL_ACCESS, however PROCESS_SUSPEND_RESUME has worked for me.
    ;   Suspending a process manually can be quite helpful when reversing memory addresses and pointers, although it's not at all required.
    ;   As an unorthodox example, memory addresses holding pointers are often stored in a slightly obfuscated manner i.e. they require bit operations to calculate their
    ;   true stored value (address). This obfuscation can prevent Cheat Engine from finding the true origin of a pointer or links to other memory regions. If there
    ;   are no static addresses between the obfuscated address and the final destination address then CE wont find anything (there are ways around this in CE). One way around this is to
    ;   suspend the process, write the true/deobfuscated value to the address and then perform your scans. Afterwards write back the original values and resume the process.

    suspend()
    {
        return DllCall("ntdll\NtSuspendProcess", "Ptr", this.hProcess)
    }

    resume()
    {
        return DllCall("ntdll\NtResumeProcess", "Ptr", this.hProcess)
    }

    ; Method:               getModules(byRef aModules, useFileNameAsKey := False)
    ;                       Stores the process's loaded modules as an array of (object) modules in the aModules parameter.
    ; Parameters:
    ;   aModules            An unquoted variable name. The loaded modules of the process are stored in this variable as an array of objects.
    ;                       Each object in this array has the following keys: name, fileName, lpBaseOfDll, SizeOfImage, and EntryPoint.
    ;   useFileNameAsKey    When true, the file name e.g. GDI32.dll is used as the lookup key for each module object.
    ; Return Values:
    ;   Positive integer    The size of the aModules array. (Success)
    ;   -3                  EnumProcessModulesEx failed.
    ;   -4                  The AHK script is 32 bit and you are trying to access the modules of a 64 bit target process.

    getModules(byRef aModules, useFileNameAsKey := False)
    {
        if (A_PtrSize = 4 && this.IsTarget64bit)
            return -4 ; AHK is 32bit and target process is 64 bit, this function wont work
        aModules := []
        if !moduleCount := this.EnumProcessModulesEx(lphModule)
            return -3
        loop % moduleCount
        {
            this.GetModuleInformation(hModule := numget(lphModule, (A_index - 1) * A_PtrSize), aModuleInfo)
            aModuleInfo.Name := this.GetModuleFileNameEx(hModule)
            filePath := aModuleInfo.name
            SplitPath, filePath, fileName
            aModuleInfo.fileName := fileName
            if useFileNameAsKey
                aModules[fileName] := aModuleInfo
            else aModules.insert(aModuleInfo)
        }
        return moduleCount
    }



    getEndAddressOfLastModule(byRef aModuleInfo := "")
    {
        if !moduleCount := this.EnumProcessModulesEx(lphModule)
            return -3
        hModule := numget(lphModule, (moduleCount - 1) * A_PtrSize)
        if this.GetModuleInformation(hModule, aModuleInfo)
            return aModuleInfo.lpBaseOfDll + aModuleInfo.SizeOfImage
        return -5
    }

    ; lpFilename [out]
    ; A pointer to a buffer that receives the fully qualified path to the module.
    ; If the size of the file name is larger than the value of the nSize parameter, the function succeeds
    ; but the file name is truncated and null-terminated.
    ; If the buffer is adequate the string is still null terminated.

    GetModuleFileNameEx(hModule := 0, fileNameNoPath := False)
    {
        ; ANSI MAX_PATH = 260 (includes null) - unicode can be ~32K.... but no one would ever have one that size
        ; So just give it a massive size and don't bother checking. Most coders just give it MAX_PATH size anyway
        VarSetCapacity(lpFilename, 2048 * (A_IsUnicode ? 2 : 1))
        DllCall("psapi\GetModuleFileNameEx"
                    , "Ptr", this.hProcess
                    , "Ptr", hModule
                    , "Str", lpFilename
                    , "Uint", 2048 / (A_IsUnicode ? 2 : 1))
        if fileNameNoPath
            SplitPath, lpFilename, lpFilename ; strips the path so = GDI32.dll

        return lpFilename
    }

    ; dwFilterFlag
    ;   LIST_MODULES_DEFAULT    0x0
    ;   LIST_MODULES_32BIT      0x01
    ;   LIST_MODULES_64BIT      0x02
    ;   LIST_MODULES_ALL        0x03
    ; If the function is called by a 32-bit application running under WOW64, the dwFilterFlag option
    ; is ignored and the function provides the same results as the EnumProcessModules function.
    EnumProcessModulesEx(byRef lphModule, dwFilterFlag := 0x03)
    {
        lastError := A_LastError
        size := VarSetCapacity(lphModule, 4)
        loop
        {
            DllCall("psapi\EnumProcessModulesEx"
                        , "Ptr", this.hProcess
                        , "Ptr", &lphModule
                        , "Uint", size
                        , "Uint*", reqSize
                        , "Uint", dwFilterFlag)
            if ErrorLevel
                return 0
            else if (size >= reqSize)
                break
            else size := VarSetCapacity(lphModule, reqSize)
        }
        ; On first loop it fails with A_lastError = 0x299 as its meant to
        ; might as well reset it to its previous version
        DllCall("SetLastError", "UInt", lastError)
        return reqSize // A_PtrSize ; module count  ; sizeof(HMODULE) - enumerate the array of HMODULEs
    }

    GetModuleInformation(hModule, byRef aModuleInfo)
    {
        VarSetCapacity(MODULEINFO, A_PtrSize * 3), aModuleInfo := []
        return DllCall("psapi\GetModuleInformation"
                    , "Ptr", this.hProcess
                    , "Ptr", hModule
                    , "Ptr", &MODULEINFO
                    , "UInt", A_PtrSize * 3)
                , aModuleInfo := {  lpBaseOfDll: numget(MODULEINFO, 0, "Ptr")
                                ,   SizeOfImage: numget(MODULEINFO, A_PtrSize, "UInt")
                                ,   EntryPoint: numget(MODULEINFO, A_PtrSize * 2, "Ptr") }
    }

    ; Method:           hexStringToPattern(hexString)
    ;                   Converts the hex string parameter into an array of bytes pattern (AOBPattern) that
    ;                   can be passed to the various pattern scan methods i.e.  modulePatternScan(), addressPatternScan(), rawPatternScan(), and processPatternScan()
    ;
    ; Parameters:
    ;   hexString -     A string of hex bytes.  The '0x' hex prefix is optional.
    ;                   Bytes can optionally be separated using the space or tab characters.
    ;                   Each byte must be two characters in length i.e. '04' or '0x04' (not '4' or '0x4')
    ;                   ** Unlike the other methods, wild card bytes MUST be denoted using '??' (two question marks)**
    ;
    ; Return Values:
    ;   Object          Success - The returned object contains the AOB pattern.
    ;   -1              An empty string was passed.
    ;   -2              Non hex character present.  Acceptable characters are A-F, a-F, 0-9, ?, space, tab, and 0x (hex prefix).
    ;   -3              Non-even wild card character count. One of the wild card bytes is missing a '?' e.g. '?' instead of '??'.
    ;   -4              Non-even character count. One of the hex bytes is probably missing a character e.g. '4' instead of '04'.
    ;
    ;   Examples:
    ;                   pattern := hexStringToPattern("DEADBEEF02")
    ;                   pattern := hexStringToPattern("0xDE0xAD0xBE0xEF0x02")
    ;                   pattern := hexStringToPattern("DE AD BE EF 02")
    ;                   pattern := hexStringToPattern("0xDE 0xAD 0xBE 0xEF 0x02")
    ;
    ;                   This will mark the third byte as wild:
    ;                   pattern := hexStringToPattern("DE AD ?? EF 02")
    ;                   pattern := hexStringToPattern("0xDE 0xAD ?? 0xEF 0x02")
    ;
    ;                   The returned pattern can then be passed to the various pattern scan methods, for example:
    ;                   pattern := hexStringToPattern("DE AD BE EF 02")
    ;                   memObject.processPatternScan(,, pattern*)   ; Note the '*'

    hexStringToPattern(hexString)
    {
        AOBPattern := []
        hexString := RegExReplace(hexString, "(\s|0x)")
        StringReplace, hexString, hexString, ?, ?, UseErrorLevel
        wildCardCount := ErrorLevel

        if !length := StrLen(hexString)
            return -1 ; no str
        else if RegExMatch(hexString, "[^0-9a-fA-F?]")
            return -2 ; non hex character and not a wild card
        else if Mod(wildCardCount, 2)
            return -3 ; non-even wild card character count
        else if Mod(length, 2)
            return -4 ; non-even character count
        loop, % length/2
        {
            value := "0x" SubStr(hexString, 1 + 2 * (A_index-1), 2)
            AOBPattern.Insert(value + 0 = "" ? "?" : value)
        }
        return AOBPattern
    }

    ; Method:           stringToPattern(string, encoding := "UTF-8", insertNullTerminator := False)
    ;                   Converts a text string parameter into an array of bytes pattern (AOBPattern) that
    ;                   can be passed to the various pattern scan methods i.e.  modulePatternScan(), addressPatternScan(), rawPatternScan(), and processPatternScan()
    ;
    ; Parameters:
    ;   string                  The text string to convert.
    ;   encoding                This refers to how the string is stored in the program's memory.
    ;                           UTF-8 and UTF-16 are common. Refer to the AHK manual for other encoding types.
    ;   insertNullTerminator    Includes the null terminating byte(s) (at the end of the string) in the AOB pattern.
    ;                           This should be set to 'false' unless you are certain that the target string is null terminated and you are searching for the entire string or the final part of the string.
    ;
    ; Return Values:
    ;   Object          Success - The returned object contains the AOB pattern.
    ;   -1              An empty string was passed.
    ;
    ;   Examples:
    ;                   pattern := stringToPattern("This text exists somewhere in the target program!")
    ;                   memObject.processPatternScan(,, pattern*)   ; Note the '*'

    stringToPattern(string, encoding := "UTF-8", insertNullTerminator := False)
    {
        if !length := StrLen(string)
            return -1 ; no str
        AOBPattern := []
        encodingSize := (encoding = "utf-16" || encoding = "cp1200") ? 2 : 1
        requiredSize := StrPut(string, encoding) * encodingSize - (insertNullTerminator ? 0 : encodingSize)
        VarSetCapacity(buffer, requiredSize)
        StrPut(string, &buffer, length + (insertNullTerminator ?  1 : 0), encoding)
        loop, % requiredSize
            AOBPattern.Insert(NumGet(buffer, A_Index-1, "UChar"))
        return AOBPattern
    }


    ; Method:           modulePatternScan(module := "", aAOBPattern*)
    ;                   Scans the specified module for the specified array of bytes
    ; Parameters:
    ;   module -        The file name of the module/dll to search e.g. "calc.exe", "GDI32.dll", "Bass.dll" etc
    ;                   If no module (null) is specified, the executable file of the process will be used.
    ;                   e.g. for calc.exe it would be the same as calling modulePatternScan(, aAOBPattern*) or modulePatternScan("calc.exe", aAOBPattern*)
    ;   aAOBPattern*    A variadic list of byte values i.e. the array of bytes to find.
    ;                   Wild card bytes should be indicated by passing a non-numeric value eg "?".
    ; Return Values:
    ;   Positive int    Success. The memory address of the found pattern.
    ;   Null            Failed to find or retrieve the specified module. ErrorLevel is set to the returned error from getModuleBaseAddress()
    ;                   refer to that method for more information.
    ;   0               The pattern was not found inside the module
    ;   -9              VirtualQueryEx() failed
    ;   -10             The aAOBPattern* is invalid. No bytes were passed

    modulePatternScan(module := "", aAOBPattern*)
    {
        MEM_COMMIT := 0x1000, MEM_MAPPED := 0x40000, MEM_PRIVATE := 0x20000
        , PAGE_NOACCESS := 0x01, PAGE_GUARD := 0x100

        if (result := this.getModuleBaseAddress(module, aModuleInfo)) <= 0
             return "", ErrorLevel := result ; failed
        if !patternSize := this.getNeedleFromAOBPattern(patternMask, AOBBuffer, aAOBPattern*)
            return -10 ; no pattern
        ; Try to read the entire module in one RPM()
        ; If fails with access (-1) iterate the modules memory pages and search the ones which are readable
        if (result := this.PatternScan(aModuleInfo.lpBaseOfDll, aModuleInfo.SizeOfImage, patternMask, AOBBuffer)) >= 0
            return result  ; Found / not found
        ; else RPM() failed lets iterate the pages
        address := aModuleInfo.lpBaseOfDll
        endAddress := address + aModuleInfo.SizeOfImage
        loop
        {
            if !this.VirtualQueryEx(address, aRegion)
                return -9
            if (aRegion.State = MEM_COMMIT
            && !(aRegion.Protect & (PAGE_NOACCESS | PAGE_GUARD)) ; can't read these areas
            ;&& (aRegion.Type = MEM_MAPPED || aRegion.Type = MEM_PRIVATE) ;Might as well read Image sections as well
            && aRegion.RegionSize >= patternSize
            && (result := this.PatternScan(address, aRegion.RegionSize, patternMask, AOBBuffer)) > 0)
                return result
        } until (address += aRegion.RegionSize) >= endAddress
        return 0
    }

    ; Method:               addressPatternScan(startAddress, sizeOfRegionBytes, aAOBPattern*)
    ;                       Scans a specified memory region for an array of bytes pattern.
    ;                       The entire memory area specified must be readable for this method to work,
    ;                       i.e. you must ensure the area is readable before calling this method.
    ; Parameters:
    ;   startAddress        The memory address from which to begin the search.
    ;   sizeOfRegionBytes   The numbers of bytes to scan in the memory region.
    ;   aAOBPattern*        A variadic list of byte values i.e. the array of bytes to find.
    ;                       Wild card bytes should be indicated by passing a non-numeric value eg "?".
    ; Return Values:
    ;   Positive integer    Success. The memory address of the found pattern.
    ;   0                   Pattern not found
    ;   -1                  Failed to read the memory region.
    ;   -10                 An aAOBPattern pattern. No bytes were passed.

    addressPatternScan(startAddress, sizeOfRegionBytes, aAOBPattern*)
    {
        if !this.getNeedleFromAOBPattern(patternMask, AOBBuffer, aAOBPattern*)
            return -10
        return this.PatternScan(startAddress, sizeOfRegionBytes, patternMask, AOBBuffer)
    }

    ; Method:       processPatternScan(startAddress := 0, endAddress := "", aAOBPattern*)
    ;               Scan the memory space of the current process for an array of bytes pattern.
    ;               To use this in a loop (scanning for multiple occurrences of the same pattern),
    ;               simply call it again passing the last found address + 1 as the startAddress.
    ; Parameters:
    ;   startAddress -      The memory address from which to begin the search.
    ;   endAddress -        The memory address at which the search ends.
    ;                       Defaults to 0x7FFFFFFF for 32 bit target processes.
    ;                       Defaults to 0xFFFFFFFF for 64 bit target processes when the AHK script is 32 bit.
    ;                       Defaults to 0x7FFFFFFFFFF for 64 bit target processes when the AHK script is 64 bit.
    ;                       0x7FFFFFFF and 0x7FFFFFFFFFF are the maximum process usable virtual address spaces for 32 and 64 bit applications.
    ;                       Anything higher is used by the system (unless /LARGEADDRESSAWARE and 4GT have been modified).
    ;                       Note: The entire pattern must be occur inside this range for a match to be found. The range is inclusive.
    ;   aAOBPattern* -      A variadic list of byte values i.e. the array of bytes to find.
    ;                       Wild card bytes should be indicated by passing a non-numeric value eg "?".
    ; Return Values:
    ;   Positive integer -  Success. The memory address of the found pattern.
    ;   0                   The pattern was not found.
    ;   -1                  VirtualQueryEx() failed.
    ;   -2                  Failed to read a memory region.
    ;   -10                 The aAOBPattern* is invalid. (No bytes were passed)

    processPatternScan(startAddress := 0, endAddress := "", aAOBPattern*)
    {
        address := startAddress
        if endAddress is not integer
            endAddress := this.isTarget64bit ? (A_PtrSize = 8 ? 0x7FFFFFFFFFF : 0xFFFFFFFF) : 0x7FFFFFFF

        MEM_COMMIT := 0x1000, MEM_MAPPED := 0x40000, MEM_PRIVATE := 0x20000
        PAGE_NOACCESS := 0x01, PAGE_GUARD := 0x100
        if !patternSize := this.getNeedleFromAOBPattern(patternMask, AOBBuffer, aAOBPattern*)
            return -10
        while address <= endAddress ; > 0x7FFFFFFF - definitely reached the end of the useful area (at least for a 32 target process)
        {
            if !this.VirtualQueryEx(address, aInfo)
                return -1
            if A_Index = 1
                aInfo.RegionSize -= address - aInfo.BaseAddress
            if (aInfo.State = MEM_COMMIT)
            && !(aInfo.Protect & (PAGE_NOACCESS | PAGE_GUARD)) ; can't read these areas
            ;&& (aInfo.Type = MEM_MAPPED || aInfo.Type = MEM_PRIVATE) ;Might as well read Image sections as well
            && aInfo.RegionSize >= patternSize
            && (result := this.PatternScan(address, aInfo.RegionSize, patternMask, AOBBuffer))
            {
                if result < 0
                    return -2
                else if (result + patternSize - 1 <= endAddress)
                    return result
                else return 0
            }
            address += aInfo.RegionSize
        }
        return 0
    }

    ; Method:           rawPatternScan(byRef buffer, sizeOfBufferBytes := "", aAOBPattern*)
    ;                   Scans a binary buffer for an array of bytes pattern.
    ;                   This is useful if you have already dumped a region of memory via readRaw()
    ; Parameters:
    ;   buffer              The binary buffer to be searched.
    ;   sizeOfBufferBytes   The size of the binary buffer. If null or 0 the size is automatically retrieved.
    ;   startOffset         The offset from the start of the buffer from which to begin the search. This must be >= 0.
    ;   aAOBPattern*        A variadic list of byte values i.e. the array of bytes to find.
    ;                       Wild card bytes should be indicated by passing a non-numeric value eg "?".
    ; Return Values:
    ;   >= 0                The offset of the pattern relative to the start of the haystack.
    ;   -1                  Not found.
    ;   -2                  Parameter incorrect.

    rawPatternScan(byRef buffer, sizeOfBufferBytes := "", startOffset := 0, aAOBPattern*)
    {
        if !this.getNeedleFromAOBPattern(patternMask, AOBBuffer, aAOBPattern*)
            return -10
        if (sizeOfBufferBytes + 0 = "" || sizeOfBufferBytes <= 0)
            sizeOfBufferBytes := VarSetCapacity(buffer)
        if (startOffset + 0 = "" || startOffset < 0)
            startOffset := 0
        return this.bufferScanForMaskedPattern(&buffer, sizeOfBufferBytes, patternMask, &AOBBuffer, startOffset)
    }

    ; Method:           getNeedleFromAOBPattern(byRef patternMask, byRef needleBuffer, aAOBPattern*)
    ;                   Converts an array of bytes pattern (aAOBPattern*) into a binary needle and pattern mask string
    ;                   which are compatible with patternScan() and bufferScanForMaskedPattern().
    ;                   The modulePatternScan(), addressPatternScan(), rawPatternScan(), and processPatternScan() methods
    ;                   allow you to directly search for an array of bytes pattern in a single method call.
    ; Parameters:
    ;   patternMask -   (output) A string which indicates which bytes are wild/non-wild.
    ;   needleBuffer -  (output) The array of bytes passed via aAOBPattern* is converted to a binary needle and stored inside this variable.
    ;   aAOBPattern* -  (input) A variadic list of byte values i.e. the array of bytes from which to create the patternMask and needleBuffer.
    ;                   Wild card bytes should be indicated by passing a non-numeric value eg "?".
    ; Return Values:
    ;  The number of bytes in the binary needle and hence the number of characters in the patternMask string.

    getNeedleFromAOBPattern(byRef patternMask, byRef needleBuffer, aAOBPattern*)
    {
        patternMask := "", VarSetCapacity(needleBuffer, aAOBPattern.MaxIndex())
        for i, v in aAOBPattern
            patternMask .= (v + 0 = "" ? "?" : "x"), NumPut(round(v), needleBuffer, A_Index - 1, "UChar")
        return round(aAOBPattern.MaxIndex())
    }

    ; The handle must have been opened with the PROCESS_QUERY_INFORMATION access right
    VirtualQueryEx(address, byRef aInfo)
    {

        if (aInfo.__Class != "_ClassMemory._MEMORY_BASIC_INFORMATION")
            aInfo := new this._MEMORY_BASIC_INFORMATION()
        return aInfo.SizeOfStructure = DLLCall("VirtualQueryEx"
                                                , "Ptr", this.hProcess
                                                , "Ptr", address
                                                , "Ptr", aInfo.pStructure
                                                , "Ptr", aInfo.SizeOfStructure
                                                , "Ptr")
    }

    /*
    // The c++ function used to generate the machine code
    int scan(unsigned char* haystack, unsigned int haystackSize, unsigned char* needle, unsigned int needleSize, char* patternMask, unsigned int startOffset)
    {
        for (unsigned int i = startOffset; i <= haystackSize - needleSize; i++)
        {
            for (unsigned int j = 0; needle[j] == haystack[i + j] || patternMask[j] == '?'; j++)
            {
                if (j + 1 == needleSize)
                    return i;
            }
        }
        return -1;
    }
    */

    ; Method:               PatternScan(startAddress, sizeOfRegionBytes, patternMask, byRef needleBuffer)
    ;                       Scans a specified memory region for a binary needle pattern using a machine code function
    ;                       If found it returns the memory address of the needle in the processes memory.
    ; Parameters:
    ;   startAddress -      The memory address from which to begin the search.
    ;   sizeOfRegionBytes - The numbers of bytes to scan in the memory region.
    ;   patternMask -       This string indicates which bytes must match and which bytes are wild. Each wildcard byte must be denoted by a single '?'.
    ;                       Non wildcards can use any other single character e.g 'x'. There should be no spaces.
    ;                       With the patternMask 'xx??x', the first, second, and fifth bytes must match. The third and fourth bytes are wild.
    ;    needleBuffer -     The variable which contains the binary needle. This needle should consist of UChar bytes.
    ; Return Values:
    ;   Positive integer    The address of the pattern.
    ;   0                   Pattern not found.
    ;   -1                  Failed to read the region.

    patternScan(startAddress, sizeOfRegionBytes, byRef patternMask, byRef needleBuffer)
    {
        if !this.readRaw(startAddress, buffer, sizeOfRegionBytes)
            return -1
        if (offset := this.bufferScanForMaskedPattern(&buffer, sizeOfRegionBytes, patternMask, &needleBuffer)) >= 0
            return startAddress + offset
        else return 0
    }
    ; Method:               bufferScanForMaskedPattern(byRef hayStack, sizeOfHayStackBytes, byRef patternMask, byRef needle)
    ;                       Scans a binary haystack for binary needle against a pattern mask string using a machine code function.
    ; Parameters:
    ;   hayStackAddress -   The address of the binary haystack which is to be searched.
    ;   sizeOfHayStackBytes The total size of the haystack in bytes.
    ;   patternMask -       A string which indicates which bytes must match and which bytes are wild. Each wildcard byte must be denoted by a single '?'.
    ;                       Non wildcards can use any other single character e.g 'x'. There should be no spaces.
    ;                       With the patternMask 'xx??x', the first, second, and fifth bytes must match. The third and fourth bytes are wild.
    ;   needleAddress -     The address of the binary needle to find. This needle should consist of UChar bytes.
    ;   startOffset -       The offset from the start of the haystack from which to begin the search. This must be >= 0.
    ; Return Values:
    ;   >= 0                Found. The pattern begins at this offset - relative to the start of the haystack.
    ;   -1                  Not found.
    ;   -2                  Invalid sizeOfHayStackBytes parameter - Must be > 0.

    ; Notes:
    ;       This is a basic function with few safeguards. Incorrect parameters may crash the script.

    bufferScanForMaskedPattern(hayStackAddress, sizeOfHayStackBytes, byRef patternMask, needleAddress, startOffset := 0)
    {
        static p
        if !p
        {
            if A_PtrSize = 4
                p := this.MCode("1,x86:8B44240853558B6C24182BC5568B74242489442414573BF0773E8B7C241CBB010000008B4424242BF82BD8EB038D49008B54241403D68A0C073A0A740580383F750B8D0C033BCD74174240EBE98B442424463B74241876D85F5E5D83C8FF5BC35F8BC65E5D5BC3")
            else
                p := this.MCode("1,x64:48895C2408488974241048897C2418448B5424308BF2498BD8412BF1488BF9443BD6774A4C8B5C24280F1F800000000033C90F1F400066660F1F840000000000448BC18D4101418D4AFF03C80FB60C3941380C18740743803C183F7509413BC1741F8BC8EBDA41FFC2443BD676C283C8FF488B5C2408488B742410488B7C2418C3488B5C2408488B742410488B7C2418418BC2C3")
        }
        if (needleSize := StrLen(patternMask)) + startOffset > sizeOfHayStackBytes
            return -1 ; needle can't exist inside this region. And basic check to prevent wrap around error of the UInts in the machine function
        if (sizeOfHayStackBytes > 0)
            return DllCall(p, "Ptr", hayStackAddress, "UInt", sizeOfHayStackBytes, "Ptr", needleAddress, "UInt", needleSize, "AStr", patternMask, "UInt", startOffset, "cdecl int")
        return -2
    }

    ; Notes:
    ; Other alternatives for non-wildcard buffer comparison.
    ; Use memchr to find the first byte, then memcmp to compare the remainder of the buffer against the needle and loop if it doesn't match
    ; The function FindMagic() by Lexikos uses this method.
    ; Use scanInBuf() machine code function - but this only supports 32 bit ahk. I could check if needle contains wild card and AHK is 32bit,
    ; then call this function. But need to do a speed comparison to see the benefits, but this should be faster. Although the benefits for
    ; the size of the memory regions be dumped would most likely be inconsequential as it's already extremely fast.

    MCode(mcode)
    {
        static e := {1:4, 2:1}, c := (A_PtrSize=8) ? "x64" : "x86"
        if !regexmatch(mcode, "^([0-9]+),(" c ":|.*?," c ":)([^,]+)", m)
            return
        if !DllCall("crypt32\CryptStringToBinary", "str", m3, "uint", 0, "uint", e[m1], "ptr", 0, "uint*", s, "ptr", 0, "ptr", 0)
            return
        p := DllCall("GlobalAlloc", "uint", 0, "ptr", s, "ptr")
        ; if (c="x64") ; Virtual protect must always be enabled for both 32 and 64 bit. If DEP is set to all applications (not just systems), then this is required
        DllCall("VirtualProtect", "ptr", p, "ptr", s, "uint", 0x40, "uint*", op)
        if DllCall("crypt32\CryptStringToBinary", "str", m3, "uint", 0, "uint", e[m1], "ptr", p, "uint*", s, "ptr", 0, "ptr", 0)
            return p
        DllCall("GlobalFree", "ptr", p)
        return
    }

    ; This link indicates that the _MEMORY_BASIC_INFORMATION32/64 should be based on the target process
    ; http://stackoverflow.com/questions/20068219/readprocessmemory-on-a-64-bit-proces-always-returns-error-299
    ; The msdn documentation is unclear, and suggests that a debugger can pass either structure - perhaps there is some other step involved.
    ; My tests seem to indicate that you must pass _MEMORY_BASIC_INFORMATION i.e. structure is relative to the AHK script bitness.
    ; Another post on the net also agrees with my results.

    ; Notes:
    ; A 64 bit AHK script can call this on a target 64 bit process. Issues may arise at extremely high memory addresses as AHK does not support UInt64 (but these addresses should never be used anyway).
    ; A 64 bit AHK can call this on a 32 bit target and it should work.
    ; A 32 bit AHk script can call this on a 64 bit target and it should work providing the addresses fall inside the 32 bit range.

    class _MEMORY_BASIC_INFORMATION
    {
        __new()
        {
            if !this.pStructure := DllCall("GlobalAlloc", "UInt", 0, "Ptr", this.SizeOfStructure := A_PtrSize = 8 ? 48 : 28, "Ptr")
                return ""
            return this
        }
        __Delete()
        {
            DllCall("GlobalFree", "Ptr", this.pStructure)
        }
        ; For 64bit the int64 should really be unsigned. But AHK doesn't support these
        ; so this won't work correctly for higher memory address areas
        __get(key)
        {
            static aLookUp := A_PtrSize = 8
                                ?   {   "BaseAddress": {"Offset": 0, "Type": "Int64"}
                                    ,    "AllocationBase": {"Offset": 8, "Type": "Int64"}
                                    ,    "AllocationProtect": {"Offset": 16, "Type": "UInt"}
                                    ,    "RegionSize": {"Offset": 24, "Type": "Int64"}
                                    ,    "State": {"Offset": 32, "Type": "UInt"}
                                    ,    "Protect": {"Offset": 36, "Type": "UInt"}
                                    ,    "Type": {"Offset": 40, "Type": "UInt"} }
                                :   {  "BaseAddress": {"Offset": 0, "Type": "UInt"}
                                    ,   "AllocationBase": {"Offset": 4, "Type": "UInt"}
                                    ,   "AllocationProtect": {"Offset": 8, "Type": "UInt"}
                                    ,   "RegionSize": {"Offset": 12, "Type": "UInt"}
                                    ,   "State": {"Offset": 16, "Type": "UInt"}
                                    ,   "Protect": {"Offset": 20, "Type": "UInt"}
                                    ,   "Type": {"Offset": 24, "Type": "UInt"} }

            if aLookUp.HasKey(key)
                return numget(this.pStructure+0, aLookUp[key].Offset, aLookUp[key].Type)
        }
        __set(key, value)
        {
             static aLookUp := A_PtrSize = 8
                                ?   {   "BaseAddress": {"Offset": 0, "Type": "Int64"}
                                    ,    "AllocationBase": {"Offset": 8, "Type": "Int64"}
                                    ,    "AllocationProtect": {"Offset": 16, "Type": "UInt"}
                                    ,    "RegionSize": {"Offset": 24, "Type": "Int64"}
                                    ,    "State": {"Offset": 32, "Type": "UInt"}
                                    ,    "Protect": {"Offset": 36, "Type": "UInt"}
                                    ,    "Type": {"Offset": 40, "Type": "UInt"} }
                                :   {  "BaseAddress": {"Offset": 0, "Type": "UInt"}
                                    ,   "AllocationBase": {"Offset": 4, "Type": "UInt"}
                                    ,   "AllocationProtect": {"Offset": 8, "Type": "UInt"}
                                    ,   "RegionSize": {"Offset": 12, "Type": "UInt"}
                                    ,   "State": {"Offset": 16, "Type": "UInt"}
                                    ,   "Protect": {"Offset": 20, "Type": "UInt"}
                                    ,   "Type": {"Offset": 24, "Type": "UInt"} }

            if aLookUp.HasKey(key)
            {
                NumPut(value, this.pStructure+0, aLookUp[key].Offset, aLookUp[key].Type)
                return value
            }
        }
        Ptr()
        {
            return this.pStructure
        }
        sizeOf()
        {
            return this.SizeOfStructure
        }
    }

}




В архиве исходник и скомпилированный вариант.

Post's attachments

COMTerminal.zip 1.09 mb, 2 downloads since 2020-10-09 

You don't have the permssions to download the attachments of this post.
Win 7 x64
AHK v1.1.24.00
                       Справка тебе в помощь.