| Orion9 
 
  
 
 | 
			
				|  Posted: Sat Mar 01, 2025 00:20    Post subject: |   |  
				| 
 |  
				| После внесения некоторых изменений можно считать, что данный вариант функций для работы с иконками в трее, более-менее законченный: 
   Hidden text 	  | Code: |  	  | Pragma IncludeOnce
 # 64000-65000
 
 RegisterCommand 64000 ShowTrayIcon
 RegisterCommand 64001 ChangeTrayIcon
 RegisterCommand 64002 NotifyInfo
 
 # иконки окна уведомлений
 Const NIIF_NONE    = 0x00000000, _
 NIIF_INFO    = 0x00000001, _
 NIIF_WARNING = 0x00000002, _
 NIIF_ERROR   = 0x00000003, _
 NIIF_USER    = 0x00000004
 
 # без звука
 Const NIIF_NOSOUND    = 0x00000010
 # большая иконка
 Const NIIF_LARGE_ICON = 0x00000020
 
 # сообщения окна уведомлений
 Const NIN_BALLOONSHOW      = 0x402, _
 NIN_BALLOONHIDE      = 0x403, _
 NIN_BALLOONTIMEOUT   = 0x404, _
 NIN_BALLOONUSERCLICK = 0x405, _
 NIN_POPUPOPEN        = 0x406, _
 NIN_POPUPCLOSE       = 0x407
 
 # флаги структуры
 Const NIF_MESSAGE = 0x00000001, _
 NIF_ICON    = 0x00000002, _
 NIF_TIP     = 0x00000004, _
 NIF_STATE   = 0x00000008, _
 NIF_INFO    = 0x00000010
 
 # системные сообщения
 Const WM_MOUSEMOVE     = 0x0200, _
 WM_LBUTTONDOWN   = 0x0201, _
 WM_LBUTTONUP     = 0x0202, _
 WM_LBUTTONDBLCLK = 0x0203, _
 WM_RBUTTONDOWN   = 0x0204, _
 WM_RBUTTONUP     = 0x0205, _
 WM_RBUTTONDBLCLK = 0x0206, _
 WM_MBUTTONDOWN   = 0x0207, _
 WM_MBUTTONUP     = 0x0208, _
 WM_MBUTTONDBLCLK = 0x0209
 
 # функции обратного вызова
 SetMessageAction /P 99999 TrayAction
 SetMessageAction /P 99998 TrayNotifyInfo
 
 # главная иконка Autorun
 Func TrayAction(hWnd, uMsg, wParam, lParam)
 # движение над иконкой
 If lParam = WM_MOUSEMOVE Then
 If LAST_HINT_WINDOW = 0 Then
 If IsPressed(KEY_CTRL) Then ShowHint(GetState("threads"), "", "", 2000, 1)
 #If IsPressed(KEY_CTRL) Then ShowDarkHint(GetState("threads"), 2000, 1)
 EndIf
 EndIf
 Switch lParam
 # левый клик
 Case WM_LBUTTONDOWN
 If IsPressed(KEY_CTRL) Then
 ShowHint(GetState("threads"))
 #ShowDarkHint(GetState("threads"))
 Return
 EndIf
 AutorunMenu(0)
 # правый клик
 Case WM_RBUTTONDOWN
 ShowDarkHint(GetState("libs"))
 # клик по сообщению
 Case NIN_BALLOONUSERCLICK
 MsgBox("Baloon click")
 #WinSetState(23)
 # клик "закрыть сообщение"
 Case NIN_BALLOONTIMEOUT
 MsgBox("Time-out")
 EndSwitch
 EndFunc
 
 # служебная иконка
 Func TrayNotifyInfo(hWnd, uMsg, wParam, lParam)
 # освобождение невидимой иконки
 If lParam = NIN_BALLOONUSERCLICK or lParam = NIN_BALLOONTIMEOUT Then
 NotifyIcon("delete", 1010)
 EndIf
 EndFunc
 
 Global TrayShow = 0, Tray1001 = 0, Tray1002 = 0
 
 TrayIcon()
 
 # главная иконка
 Func TrayIcon()
 Local ico, idx = 0
 # чтение конфигурации
 IniRead TrayShow %AUTORUN_INI% "TrayIcon" "Show" 1
 If Not TrayShow Then
 Return
 EndIf
 IniRead ico %AUTORUN_INI% "TrayIcon" "Icon"
 IniRead idx %AUTORUN_INI% "TrayIcon" "Index" 0
 # откат к умолчаниям
 If ico = "" Or Not FileExist(ico) Then
 idx = 0
 ico = COMMANDER_PATH & "\TOTALCMD.EXE"
 EndIf
 Local hint = "Autorun " & FileGetVersion(AUTORUN_PATH & "\Autorun.wdx", "FileVersion")
 Local hIco = DllCall("shell32\ExtractIconW", "ptr", AUTORUN_TCHANDLE, "wstr", ico, "uint", idx, "ptr")
 If NotifyIcon("add", 1001, 99999, hIco, hint) Then Tray1001 = 1
 If hIco > 0 Then DllCall("DestroyIcon", "ptr", hIco)
 EndFunc
 
 Func ShowTrayIcon(lParam)
 If Tray1001 > 0 Then
 IniWrite %AUTORUN_INI% "TrayIcon" "Show" 0
 HideTrayIcon()
 Else
 IniWrite %AUTORUN_INI% "TrayIcon" "Show" 1
 TrayIcon()
 EndIf
 If Tray1001 > 0 Then
 ShowHint("Иконка включена")
 Else
 ShowHint("Иконка отключена")
 EndIf
 EndFunc
 
 Func HideTrayIcon()
 If NotifyIcon("delete", 1001) Then Tray1001 = 0
 EndFunc
 
 Func ChangeTrayIcon(lParam)
 Static f = GetKnownFolderPath("System") & "\shell32.dll", i = 10
 If Tray1001 = 0 Then
 ShowHint("Иконка отключена")
 Return
 EndIf
 i += 1
 Local hIco = DllCall("shell32\ExtractIconW", "ptr", AUTORUN_TCHANDLE, "wstr", f, "uint", i, "ptr")
 If IsPressed(KEY_CTRL) Then
 NotifyIcon("add", 1002, 99999, hIco, "Autorun #2", "Second icon added", "Autorun", NIIF_NONE)
 Tray1002 = 1
 Else
 If IsPressed(KEY_ALT) Then hIco = 0
 NotifyIcon("set", 1001, 99999, hIco, "", "Icon changed to shell32.dll, " & i, "", NIIF_INFO)
 Sleep(2000)
 NotifyIcon("set", 1001,, hIco,, "Icon changed to shell32.dll, " & i,, NIIF_ERROR)
 EndIf
 If hIco > 0 Then DllCall("DestroyIcon", "ptr", hIco, "bool")
 EndFunc
 
 Func NotifyInfo(lParam)
 # тест 1
 NotifyInfoMessage(1, "Test message", "", NIIF_INFO)
 sleep(3000)
 # тест 2
 txt = "Pragma Include %COMMANDER_PATH%\Ini\Scripts\Test.aucfg"
 NotifyInfoMessage(0, txt, "Script", NIIF_WARNING + NIIF_NOSOUND)
 sleep(3000)
 # тест 3
 txt = "Test message line 1" & auCRLF & "Test message line 2"
 NotifyInfoMessage(0, txt, "Script", NIIF_ERROR)
 EndFunc
 
 #{
 Функция для показа системных уведомлений:
 
 Icon - иконка в трее (1 - да, 0 - нет)
 InfoText - текст уведомления (199 символов)
 InfoTitle - текст заголовка уведомления (47 символов)
 InfoType - флаги NIIF_* меняющие вид и поведение окна уведомлений
 
 Функция использует идентификатор "1010" и номер сообщения "99998"
 #}
 Func NotifyInfoMessage(Icon, InfoText, InfoTitle = "Autorun", InfoType = 0)
 Local dwFlags = 0
 Local buf = Buffer(auPtrSize = 4 ? 956 : 976)
 Local hIco = 0, tc = COMMANDER_PATH & "\TOTALCMD.EXE"
 hIco = DllCall("shell32\ExtractIconW", "ptr", AUTORUN_TCHANDLE, "wstr", tc, "uint", 0, "ptr")
 If Icon Then
 dwFlags = BitOR(NIF_MESSAGE, NIF_ICON, NIF_INFO)
 Else
 dwFlags = BitOR(NIF_MESSAGE, NIF_STATE, NIF_ICON, NIF_INFO)
 Endif
 buf.Zero()
 If auX64 Then
 buf.SetNum(0,  "dword", buf.size)
 buf.SetNum(8,  "hwnd", AUTORUN_TCHANDLE)
 buf.SetNum(16, "uint", 1010, _
 "uint", dwFlags, _
 "uint", 99998)
 buf.SetNum(32, "ptr",  hIco)
 buf.SetNum(296, "dword", 1, _
 "dword", 1)
 Else
 buf.SetNum(0, "dword", buf.size, _
 "hwnd", AUTORUN_TCHANDLE, _
 "uint", 1010, _
 "uint", dwFlags, _
 "uint", 99998, _
 "ptr",  hIco) )
 buf.SetNum(280, "dword", 1, _
 "dword", 1)
 
 EndIf
 Local offset = (auX64 ? 40 : 24) + 256 + 4 + 4
 buf.SetStr(StrLeft(InfoText, 199) & Chr(0), offset)
 If StrLen(InfoTitle) > 0 Then
 buf.SetStr(StrLeft(InfoTitle, 47) & Chr(0), offset + 512 + 4)
 EndIf
 If InfoType > 0 Then
 buf.SetNum(offset + 512 + 4 + 128, "dword", InfoType)
 EndIf
 Local Result = DllCall("Shell32.dll\Shell_NotifyIconW", "uint", 0, "ptr", buf.ptr)
 If hIco > 0 Then DllCall("DestroyIcon", "ptr", hIco)
 Free(buf)
 Return Result
 EndFunc
 
 #{
 Отображение иконки в трее:
 https://learn.microsoft.com/en-us/windows/win32/api/shellapi/ns-shellapi-notifyicondataw
 
 Action - тип операции ("add", "set", "delete")
 ID - внутренний идентификатор иконки (1001, 1002, ...)
 CallbackMessage - код сообщения для обратного вызова (99999, 99998, ...)
 hIcon - дескриптор иконки (обязателен для "add", необязателен для "set")
 Tip - текст подсказки в трее
 InfoText - текст уведомления (только при операции "add" или "set")
 InfoTitle - текст заголовка уведомления
 InfoType - флаги NIIF_* меняющие вид и поведение окна уведомлений
 
 #}
 Func NotifyIcon(Action, ID, CallbackMessage = 0, hIcon = 0, Tip = "", InfoText = "", InfoTitle = "", InfoType = 0)
 Local Message, Result
 Local TIP_MAXCHAR = 127, INFO_MAXCHAR = 199, TITLE_MAXCHAR = 47
 Local buf = Buffer((auPtrSize = 4 ? 956 : 976))
 buf.Zero()
 Local FLAGS = BitOR((CallbackMessage = 0 ? 0 : NIF_MESSAGE), _
 (hIcon = 0 ? 0 : NIF_ICON), _
 (StrLen(Tip) = 0 ? 0 : NIF_TIP), _
 (StrLen(InfoText) = 0 ? 0 : NIF_INFO))
 Switch Action
 Case "add"
 Message = 0
 Case "set"
 Message = 1
 Case "delete"
 Message = 2
 Else
 Return
 EndSwitch
 If auX64 Then
 buf.SetNum(0,  "dword", buf.size)
 buf.SetNum(8,  "hwnd", AUTORUN_TCHANDLE)
 buf.SetNum(16, "uint", ID, _
 "uint", FLAGS, _
 "uint", CallbackMessage)
 buf.SetNum(32, "ptr", hIcon)
 Else
 buf.SetNum(0, "dword", buf.size, _
 "hwnd", AUTORUN_TCHANDLE, _
 "uint", ID, _
 "uint", FLAGS, _
 "uint", CallbackMessage, _
 "ptr", hIcon)
 EndIf
 If StrLen(Tip) > 0 Then
 buf.SetStr(StrLeft(Tip, TIP_MAXCHAR) & Chr(0), (auX64 ? 40 : 24)) # auPtrSize*2 + 4*4 + padding for X64
 EndIf
 If StrLen(InfoText) > 0 Then
 buf.SetStr(StrLeft(InfoText, INFO_MAXCHAR) & Chr(0), (auX64 ? 40 : 24) + 264) # 256 + 4 + 4
 If StrLen(InfoTitle) > 0 Then
 buf.SetStr(StrLeft(InfoTitle, TITLE_MAXCHAR) & Chr(0), (auX64 ? 40 : 24) + 780) # 512 + 4
 EndIf
 If InfoType > 0 Then
 buf.SetNum((auX64 ? 40 : 24) + 780 + 128, "dword", InfoType)
 EndIf
 EndIf
 Result = DllCall("Shell32.dll\Shell_NotifyIconW", "uint", Message, "ptr", buf.ptr, "bool")
 Free(buf)
 Return Result
 EndFunc
 | 
Добавил туда важных комментариев, которые позволят быстрее разобраться, но если коротко, сейчас используются две функции: одна для добавления иконок в трей и работы с ними (NotifyIcon), другая - только для показа уведомлений (NotifyInfoMessage):
 
 
   Hidden text 	  | Code: |  	  | 1.Отображение иконки в трее:
https://learn.microsoft.com/en-us/windows/win32/api/shellapi/ns-shellapi-notifyicondataw
 
 Action - тип операции ("add", "set", "delete")
 ID - внутренний идентификатор иконки (1001, 1002, ...)
 CallbackMessage - код сообщения для обратного вызова (99999, 99998, ...)
 hIcon - дескриптор иконки (обязателен для "add", необязателен для "set")
 Tip - текст подсказки в трее
 InfoText - текст уведомления (только при операции "add" или "set")
 InfoTitle - текст заголовка уведомления
 InfoType - флаги NIIF_* меняющие вид и поведение окна уведомлений
 
 Func NotifyIcon(Action, ID, CallbackMessage = 0, hIcon = 0, Tip = "", InfoText = "", InfoTitle = "", InfoType = 0)
 
 2.Функция для показа системных уведомлений:
 
 Icon - иконка в трее (1 - да, 0 - нет)
 InfoText - текст уведомления (199 символов)
 InfoTitle - текст заголовка уведомления (47 символов)
 InfoType - флаги NIIF_* меняющие вид и поведение окна уведомлений
 
 Функция использует идентификатор "1010" и номер сообщения "99998"
 
 Func NotifyInfoMessage(Icon, InfoText, InfoTitle = "Autorun", InfoType = 0)
 | 
Кнопки для тестов:
 
   Hidden textTOTALCMD#BAR#DATA
64000
 
 %COMMANDER_EXE%,1
 Иконка в трее
 
 1
 -1
 
   Hidden textTOTALCMD#BAR#DATA
64001
 
 %COMMANDER_EXE%,1
 Смена икоки в трее|ALT - Только сообщение|CTRL - Добавление второй
 
 1
 -1
 
   Hidden textTOTALCMD#BAR#DATA
64002
 
 %COMMANDER_EXE%,1
 Тест уведомления
 
 1
 -1
 
Первая кнопка включает или отключает иконку в трее, вторая меняет иконку и показывает сообщения, третья тестируют уведомления.
 
 В основном файле конфигурации важно не забыть удалить иконки при выходе в секции финализации:
 
   Hidden text 	  | Code: |  	  | Pragma AutorunFinalizeSection
 # удаление иконок
 If Tray1001 > 0 Then NotifyIcon("delete", 1001)
 If Tray1002 > 0 Then NotifyIcon("delete", 1002)
 | 
 Loopback, хотел попробовать сделать по мотивам темы:
 https://www.autohotkey.com/boards/viewtopic.php?t=110783
 Там есть рабочий код на AutoHotkey, вроде ничего сложного:
 
   Hidden text 	  | Code: |  	  | class UCharDet
{
 #DllLoad 'libuchardet.dll'
 
 /**
 * Create an encoding detector.
 * @return an instance of uchardet_t.
 *
 * UCHARDET_INTERFACE uchardet_t uchardet_new(void);
 */
 Ptr := DllCall('libuchardet\uchardet_new', 'CDecl Ptr')
 
 /**
 * Delete an encoding detector.
 * @param ud [in] the uchardet_t handle to delete.
 *
 * UCHARDET_INTERFACE void uchardet_delete(uchardet_t ud);
 */
 __Delete() => this.Ptr && DllCall('libuchardet\uchardet_delete', 'Ptr', this, 'CDecl')
 
 /**
 * Feed data to an encoding detector.
 * The detector is able to shortcut processing when it reaches certainty
 * for an encoding, so you should not worry about limiting input data.
 * As far as you should be concerned: the more the better.
 *
 * @param ud [in] handle of an instance of uchardet
 * @param data [in] data
 * @param len [in] number of byte of data
 * @return non-zero number on failure.
 *
 * UCHARDET_INTERFACE int uchardet_handle_data(uchardet_t ud, const char * data, size_t len);
 */
 HandleData(pBytes, cBytes) => DllCall('libuchardet\uchardet_handle_data', 'Ptr', this, 'Ptr', pBytes, 'Ptr', cBytes, 'CDecl Int')
 
 /**
 * Notify an end of data to an encoding detector.
 * @param ud [in] handle of an instance of uchardet
 *
 * UCHARDET_INTERFACE void uchardet_data_end(uchardet_t ud);
 */
 DataEnd() => DllCall('libuchardet\uchardet_data_end', 'Ptr', this, 'CDecl')
 
 /**
 * Reset an encoding detector.
 * @param ud [in] handle of an instance of uchardet
 *
 * UCHARDET_INTERFACE void uchardet_reset(uchardet_t ud);
 */
 Reset() => DllCall('libuchardet\uchardet_reset', 'Ptr', this, 'CDecl')
 
 /**
 * Get an iconv-compatible name of the encoding that was detected.
 * @param ud [in] handle of an instance of uchardet
 * @return name of charset on success and "" on failure.
 *
 * UCHARDET_INTERFACE const char * uchardet_get_charset(uchardet_t ud);
 */
 GetCharset() => DllCall('libuchardet\uchardet_get_charset', 'Ptr', this, 'CDecl AStr')
 
 DetectBuffer(Buffer) {
 this.Reset()
 
 if res := this.HandleData(Buffer.Ptr, Buffer.Size)
 throw Error("Internal 'libuchardet' error, uchardet_handle_data() returned " res)
 
 this.DataEnd()
 charset := this.GetCharset()
 this.Reset()
 
 return charset
 }
 }
 
 UCD := UCharDet()
 MsgBox UCD.DetectBuffer(FileRead('txt.txt', 'RAW')) ; GB18030
 | 
И вроде выложили последние библиотеки:
 https://github.com/telppa/ahk-chardet/tree/main/Lib
 Я проверил - скрипт на них работает. Решил сделать то же самое на Autorun, но последняя функция возвращает пустую строку. Если у вас будет время, посмотрите, пожалуйста, в чём может быть причина. Здесь даже два варианта )
 
   Hidden text 	  | Code: |  	  | Func DetectCharset()
Local sLib = COMMANDER_PATH & "\Ini\Tools\Libs\" & (auX64 ? "" : "x86") & "\uchardet.dll"
 Local sFile = COMMANDER_PATH & "\Ini\Tools\Libs\test.txt", sBuf
 Static hLib = DllCall("LoadLibrary", "wstr", sLib, "ptr")
 If hLib = 0 Then Return
 ProcessExecGetOutput sBuf %COMSPEC% '/c type "%sFile%"'
 Local hUC = DllCall('uchardet\uchardet_new', 'ptr:cdecl')
 DllCall('uchardet\uchardet_reset', 'ptr', hUC, 'cdecl')
 Local nBytes = StrLen(sBuf)
 Local Res = DllCall('uchardet\uchardet_handle_data', 'ptr', hUC, 'str', sBuf, 'uint', nBytes, 'int:cdecl')
 MsgBox(hLib & auCRLF & hUC & auCRLF & nBytes & auCRLF & Res)
 DllCall('uchardet\uchardet_data_end', 'ptr', hUC, 'cdecl')
 Local sCharSet = DllCall('uchardet\uchardet_get_charset', 'ptr', hUC, 'str:cdecl')
 MsgBox(sCharSet)
 DllCall('uchardet\uchardet_delete', 'ptr', hUC, 'cdecl')
 #DllCall('uchardet\uchardet_reset', 'ptr', hUC, 'cdecl')
 EndFunc
 
 | 
   Hidden text 	  | Code: |  	  | Func DetectCharset2()
Local sLib = COMMANDER_PATH & "\Ini\Tools\Libs\" & (auX64 ? "" : "x86") & "\uchardet.dll"
 Local txt
 txt = "* Feed data to an encoding detector." & auCRLF & _
 "* The detector is able to shortcut processing when it reaches certainty" & auCRLF & _
 "* for an encoding, so you should not worry about limiting input data." & auCRLF & _
 "* As far as you should be concerned: the more the better."
 Static hLib = DllCall("LoadLibrary", "wstr", sLib, "ptr")
 If hLib = 0 Then Return
 Local hUC = DllCall('uchardet\uchardet_new', 'ptr:cdecl')
 DllCall('uchardet\uchardet_reset', 'ptr', hUC, 'cdecl')
 Local buf = Buffer(StrLen(txt))
 buf.SetStr(txt, 0, "ANSI")
 Local Res = DllCall('uchardet\uchardet_handle_data', 'ptr', hUC, 'ptr', buf.ptr, 'uint', buf.size, 'int:cdecl')
 MsgBox(hLib & auCRLF & hUC & auCRLF & buf.size & auCRLF & Res)
 DllCall('uchardet\uchardet_data_end', 'ptr', hUC, 'cdecl')
 Local sCharSet = DllCall('uchardet\uchardet_get_charset', 'ptr', hUC, 'str:cdecl')
 MsgBox(sCharSet)
 #DllCall('uchardet\uchardet_delete', 'ptr', hUC, 'cdecl')
 DllCall('uchardet\uchardet_reset', 'ptr', hUC, 'cdecl')
 EndFunc
 | 
 |  |