1

Тема: AutoHotkey: Отображение загрузки процессора на кнопке Пуск

Скрипт взят с форума AutoHotkey из темы  CPU usage.

Я добавил в него только 2 команды. Одна - VarSetCapacity(IdleTicks, 8) - задаёт размер переменной IdleTicks. Как я понял, это требуется для новых версий AutoHotkey. Вторая - ControlMove, Button1, -7,, 110,, ahk_class Shell_TrayWnd - связана с тем, что кнопка "Пуск" оказалась коротковата, чтобы аккуратно вместить весь требуемый текст. Знак процента частично вылезал за границу видимости. Я расширил кнопку до 110 пикселов и сдвинул влево на 7, чтобы она не так наезжала на панель быстрого запуска.

#Persistent
VarSetCapacity(IdleTicks, 8)
SetTimer, CheckCPULoad, 1000
return 

CheckCPULoad:
  ControlMove, Button1, -7,, 110,, ahk_class Shell_TrayWnd
  SetFormat, float, 02
  ControlSetText, Button1, % " " GetCPULoad() " %", ahk_class Shell_TrayWnd 
return 

GetCPULoad() 
{ 
  Global
  SetBatchLines, -1 
  IdleTime0 = %IdleTime%  ; Save previous values 
  Tick0 = %Tick% 
  DllCall("kernel32.dll\GetSystemTimes", "uint",&IdleTicks, "uint",0, "uint",0) 
  IdleTime := *(&IdleTicks) 
  Loop 7                  ; Ticks when Windows was idle 
    IdleTime += *( &IdleTicks + A_Index ) << ( 8 * A_Index ) 
  Tick := A_TickCount     ; Ticks all together 
  load := 100 - 0.01*(IdleTime - IdleTime0)/(Tick - Tick0) 
  Return, load 
}

2

Re: AutoHotkey: Отображение загрузки процессора на кнопке Пуск

Оказалось, что на компьютерах с двухъядерными процессорами на кнопку выводится отрицательное число. Причина, видимо, в том, что используемая в скрипте функция GetSystemTimes учитывает время простоя для каждого из ядер и потом суммирует результаты.
Чтобы сделать скрипт более универсальным, учитывающим количество процессоров/ядер, можно использовать переменную окружения NUMBER_OF_PROCESSORS.

И ещё одно замечание: как следует из описания упомянутой функции на MSDN, для её нормальной работы требуется по крайней мере Windows XP SP1.

#Persistent
EnvGet, NumCPU, NUMBER_OF_PROCESSORS    ; Добавлено.
VarSetCapacity(IdleTicks, 8)
SetTimer, CheckCPULoad, 1000
return 

CheckCPULoad:
  ControlMove, Button1, -7,, 110,, ahk_class Shell_TrayWnd
  SetFormat, float, 02
  ControlSetText, Button1, % " " GetCPULoad() " %", ahk_class Shell_TrayWnd 
return 

GetCPULoad() 
{ 
  Global
  SetBatchLines, -1 
  IdleTime0 = %IdleTime%  ; Save previous values 
  Tick0 = %Tick% 
  DllCall("kernel32.dll\GetSystemTimes", "uint",&IdleTicks, "uint",0, "uint",0) 
  IdleTime := *(&IdleTicks) 
  Loop 7                  ; Ticks when Windows was idle 
    IdleTime += *( &IdleTicks + A_Index ) << ( 8 * A_Index ) 
  Tick := A_TickCount     ; Ticks all together 
  load := 100 - 0.01*(IdleTime - IdleTime0)/NumCPU/(Tick - Tick0)  ; Изменено.
  Return, load 
}

3 (изменено: teadrinker, 2010-11-09 03:14:11)

Re: AutoHotkey: Отображение загрузки процессора на кнопке Пуск

Расширение функционала предыдущего скрипта. В дополнение к отражению загрузки процессора, при наведении курсора на кнопку Пуск появляется ToolTip со сведениями о нескольких наиболее загружающих CPU процессах. Имеется возможность настраивать скрипт.

/*
   Скрипт предназначен для отображения сведений о загрузке CPU на кнопке Пуск (вместо
надписи "Пуск"). При наведении курсора на кнопку Пуск появляется ToolTip, в котором
отображается информация о нескольких (по умолчанию 3-х) процессах, занимающих в данный
период (1 секунду) наибольшую часть процессорного времени.

   ToolTip можно передвинуть левой кнопкой мыши в любое удобное место на экране, новое
положение будет сохранено в ini-файле.

   Щёлкнув левой кнопкой мыши по ToolTip'у, можно вызвать Диспетчер задач Windows,
правой — окно настроек. Пункты настроек:

1. Сколько процессов показать?
   Выбрать, сколько процессов, загрузка которых не менее 1% будут отображаться
   в ToolTip'е.

2. Показывать заголовок.
   При снятии галочки в ToolTip'е не будет появляться заголовок "Max Load Info"
   с иконкой.

3. Показывать дату.
   При запуске скрипта уничтожается системная всплывающая подсказка при наведении
   курсора на часы на панели задач с отображением даты. При установленной галочке
   скрипт покажет её. Работает, только если панель задач не растянута в несколько
   строк (при этом дата отображается на панели задач).

4. Настройка размеров кнопки Пуск.
   Можно скорректировать размеры кнопки Пуск, изменяя положение её левой и правой
   границы. Для этого поместите курсор в соответствующее поле, нажмите левую кнопку
   мыши и двигайте влево или вправо. Соответственно будет сдвигаться граница кнопки.
   Новые размеры будут сохранены в ini-файле, по окончании работы скрипта система
   восстановит первоначальные.

5. Знак % на кнопке.
   Показывать ли знак % при отображении загрузки CPU на кнопке Пуск.

Все настройки применяются сразу же, кнопка OK закрывает окно, сохраняя его позицию
в ini-файле.

Прошу свои отзывы о работе скрипта, предложения и сообщения о замеченных багах
оставлять здесь: http://forum.script-coding.com/viewtopic.php?id=5038
*/

   SetBatchLines, -1
   CoordMode, Mouse
   CoordMode, ToolTip
   SetWinDelay, 0

   Title = Max Load Info   ; заголовок ToolTip'а

   AttemptKill = 0
   IniName := A_ScriptDir "\" RegExReplace(A_ScriptName, "(.*)\..*", "$1") ".ini"

   IniRead, HowManyProcShow, % IniName, Settings, HowManyProcShow, 3
   IniRead, ShowTitle, % IniName, Settings, ShowTitle, 1
   IniRead, ShowDate, % IniName, Settings, ShowDate, 1
   IniRead, ShowPercent, % IniName, Settings, ShowPercent, 1
   IniRead, xTT, % IniName, Positions, xTT, % " "
   IniRead, yTT, % IniName, Positions, yTT, % " "
   IniRead, xSett, % IniName, Positions, xSett, % " "
   IniRead, ySett, % IniName, Positions, ySett, % " "
   IniRead, xButt, % IniName, Positions, xButt, % " "
   IniRead, wButt, % IniName, Positions, wButt, % " "

   ControlGetPos, _XButt,, _WButt,, Button1, ahk_class Shell_TrayWnd
   WinGetPos, XTray, YTray, WTray, HTray, ahk_class Shell_TrayWnd
   if (xTT = "" || yTT = "")
   {
      xTT := XTray > 10 ? XTray-120 : XTray+30
      yTT := YTray > 10 ? YTray-100 : 50
   }
   if (xSett = "" || ySett = "")
   {
      xSett := XTray > 10 ? XTray-220 : XTray+25
      ySett := YTray > 10 ? YTray-230 : 50
   }

   hTT := TrackToolTip("", "", "")

   AdjustPrivileges()
   EnvGet, NumCPU, NUMBER_OF_PROCESSORS
   OnMessage(WM_LBUTTONDOWN := 0x201, "ToolMove")
   OnMessage(WM_RBUTTONUP := 0x205, "Settings")
   SetTimer, CheckCPULoad, 1000

CheckCPULoad:
   WinWait, ahk_class Shell_TrayWnd
   SetFormat, float, 02
   CPULoad := GetCPULoad()
   List := GetProcessList()

   ProcessList := ShortProcessList := ""
   Loop, parse, List, |
   {
      PID := RegExReplace(A_LoopField, ".*?,(.*),.*", "$1")
      Time%PID% := RegExReplace(A_LoopField, ".*,(.*)", "$1")

      Load := Round((Time%PID% - OldTime%PID%)/NumCPU/10000000 * 100)
      OldTime%PID% := Time%PID%
      ProcessList .= Load . "%" . RegExReplace(A_LoopField, "(.*?),.*", "$1") "`n"
   }
   if !once
   {
      once = 1
      Return
   }

   ControlMove, Button1, xButt ? XButt : _XButt - 5,
      , wButt ? wButt : (WTray > 100 ? _WButt + 10 : _WButt + 15)

   if ShowPercent
      ControlSetText, Button1, % CPULoad < 100 ? " " CPULoad " %" : CPULoad "%"
   Else
      ControlSetText, Button1, % CPULoad < 100 ? "  " CPULoad : " " CPULoad

   MouseGetPos,, yMouse, WinID, Control
   WinGetClass, Class, ahk_id %WinID%

   if (ShowDate && Class = "Shell_TrayWnd" && Control = "TrayClockWClass1")
   {
      FormatTime, Date
      WinGetPos, XTray, YTray, WTray, HTray, ahk_class Shell_TrayWnd
      if !((WTray > 200 && HTray > 40) || (HTray > 200 && WTray > 40))
      {
         if !(AttemptKill > 2 || ToolTipDead)
            ToolTipDead := ToolTipKill(A_YYYY), ++AttemptKill
         Date := RegExReplace(Date, ".*:.. +(.*)", " $1")
         ToolTip, % Date,, YTray > 40 ? YTray - 25 : YTray + HTray + 10
      }
   }
   Else
      ToolTip

   if !((Class = "Shell_TrayWnd" && Control = "Button1") || WinID = hTT)
   {
      SetTimer, CloseToolTip, -300
      Return
   }

   if !(AttemptKill > 2 || ToolTipDead)
      ToolTipDead := ToolTipKill("Начните работу"), ++AttemptKill

   StringTrimRight, ProcessList, ProcessList, 1
   Sort, ProcessList, N R D`n

   EmptyString =
   Loop % HowManyProcShow
      EmptyString .= "`n"

   Loop, parse, ProcessList, `n
   {
      if (RegExReplace(A_LoopField, "(.*)%.*", "$1") = 0 || A_Index = HowManyProcShow + 1)
         Break
      ShortProcessList .= A_LoopField . "`n"
      StringTrimRight, EmptyString, EmptyString, 1
   }

   SPL := RegExReplace(SubStr(ShortProcessList, 1, -1), "m`a)(.*%)(.*)", "$2   $1")
   ToolTipText := SPL ? SPL . EmptyString : "Нет загрузки" . SubStr(EmptyString, 1, -1)

   TrackToolTip(xTT, yTT, ToolTipText, ShowTitle ? Title : "", 1)
   PosCorrection()
   Return

CloseToolTip:
   DllCall("SendMessage", UInt, hTT, UInt, 1041, UInt, 0, UInt, &TOOLINFO)   ; TTM_TRACKACTIVATE
   Return

ToolTipKill(Text)
{
   local PID
   DetectHiddenWindows, On
   WinGet, TTList, List, ahk_class tooltips_class32
   Loop % TTList
   {
      ControlGetText, Content,, % "ahk_id " TTList%A_Index%
      if InStr(Content, Text)
      {
         WinGet, PID, PID, % "ahk_id " TTList%A_Index%
         if (PID != DllCall("GetCurrentProcessId"))
            WinClose, % "ahk_id " TTList%A_Index%
         if !WinExist("ahk_id " TTList%A_Index%)
            ToolTipDead = 1
         Break
      }
   }
   Return ToolTipDead
}

AdjustPrivileges()
{
   Process, Exist  ; sets ErrorLevel to the PID of this running script
   ; Get the handle of this script with PROCESS_QUERY_INFORMATION (0x0400)
   h := DllCall("OpenProcess", "UInt", 0x0400, "Int", false, "UInt", ErrorLevel)
   ; Open an adjustable access token with this process (TOKEN_ADJUST_PRIVILEGES = 32)
   DllCall("Advapi32.dll\OpenProcessToken", "UInt", h, "UInt", 32, "UIntP", t)
   VarSetCapacity(ti, 16, 0)  ; structure of privileges
   NumPut(1, ti, 0)  ; one entry in the privileges array...
   ; Retrieves the locally unique identifier of the debug privilege:
    if A_IsUnicode
        DllCall("Advapi32.dll\LookupPrivilegeValueW", "UInt", 0, "Str", "SeDebugPrivilege"
                                               , "Int64P", luid)
    else
        DllCall("Advapi32.dll\LookupPrivilegeValueA", "UInt", 0, "Str", "SeDebugPrivilege"
                                               , "Int64P", luid)
   NumPut(luid, ti, 4, "int64")
   NumPut(2, ti, 12)  ; enable this privilege: SE_PRIVILEGE_ENABLED = 2
   ; Update the privileges of this process with the new access token:
   DllCall("Advapi32.dll\AdjustTokenPrivileges", "UInt", t, "Int", false
                                               , "UInt", &ti, "UInt", 0
                                               , "UInt", 0, "UInt", 0)
   DllCall("CloseHandle", "UInt", h)  ; close this process handle to save memory
}

GetCPULoad()
{
   static
   global NumCPU
   IdleTime0 = %IdleTime%  ; Save previous values
   Tick0 = %Tick%
   if !IdleTicksCapacity
      IdleTicksCapacity := VarSetCapacity(IdleTicks, 8)
   DllCall("kernel32.dll\GetSystemTimes", UInt, &IdleTicks, UInt, 0, UInt, 0)
   IdleTime := *(&IdleTicks)
   Loop 7                  ; Ticks when Windows was idle
     IdleTime += *( &IdleTicks + A_Index ) << ( 8 * A_Index )
   Tick := A_TickCount     ; Ticks all together
   load := 100 - 0.01*(IdleTime - IdleTime0)/NumCPU/(Tick - Tick0)
   Return, InStr(load, "-") ? 00 : load
}

GetProcessList()
{
   s := 4096  ; size of buffers and arrays (4 KB)

   hModule := DllCall("LoadLibrary", "Str", "Psapi.dll")  ; increase performance by preloading the libaray
   s := VarSetCapacity(a, s)  ; an array that receives the list of process identifiers:
   DllCall("Psapi.dll\EnumProcesses", "UInt", &a, "UInt", s, "UIntP", r)
   Loop, % r // 4  ; parse array for identifiers as DWORDs (32 bits):
   {
      id := NumGet(a, A_Index * 4)
      ; Open process with: PROCESS_VM_READ (0x0010) | PROCESS_QUERY_INFORMATION (0x0400)
      h := DllCall("OpenProcess", "UInt", 0x0010 | 0x0400, "Int", false, "UInt", id)

      VarSetCapacity(n, s, 0)  ; a buffer that receives the base name of the module:
      e := DllCall("Psapi.dll\GetModuleBaseName" . (A_IsUnicode ? "W" : "A")
         , "UInt", h, "UInt", 0, "Str", n, "UInt", s)

      DllCall("GetProcessTimes", "Uint", h, "int64P", CreationTime, "int64P"
              , ExitTime, "int64P", KrnlTime, "int64P", UserTime)

      DllCall("CloseHandle", "UInt", h)  ; close process handle to save memory
      if (n && e)  ; if image is not null add to list:
         l .= n . "," . id . "," . KrnlTime + UserTime . "|"
   }

   Process, Exist, System
   id := ErrorLevel
   h := DllCall("OpenProcess", "UInt", 0x0400, "Int", false, "UInt", id)
   DllCall("GetProcessTimes", "Uint", h, "int64P", CreationTime, "int64P"
           , ExitTime, "int64P", KrnlTime, "int64P", UserTime)
   DllCall("CloseHandle", "UInt", h)
   l .= "System," . id . "," . KrnlTime + UserTime

   DllCall("FreeLibrary", "UInt", hModule)  ; unload the library to free memory
   Return l
}

TrackToolTip(x, y, sText, sTitle = "", h_icon = 0)
{
   ; h_icon — 0: None, 1:Info, 2: Warning, 3: Error. n > 3: assumed to be an hIcon.
   static
   global hTT, TOOLINFO
   WS_EX_TOPMOST := 8, TTS_NOPREFIX := 2, TTS_ALWAYSTIP := 1

   if !hTT
   {
      hWnd := DllCall("CreateWindowEx" , UInt, WS_EX_TOPMOST
                                       , Str, "tooltips_class32"
                                       , Str, ""
                                       , UInt, TTS_NOPREFIX|TTS_ALWAYSTIP
                                       , Int, 0, Int, 0, Int, 0, Int, 0
                                       , UInt, 0, UInt, 0, UInt, 0, UInt, 0)

      VarSetCapacity(TOOLINFO, 40, 0)
      NumPut(40, TOOLINFO)
      NumPut(0x20, TOOLINFO, 4)      ; TTF_TRACK = 0x20
      NumPut(&sText, TOOLINFO, 36)

      VarSetCapacity(Rect, 16)
      NumPut(4, Rect)
      NumPut(5, Rect, 4)
      NumPut(5, Rect, 8)
      NumPut(5, Rect, 12)

      VarSetCapacity(Rect0, 16)
      NumPut(0, Rect0)
      NumPut(0, Rect0, 4)
      NumPut(0, Rect0, 8)
      NumPut(0, Rect0, 12)

   ; используется DllCall, т. к. работает и со скрытыми окнами, в отличие от SendMessage.
      DllCall("SendMessage", UInt, hWnd, UInt, 1048, UInt, 0, UInt, 500)      ; TTM_SETMAXTIPWIDTH
      DllCall("SendMessage", UInt, hWnd, UInt, 1028, UInt, 0, UInt, &TOOLINFO)   ; TTM_ADDTOOL
      Return hWnd
   }
   else
   {
      if sTitle
         DllCall("SendMessage", UInt, hWnd, UInt, 1050, UInt, 0, UInt, &Rect0)   ; TTM_SETMARGIN
      Else
         DllCall("SendMessage", UInt, hWnd, UInt, 1050, UInt, 0, UInt, &Rect)   ; TTM_SETMARGIN

      NumPut(&sText, TOOLINFO, 36)
      if A_IsUnicode
      {
         DllCall("SendMessage", UInt, hWnd, UInt, 1057, UInt, h_icon            ; TTM_SETTITLEW
             , UInt, &sTitle)
         DllCall("SendMessage", UInt, hWnd, UInt, 1081, UInt, 0, UInt, &TOOLINFO)   ; TTM_UPDATETIPTEXTW
      }
      Else
      {
         DllCall("SendMessage", UInt, hWnd, UInt, 1056, UInt, h_icon            ; TTM_SETTITLE
             , UInt, &sTitle)
         DllCall("SendMessage", UInt, hWnd, UInt, 1036, UInt, 0, UInt, &TOOLINFO)   ; TTM_UPDATETIPTEXT
      }
      DllCall("SendMessage", UInt, hWnd, UInt, 1042, UInt, 0, UInt         ; TTM_TRACKPOSITION
         , (x & 0xFFFF)|(y & 0xFFFF)<<16)
      DllCall("SendMessage", UInt, hWnd, UInt, 1041, UInt, 1, UInt, &TOOLINFO)   ; TTM_TRACKACTIVATE
   }
}

ToolMove(wParam, lParam, msg, hwnd)
{
   global
   if (hwnd = hSett)
      PostMessage, WM_NCLBUTTONDOWN := 0xA1, HTCAPTION := 2

   if (hwnd != hTT)
      Return

   StartTime := A_TickCount
   MouseGetPos, _MouseX, _MouseY
   SetTimer, CheckCPULoad, Off

   While GetKeyState("Lbutton", "P")
   {
      MouseGetPos, MouseX_, MouseY_
      WinMove, ahk_id %hTT%,, MouseX_ - _MouseX + xTT, MouseY_ - _MouseY + yTT
      Sleep, 10
   }

   if (A_TickCount - StartTime) < 200
      Run Taskmgr
   else
   {
      xTT += (MouseX_ - _MouseX), yTT += (MouseY_ - _MouseY)
      DllCall("SendMessage", UInt, hwnd, UInt, 1042, UInt, 0, UInt         ; TTM_TRACKPOSITION
         , (xTT & 0xFFFF)|(yTT <<16))
      IniWrite, % xTT, % IniName, Positions, xTT
      IniWrite, % yTT, % IniName, Positions, yTT
      PosCorrection()
   }
   SetTimer, CheckCPULoad, On
}

Settings(wParam, lParam, msg, hwnd)
{
   local Str
   if (hwnd != hTT || WinExist("Настройки CPULoad ahk_class AutoHotkeyGUI"))
      Return
   DllCall("SendMessage", UInt, hTT, UInt, 1041, UInt, 0, UInt, &TOOLINFO)   ; TTM_TRACKACTIVATE
   Gui, -Caption +Border +AlwaysOnTop +Owner +LastFound
   WinGet, hSett
   Gui, Color, FFFEE1
   Gui, Add, Text, gWinMove x0 y0 w209 h12 +0x5
   Gui, Add, Text, x7 yp+24, Сколько процессов показать?
   Loop 10
      Str .= A_Index "|"
   StringReplace, Str, Str, % HowManyProcShow "|", % HowManyProcShow "||"
   Gui, Add, DDL, vHowManyProcShow gSettChange xp+160 yp-3 w35, % Str
   Gui, Add, Checkbox, vShowTitle gSettChange x8 yp+29, Показывать заголовок
   Gui, Add, Checkbox, VShowDate gSettChange xp yp+28, Показывать дату
   Gui, Add, GroupBox, x8 yp+28 w193 h60, Настройка размеров кнопки Пуск
   Gui, Add, Text, x105 yp+17 w1 hp-24 +0x8
   Gui, Add, Text, vLeft gButtonMove x9 yp-4 w96 h46 cAAAAAA BackgroundTrans Center, `nЛевая сторона
   Gui, Add, Text, vRight gButtonMove x106 yp wp-1 hp cAAAAAA BackgroundTrans Center, `nПравая сторона
   Gui, Add, Checkbox, vShowPercent gSettChange x8 y185, Знак `% на кнопке
   Gui, Add, Button, x150 y187 w53 h23 Default, OK

   GuiControl,, ShowTitle, % ShowTitle ? 1 : 0
   GuiControl,, ShowPercent, % ShowPercent ? 1 : 0
   GuiControl,, ShowDate, % ShowDate ? 1 : 0

   Gui, Show, x%xSett% y%ySett% w209 h218, Настройки CPULoad
   GuiControl, Focus, Static1
   Return

SettChange:
   Gui, Submit, NoHide
   IniWrite, % %A_GuiControl%, % IniName, Settings, % A_GuiControl
   Return

WinMove:
   PostMessage, WM_NCLBUTTONDOWN := 0xA1, HTCAPTION := 2
   Return

ButtonMove:
   WinWait, ahk_class Shell_TrayWnd
   SetTimer, CheckCPULoad, Off
   Gui, Font, c00AA00
   GuiControl, Font, % A_GuiControl
   MouseGetPos, xM
   ControlGetPos, xB,, wB,, Button1, ahk_class Shell_TrayWnd
   hCursor := DllCall("LoadCursor" . (A_IsUnicode ? "W" : "A"), UInt, 0, Int, OCR_SIZEWE := 32644)
   DllCall("SetSystemCursor", Uint, hCursor, Int, OCR_NORMAL := 32512)

   While GetKeyState("LButton", "P")
   {
      MouseGetPos, xM_
      Shift := (xM_ - xM)/4
      if A_GuiControl = Left
         ControlMove, Button1, xButt := xB + Shift,, wButt := wB - Shift
      Else
         ControlMove, Button1,,, wButt := wB + Shift
      Sleep, 10
   }
;   DllCall("SystemParametersInfo" . (A_IsUnicode ? "W" : "A")      ; на офф. форуме восстанавливают
;      , UInt, SPI_SETCURSORS := 0x57, UInt, 0, UInt, 0, UInt, 0)   ; курсор так
   DllCall("SetSystemCursor", UInt, hCursor, Int, OCR_NORMAL := 32512)   ; но вроде, можно и так

   Gui, Font, cAAAAAA
   GuiControl, Font, % A_GuiControl
   IniWrite, % xButt, % IniName, Positions, xButt
   IniWrite, % wButt, % IniName, Positions, wButt
   SetTimer, CheckCPULoad, On
   Return

GuiClose:
GuiEscape:
   Gui, Destroy
   Return

ButtonOK:
   WinGetPos, xSett, ySett,,, ahk_id %hSett%
   Gui, Destroy
   IniWrite, % xSett, % IniName, Positions, xSett
   IniWrite, % ySett, % IniName, Positions, ySett
   Return
}

PosCorrection()
{
   global
   WinGetPos, _xTT, _yTT, _wTT, _hTT, ahk_id %hTT%
   if (yTT != _yTT || xTT != _xTT)
   {
      yTT := (yTT - _yTT > 300) ? A_ScreenHeight - _hTT - 5 : _yTT
      xTT := _xTT
      WinMove, ahk_id %hTT%,, xTT, yTT
      DllCall("SendMessage", UInt, hwnd, UInt, 1042, UInt, 0, UInt         ; TTM_TRACKPOSITION
         , (xTT & 0xFFFF)|(yTT <<16))
      IniWrite, % xTT, % IniName, Positions, xTT
      IniWrite, % yTT, % IniName, Positions, yTT
   }
}

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

Разработка AHK-скриптов:
e-mail dfiveg@mail.ru
Skype dmitry_fiveg