1 (изменено: Alectric, 2020-03-01 15:18:58)

Тема: 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:=SeachCOM("USB"),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, 2 downloads since 2020-03-01 

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