Страница 211 из 243

Добавлено: Ср окт 15, 2025 18:36
Orion9
Ну ладно, эксперементировать можно долго, поэтому вот более менее стабильная версия

[cut="Clusters.aucfg"]

Код: Выделить всё

Pragma IncludeOnce

# 70400-70499

RegisterCommand 70400 Clusters
RegisterCommand 70401 ClustersButton

SetHotkeyAction /K:A /K:W /H:F /DM /S ClustersOptions

Func Clusters(lParam)
    Local bClipBoard = IsPressed(0x12)
    Local sPath = RequestCopyDataInfo("SP")
    Local sName = RequestCopyDataInfo("SN")
    Local sFile = sPath & sName    
    If Not FileExist(sFile) Then
       ShowHint("Файл не существует " & sFile)
       Return 
    Endif
    Local nMode = -1
    If bClipBoard Then nMode -= 1
    RunThread("FileVCN", sFile, nMode, 0)
EndFunc

Func ClustersButton(lParam)
    If IsPressed(0x11) Then
       ClustersOptions()
       Return
    EndIf
    Clusters(0)
EndFunc

Func NumVCN(FileName, FieldIndex, UnitIndex)
     Local nFragNum = FRAG_NUM
     FRAG_NUM = 1
     Local nRet = FileVCN(FileName, FieldIndex, 0)
     FRAG_NUM = nFragNum
     Return nRet
EndFunc

Func EntryVCN(FileName, FieldIndex, UnitIndex)
     Local nFragEntry = FRAG_ENTRY
     FRAG_ENTRY = 3
     Local nRet = FileVCN(FileName, FieldIndex, UnitIndex)
     FRAG_ENTRY = nFragEntry
     Return nRet
EndFunc

Func FileVCN(FileName, FieldIndex, UnitIndex)
    Static GENERIC_ALL     = 0x10000000, _
           GENERIC_EXECUTE = 0x20000000, _
           GENERIC_WRITE   = 0x40000000, _
           GENERIC_READ    = 0x80000000, _
           FSCTL_GET_RETRIEVAL_POINTERS     = 0x90073, _
           FSCTL_GET_RETRIEVAL_POINTER_BASE = 0x90234, _
           IOCTL_DISK_GET_PARTITION_INFO_EX = 0x70048, _ 
           OPEN_EXISTING   = 3, _           
           ERROR_MORE_DATA = 234

    Local bMap = IsPressed(0x10)
    Local bShow = (FieldIndex = -1 ? true : false)
    Local bVirt = 0    
    Local bClipBoard = (FieldIndex = -2 ? true : false)
    If bClipBoard Then bShow = false

    Local Bytes = 0
    Local hFile = 0
    Local fs = FileGetSize(FileName)
    Local sVolume, nCluster, nSector, nFree, nTotal, nRet, nSysErr, Dbg = 0, sDbg
    Static bf1 = Buffer(8)
    bf1.Zero()      
    DllCall("GetVolumePathNameW", "wstr", FileName, "ptr", bf1.ptr, "dword", bf1.size)
    sVolume = bf1.GetStr()
    DllCall("GetDiskFreeSpaceW", "wstr", sVolume, _
                                 "dword*", @nCluster, "dword*", @nSector, _
                                 "dword*", @nFree, "dword*", @nTotal)
    bf1.Zero()
    Static bf2 = Buffer(FRAG_BUFFER)
    If bf2.Size <> FRAG_BUFFER Then bf2.Size = FRAG_BUFFER
    bf2.Zero()
    hFile = DllCall("CreateFileW", "wstr",  FileName, _
                                   "dword", 0, _
                                   "dword", 3, _
                                   "ptr",   0, _
                                   "dword", OPEN_EXISTING, _
                                   "dword", 0x40000000, _
                                   "ptr",   0, "handle")
    If hFile <= 0 Then 
       If bClipBoard Or bShow Then       
          ShowHint("Error opening file " & FileName)
       EndIf   
       Return 0
    EndIf      
    
    Local hVol, nBase, bBase, nPartOffset = 0, nPartLength = 0
    hVol = DllCall("CreateFileW", "wstr", "\\.\" & StrReplace(sVolume, "\", ""), _
                                  "dword", GENERIC_READ, _
                                  "dword", 3, _
                                  "ptr",   0, _
                                  "dword", OPEN_EXISTING, _
                                  "dword", 0, _
                                  "ptr",   0, "handle")    
    If SYSERROR = 0 And hVol > 0 Then 
       nRet = DllCall("DeviceIoControl", "handle", hVol, _
                                  "dword",  FSCTL_GET_RETRIEVAL_POINTER_BASE, _
                                  "ptr",    0, _
                                  "dword",  0, _
                                  "ptr",    bf1.ptr, _
                                  "dword",  bf1.size, _
                                  "dword*", @Bytes, _
                                  'ptr', 0)                                  
       If Bytes > 0 Then
          bBase = true
          nBase = bf1.GetNum(0,"int64") 
       EndIf                   
       nRet = DllCall("DeviceIoControl", "handle", hVol, _
                                  "dword",  IOCTL_DISK_GET_PARTITION_INFO_EX, _
                                  "ptr",    0, _
                                  "dword",  0, _
                                  "ptr",    bf2.ptr, _
                                  "dword",  bf2.size, _
                                  "dword*", @Bytes, _
                                  'ptr', 0)                               
       If Bytes > 0 Then 
          nPartOffset =  bf2.GetNum(8,"int64")
          nPartLength =  bf2.GetNum(16,"int64")
       EndIf       
       DllCall("CloseHandle", "handle", hVol)    
    EndIf
    
    bf1.Zero()
    bf2.Zero()
    
    If nPartLength = 0 Then
       Local sDisk, wq = WMIQuery()
       wq.Query('SELECT * FROM Win32_LogicalDiskToPartition')     
       While wq.Next()
           sDisk = StrReplace(StrPart(wq.GetValue("Dependent"), "=", 2), '"', '')
           If sDisk = StrReplace(sVolume, "\", "") Then
              nPartOffset = wq.GetValue("StartingAddress") 
              nPartLength = wq.GetValue("EndingAddress") - nPartOffset
           EndIf                 
       Wend
       Free(wq)
    EndIf

    Local lines = FRAG_LINES-1 
    If bMap Then lines -= 10

    Local nRange1 = 0, nRange2 = 0, nRange3 = 0
    Local c = 0, contig =  0, num = 1, sOut, last_lcn = 0, last_clus = 0, virt_lcn = 0
    Static aMap = List()
    aMap.Count = 0
    
    While 1
       nRet = DllCall("DeviceIoControl", "handle", hFile, _
                                        "dword",  FSCTL_GET_RETRIEVAL_POINTERS, _
                                        "ptr",    bf1.ptr, _
                                        "dword",  bf1.size, _
                                        "ptr",    bf2.ptr, _
                                        "dword",  bf2.size, _
                                        "dword*", @Bytes, _
                                        'ptr', 0)
       nSysErr = SYSERROR
       
       If nRet = 0 Then      
          If nSysErr <> ERROR_MORE_DATA Then 
             DllCall("CloseHandle", "handle", hFile)
             If bClipBoard Or bShow Then
                ShowHint("Error getting file extents " & FileName)
             EndIf
             Return 0
          EndIf   
       EndIf
       
       Local ext       =  bf2.GetNum(0,"dword"), _
             vcn       =  bf2.GetNum(8,"int64"), _
             lcn       =  bf2.GetNum(24,"int64"), _
             next_vcn  =  0, _
             last_vcn  =  bf2.GetNum((ext-1)*16+16,"int64"), _         
             prev_lcn  =  0, _
             real_lcn  =  last_lcn, _
             prev_clus =  last_clus, _ 
             clus, i   =  0
             
       c += ext
       If lcn <> -1 And (lcn - last_clus = last_lcn) Then contig += 1
                  
       If bClipBoard Or (bShow And num <= lines) Then
          If bVirt And lcn = -1 Then
             sOut &= StrFormat("%3u %8u %12s", num, vcn, "VIRT")
          Else
             sOut &= StrFormat("%3u %8u %12s", num, vcn, SizeFormat(lcn, 0, "B", 0, 2))
          EndIf   
       EndIf
       
       For i = 0 To ext - 1           
           next_vcn   =  bf2.GetNum(i*16+16,"int64")
           prev_lcn   =  lcn           
           
           If lcn <> -1 Then
              real_lcn = lcn
           Else   
              virt_lcn += 1
           EndIf
                
           clus =  next_vcn - vcn
           If prev_lcn <> -1 Then prev_clus = clus           
           
           If i < ext - 1 Then
              lcn  =  bf2.GetNum((i+1)*16+24,"int64")
              If lcn <> -1 And (lcn - prev_clus = real_lcn) Then contig += 1
           EndIf
           
           If (bShow Or bClipBoard) And bMap And prev_lcn <> - 1 Then
              ms = Ceil(prev_lcn/nTotal*1000)
              me = Ceil((clus + prev_lcn)/nTotal*1000)
              For m = ms To me              
                 If aMap.IndexOf(m) = -1 Then aMap.Add(m)                
              Next
           EndIf
           
           If FRAG_ENTRY And prev_lcn <> - 1 Then
              Local lcn_start = prev_lcn, lcn_end = prev_lcn + clus
              If (lcn_start >= FRAG_START1 And lcn_start <= FRAG_END1) Or _
                 (lcn_end >= FRAG_START1 And lcn_end <= FRAG_END1) Then 
                 nRange1 += 1
              ElseIf (lcn_start >= FRAG_START2 And lcn_start <= FRAG_END2) Or _
                     (lcn_end >= FRAG_START2 And lcn_end <= FRAG_END2) Then 
                 nRange2 += 1
              ElseIf (lcn_start >= FRAG_START3 And lcn_start <= FRAG_END3) Or _
                     (lcn_end >= FRAG_START3 And lcn_end <= FRAG_END3) Then 
                 nRange3 += 1
              EndIf
           EndIf
           
           If bClipBoard Or (bShow And (num+i+0) <= lines) Then 
              sOut &= ' ' & StrFormat("%9u %12s", clus, SizeFormat(clus*nCluster*nSector, 1, "G", 2))
           EndIf
           
           vcn = next_vcn
           
           If bClipBoard Or (bShow And (num+i+0) <= lines) Then 
              sOut &= auCRLF & StrFormat("%3u %8u", num+i+1, vcn)
              If i < ext - 1 Then
                 If bVirt And lcn = -1 Then
                    sOut &= StrFormat(" %12s", "VIRT")        
                 Else
                    sOut &= StrFormat(" %12s", SizeFormat(lcn, 0, "B", 0, 2))
                 Endif   
              Endif
           EndIf
       Next
       If nSysErr <> ERROR_MORE_DATA Then Break
       bf1.Zero()
       bf1.SetNum(0, "int64", last_vcn)
       bf2.Zero()
       If bClipBoard Or (bShow And (num+i+1) < lines) Then sOut &= auCRLF
       num += ext
       last_lcn = real_lcn
       last_clus = prev_clus
    Wend
    contig += virt_lcn
    
    DllCall("CloseHandle", "handle", hFile)
    
    If bShow Or bClipBoard Then
       If bMap Then
          sMap = ""
           For i = 0 To 19
              For j = 1 To 50
                sMap &= (aMap.IndexOf(i*50+j) = -1 ? " " : "X")
              Next
              If i < 19 Then sMap &= auCRLF
           Next
       EndIf
       Local sRange
       If bMap Then
          If FRAG_ENTRY Then
             sRange = "Range 1-3 check: " & _
                      nRange1 & "+" & nRange2 & "+" & nRange3 & "=" & _
                      nRange1 + nRange2 + nRange3 & " total fragments"
          Else
             sRange = "Range 1-3 check: disabled"      
          EndIf   
       EndIf
       SetHintParam("ShowHint", "Font", 9, "Consolas")
       SetHintParam("ShowHint", "BackColor", 0x000000)
       SetHintParam("ShowHint", "Text", 0xFFFFFF)
       sPart = " Offset: " & SizeFormat(nPartOffset, 1, "T", 2) & " Size: " & SizeFormat(nPartLength, 1, "T", 2)
       sHeader = "File: " & StrLeft(FileGetName(FileName),40) & auCRLF & _
                 "Volume: " & sVolume & sPart & auCRLF & _
                 "Sector: " & nSector & " bytes " & _                 
                 "Cluster: " & nCluster & " sectors Base: " & (bBase ? nBase : "") & auCRLF & _
                 "Free: " & nFree & " (" & Round(nFree/nTotal*100,0) & "%) " & _
                 "Total: " & SizeFormat(nTotal, 0, "B", 0, 2) & auCRLF & _
                 "Fragments: " & (contig > 0 ? c-contig & "/" : "") & c & _
                 " Clusters: " & (nSysErr = ERROR_MORE_DATA ? ">" : "") & last_vcn & _
                 " Size: " & SizeFormat(fs, 1, "G", 2) & auCRLF & _
                 (bMap ? sRange & auCRLF : "") & _
                 "-------------------------------------------------" & auCRLF & _
                 " N     VCN       LCN       Clusters      Size    " & auCRLF & _
                 "-------------------------------------------------"                 
       sOut = (Dbg ? sDbg : "") & sHeader & auCRLF & sOut
       If bMap Then
          block = Round(nTotal/1000)
          sOut &= auCRLF & "--------------------------------------------------"           
          sOut &= auCRLF & " 1000 blocks, 1 block = " & block & ", size: " & SizeFormat(block*nCluster*nSector, 1, "G", 2) 
          sOut &= auCRLF & "--------------------------------------------------" 
          sOut &= auCRLF & sMap
       EndIf
       If bClipBoard = 1 Then 
          ClipPut(sOut)
          ShowHint(c & " фрагментов скопировано в буфер") 
          Sleep(200)
       Else   
          ShowHint(sOut)
       EndIf   
       Sleep(200)
       SetHintParam("ShowHint", "Reload")
       Return
    EndIf
        
    If FRAG_ENTRY = 3 Then 
       Switch UnitIndex
        Case 0
            Return nRange1
        Case 1
            Return nRange2
        Case 2
            Return nRange3
        Else
            Return -1
       EndSwitch 
    EndIf
    
    If FRAG_NUM Then Return c-contig
    
    Local sEntry
    If FRAG_ENTRY = 1 Then 
       If nRange1 Then 
          sEntry = "1"
       ElseIf nRange2 Then 
          sEntry = "2"
       ElseIf nRange3 Then 
          sEntry = "3"
       EndIf    
    ElseIf FRAG_ENTRY = 2 Then 
       If nRange1 Then sEntry &= "1"
       If nRange2 Then sEntry &= "2"
       If nRange3 Then sEntry &= "3"    
    EndIf   
    Return (nSysErr = ERROR_MORE_DATA ? ">" : "") & _
           (contig > 0 ? c-contig & "/" : "") & c & (sEntry <> "" ? "*" & sEntry : "")
EndFunc

Func ClustersOptions
    ShowPopupMenu /D /F /I:0 "ClustersOptionsMenu"
EndFunc

Func ClustersOptionsMenu()
    Local txt
    txt = txt & 'MENUITEM "Options Menu", em_aucmd /D' & auCRLF
    txt = txt & 'MENUITEM SEPARATOR' & auCRLF
    txt = txt & 'MENUITEM "Use numeric field", em_aucmd ' & (FRAG_NUM ? "/C" : "") & ' -1 ClusterOptionsNum' & auCRLF
    If FRAG_NUM Then  
       txt = txt & 'MENUITEM "Show range number after *", em_aucmd /D' & auCRLF
    Else    
       txt = txt & 'MENUITEM "Show range number after *", em_aucmd ' & (FRAG_ENTRY ? "/C" : "") & ' -1 ClusterOptionsEntry' & auCRLF
    EndIf   
    txt = txt & 'MENUITEM "Lines = ' & FRAG_LINES & '", em_aucmd /D' & auCRLF
    txt = txt & 'MENUITEM "Buffer = ' & FRAG_BUFFER & ' bytes", em_aucmd /D' & auCRLF
    txt = txt & 'MENUITEM SEPARATOR' & auCRLF
    txt = txt & 'MENUITEM "Range 1:  ' & FRAG_START1 & '-' & FRAG_END1 & '", em_aucmd /D' & auCRLF
    txt = txt & 'MENUITEM "Range 2:  ' & FRAG_START2 & '-' & FRAG_END2 & '", em_aucmd /D' & auCRLF
    txt = txt & 'MENUITEM "Range 3:  ' & FRAG_START3 & '-' & FRAG_END3 & '", em_aucmd /D' & auCRLF
    txt = txt & 'MENUITEM SEPARATOR' & auCRLF
    txt = txt & 'MENUITEM "Search Range 1", em_search_frag_range1' & auCRLF
    txt = txt & 'MENUITEM "Search Range 2", em_search_frag_range2' & auCRLF
    txt = txt & 'MENUITEM "Search Range 3", em_search_frag_range3' & auCRLF
    txt = txt & 'MENUITEM "Search Clusters > 500", em_search_frag_clus500' & auCRLF
    txt = txt & 'MENUITEM "Search Clusters > 2000", em_search_frag_clus2000' & auCRLF    
    txt = txt & 'MENUITEM SEPARATOR' & auCRLF
    txt = txt & 'MENUITEM "Settings...", em_aucmd -1 ShellExec "' & FRAG_INI & '"' & auCRLF
    txt = txt & 'MENUITEM "Reread settings", em_aucmd -1 RereadFragConfig' & auCRLF
    txt = txt & 'MENUITEM "Custom View: File clusters", em_aucmd -1 ClusterOptionsCustom' & auCRLF
    Return txt
EndFunc

Func RereadFragConfig()
    ReadFragConfig()
    ShowClusterHint("Reading cluster configuration")     
EndFunc

Func ClusterOptionsNum()
     FRAG_NUM = Not FRAG_NUM
     SaveFragConfig()
     ShowClusterHint("Saving cluster configuration")
     ReadFragConfig()        
EndFunc

Func ClusterOptionsEntry()
     FRAG_ENTRY = Not FRAG_ENTRY
     SaveFragConfig()     
     ShowClusterHint("Saving cluster configuration")
     ReadFragConfig()
EndFunc

Func ClusterOptionsCustom()
     CommandExec em_fields_clusters     
     ShowClusterHint("File clusters custom view")
EndFunc

Func ShowClusterHint(HintText)
    Local d
    IniRead d %COMMANDER_INI% "Configuration" "DarkMode" 0
    SetHintParam("ShowHint", "Font", 13, "Tahoma")
    SetHintParam("ShowHint", "BackColor", 0x0F0F0F)
    SetHintParam("ShowHint", "Text", 0xFFFFFF)
    SetHintParam("ShowHint", "DarkBackColor", 0xFF0000)
    SetHintParam("ShowHint", "DarkText", 0xFFFFFF)    
    ShowHint(auCRLF & "    " & HintText & "    " & auCRLF, 0, 0, 1000, 1)
    WinAlign(LAST_HINT_WINDOW)
    If d = 0 Then
       WinSetStyle(0x80000, 3, LAST_HINT_WINDOW)
       DllCall("SetLayeredWindowAttributes", "hwnd", LAST_HINT_WINDOW, _ 
                                             "ptr", 0, "byte", 192, "dword", 2)
    EndIf                                         
    Sleep(100)
    SetHintParam("ShowHint", "Reload")
    WinRedraw(1)
    SendCommand(540)     # cm_RereadSource         
EndFunc

Func SaveFragConfig()
    IniWrite %FRAG_INI% "Config" "Numeric" %FRAG_NUM%
    IniWrite %FRAG_INI% "Config" "Entry"   %FRAG_ENTRY%    
EndFunc
[/cut]
Модуль подключается стандартным образом, но кроме этого в файле основной конфигурации должны быть кастомные поля, глобальные переменные и инициализация из Clusters.ini.

[cut="autorun.cfg"]

Код: Выделить всё

Pragma AutorunPluginFields "Frag:::FileVCN" "FragNum::ft_numeric_32:NumVCN" "FragEntry:R1|R2|R3:ft_numeric_32:EntryVCN"

# кластеры файлов
Global FRAG_INI    = COMMANDER_PATH & "\Ini\" & "Clusters.ini", _
       FRAG_LINES  = 30, _
       FRAG_BUFFER = 2048, _
       FRAG_NUM    = 0, _ 
       FRAG_ENTRY  = 1   
Global FRAG_START1 = 100000000, FRAG_END1 = 110000000, _
       FRAG_START2 = 200000000, FRAG_END2 = 220000000, _
       FRAG_START3 =         0, FRAG_END3 =    100000

Func ReadFragConfig
    If FileExist(FRAG_INI) Then
       IniRead FRAG_LINES   %FRAG_INI% "Config" "Lines"   %"FRAG_LINES"
       IniRead FRAG_BUFFER  %FRAG_INI% "Config" "Buffer"  %"FRAG_BUFFER"
       IniRead FRAG_NUM     %FRAG_INI% "Config" "Numeric" %"FRAG_NUM"
       IniRead FRAG_ENTRY   %FRAG_INI% "Config" "Entry"   %"FRAG_ENTRY"
       
       IniRead FRAG_START1  %FRAG_INI% "Range" "Start1"  %"FRAG_START1"
       IniRead FRAG_START2  %FRAG_INI% "Range" "Start2"  %"FRAG_START2"
       IniRead FRAG_START3  %FRAG_INI% "Range" "Start3"  %"FRAG_START3"   
       IniRead FRAG_END1    %FRAG_INI% "Range" "End1"    %"FRAG_END1"
       IniRead FRAG_END2    %FRAG_INI% "Range" "End2"    %"FRAG_END2"
       IniRead FRAG_END3    %FRAG_INI% "Range" "End3"    %"FRAG_END3"                           
    EndIf
    
    If (FRAG_END1 - FRAG_START1 < 0) OR (FRAG_END2 - FRAG_START2 < 0) OR (FRAG_END3 - FRAG_START3 < 0) Then
       MsgBox("Отрицательный диапазон значений FRAG_START - FRAG_END", "Clusters")
    Endif
    If FRAG_LINES < 30 Then FRAG_LINES = 30
    If FRAG_BUFFER < 100 Then FRAG_BUFFER = 100    
EndFunc

ReadFragConfig

Pragma Include %COMMANDER_PATH%\Ini\Scripts\Clusters.aucfg
[/cut]
Пути можно подправить на свои, а в файле Clusters.ini есть описание настроек:

[cut="Clusters.ini"]

Код: Выделить всё

[Config]
;  Количество строк в подсказке
;  При отображении карты число уменьшается на 10
Lines=30
;  Буфер для считывания кластерных цепочек
;  Минимальное значение 100 байт, 1024 байт 63 цепочки
Buffer=2048
;  Формат для поля "Frag"
;  Numeric 1 отображать только число
;  Numeric 0 отображать строку в формате "1/3*2", где 
;  1 - число реальных фрагментов, 3 - число записей в MFT, 2 - попадание во второй диапазон
Numeric=1
;  Вхождение в диапазон после звездочки в поле "Frag"
;  Entry 0 не отображать
;  Entry 1 отображать первый по порядку диапазон
;  Entry 2 отображать вхождение во все диапазоны
Entry=1

[Range]
;  Первый диапазон для поиска значений
Start1=100000000
End1=110000000
;  Второй диапазон для поиска значений
Start2=200000000
End2=220000000
;  Третий диапазон для поиска значений
Start3=0
End3=100000
[/cut]

В Wincmd.ini (или в секции редиректа) необходимо добавить поисковые шаблоны и поля кастомных колонок, а также можно повесить em_команду с кодом вызова подсказки на горячую клавишу (в примере Ctrl+F)

[cut]

Код: Выделить всё

[Shortcuts]
C+F=em_file_fragments
CS+F=em_file_fragments
[/cut]
[cut]

Код: Выделить всё

[CustomFields]
Titles=File clusters
Headers=Размер\nFrag
Contents=[=tc.size.bkMGT2]\n[=autorun.Frag]
[/cut]
[cut]

Код: Выделить всё

[Searches]
FragRange1_SearchFor=
FragRange1_SearchIn=
FragRange1_SearchText=
FragRange1_SearchFlags=0|002002000020|||||||||0000|||
FragRange1_plugin=autorun.FragEntry.R1 > 0
FragRange2_SearchFor=
FragRange2_SearchIn=
FragRange2_SearchText=
FragRange2_SearchFlags=0|002002000020|||||||||0000|||
FragRange2_plugin=autorun.FragEntry.R2 > 0
FragRange3_SearchFor=
FragRange3_SearchIn=
FragRange3_SearchText=
FragRange3_SearchFlags=0|002002000020|||||||||0000|||
FragRange3_plugin=autorun.FragEntry.R3 > 0
FragNum > 500_SearchFor=
FragNum > 500_SearchIn=
FragNum > 500_SearchText=
FragNum > 500_SearchFlags=0|002002000020|||||||||0000|||
FragNum > 500_plugin=autorun.FragNum > 500
FragNum > 2000_SearchFor=
FragNum > 2000_SearchIn=
FragNum > 2000_SearchText=
FragNum > 2000_SearchFlags=0|002002000020|||||||||0000|||
FragNum > 2000_plugin=autorun.FragNum > 2000
[/cut]
Дополнительный набор по умолчанию называется File clusters, но его можно переименовать в usercmd.ini

[cut="usercmd.ini"]

Код: Выделить всё

[em_file_fragments]
cmd=70400

[em_search_frag_range1]
cmd=LOADSEARCH ="FragRange1"
menu=FragRange1
button=Wcmicons.dll,47

[em_search_frag_range2]
cmd=LOADSEARCH ="FragRange2"
menu=FragRange2
button=Wcmicons.dll,47

[em_search_frag_range3]
cmd=LOADSEARCH ="FragRange3"
menu=FragRange3
button=Wcmicons.dll,47

[em_search_frag_clus500]
cmd=LOADSEARCH ="FragNum > 500"
menu=FragNum > 500
button=Wcmicons.dll,47

[em_search_frag_clus2000]
cmd=LOADSEARCH ="FragNum > 2000"
menu=FragNum > 2000
button=Wcmicons.dll,47

[em_fields_clusters]
cmd=OPENCUSTOMVIEW File clusters
menu=File clusters
button=Wcmicons.dll,52
[/cut]

[cut="Кнопка"]

Код: Выделить всё

TOTALCMD#BAR#DATA
70401

%COMMANDER_EXE%
Кластеры файла|Ctrl - Меню|Shift - Карта диска|Alt - Скопировать в буфер


-1
[/cut]
Помимо информационно-развлекательных целей функционал может приносить пользу для поиска файлов в заранее указанных диапазонах. Но глубоких и длительных тестов пока не проводилось, поэтому могут быть косяки и ошибки.

Добавлено спустя 7 минут:

А, забыл добавить. Меню можно вызвать по Alt+Win+F. Эта комбинация жестко прописана в модуль.

Добавлено: Чт окт 16, 2025 22:33
A55555
Orion9
Вы просили сообщить, если улучшить что-то в поиске файлов относящихся к торрент-файлу. Всё что в голову пришло за это время.

70501 как основной поиск, самый быстрый и самый логичный, т.к. как правило целиком скачивается всё.
На случай, если всё-таки подворачивается не частый случай частично скачанного торрента и т.п., чтоб предлагало попробовать найти через 70504 в случае без результативного поиска по 70501.
Если это не целое дело, требующее переворошить весь код.

А так всё тихо, не зависало.

Добавлено: Пт окт 17, 2025 11:48
Orion9
A55555 писал(а):Если это не целое дело, требующее переворошить весь код
Да нет, это не сложно. Просто вчера поздно увидел сообщение, не успел сделать и попробовать.

Замените этот блок (где-то с 205-ой строки) на новый:

[cut]

Код: Выделить всё

g_FindTorrTask = 0
SendMessage(h_WinFindTorr, 0x0010, 0, 0)
If DllCall("DestroyWindow", "handle", h_WinFindTorr) Then h_WinFindTorr = 0
If gTorrentCount = 0 Then       
   MsgBox("Найдено: " & gTorrentCount & auCRLF & auCRLF & found, "Autorun", 64)
Else
[/cut]
[cut]

Код: Выделить всё

g_FindTorrTask = 0
SendMessage(h_WinFindTorr, 0x0010, 0, 0)
If DllCall("DestroyWindow", "handle", h_WinFindTorr) Then h_WinFindTorr = 0
If gTorrentCount = 0 Then
   Local mode = files > 1 ? 0 : 1
   Local sMode = (mode = 0 ? "«Имя и размер»" : "«Только имя»")
   MsgBox("Найдено: " & gTorrentCount & auCRLF & auCRLF & _
          "Файлов в торренте: " & files & " " & auCRLF & auCRLF & _         
          "Продолжить в режиме " & sMode & "?", "Autorun", 3+64+0)
   If EXTENDED = 6 Then Return FindTorrentParts(mode)
Else
[/cut]
Я не стал пркидывать весь модуль, там сейчас лишние строки есть для отладки, которые использовались для поиска причин зависаний. Могут еще пригодится, пока проблема не будет решена.
A55555 писал(а):А так всё тихо, не зависало
В ТС х32 действительно не виснет, и это хорошо. Можете пока этой версией Тотала попользоваться, думаю, разницы между 64-битной не заметите. Зато в 32-битном ТС подсказку можно по Ctrl вызывать.

P.S. Небольшое, но важное уточнение по поводу кластерной подсказки. Если в таблице появляются минуса, это нормально:

[cut]

Код: Выделить всё

Fragments: 7/14 Clusters: 256 Size: 976,56 Kb
-------------------------------------------------
 N     VCN       LCN       Clusters      Size    
-------------------------------------------------
  1        0           -1        48       192 Kb
  2       48   10 522 750        16        64 Kb
  3       64           -1        16        64 Kb
  4       80   10 522 782        16        64 Kb
  5       96           -1        16        64 Kb
  6      112    2 835 733        16        64 Kb
  7      128           -1        16        64 Kb
  8      144    2 833 946        16        64 Kb
  9      160           -1        16        64 Kb
 10      176    1 260 034        16        64 Kb
 11      192           -1        16        64 Kb
 12      208      572 534        16        64 Kb
 13      224           -1        16        64 Kb
 14      240   10 522 942        16        64 Kb
 15      256
[/cut]
Так представляется сжатие в NTFS или разреженные файлы.
[cut]On the NTFS file system, the value (LONGLONG) –1 indicates either a compression unit that is partially allocated, or an unallocated region of a sparse file.[/cut]
В Contig такие позиции помечаются как виртуальные:

[cut]

Код: Выделить всё

[Cluster] Runlength
  [0] 48 VIRTUAL
  [48] 16
  [64] 16 VIRTUAL
  [80] 16
  [96] 16 VIRTUAL
  [112] 16
  [128] 16 VIRTUAL
  [144] 16
  [160] 16 VIRTUAL
  [176] 16
  [192] 16 VIRTUAL
  [208] 16
  [224] 16 VIRTUAL
  [240] 16
[/cut]
В модуле есть переменная bVirt. Если bVirt=1, отрицательные значения заменяются на слово "VIRT". Слово можно заменить на другое при необходимости.

Добавлено: Сб окт 18, 2025 23:33
A55555
Orion9 писал(а): Замените этот блок (где-то с 205-ой строки) на новый:

[cut]

Код: Выделить всё

g_FindTorrTask = 0
SendMessage(h_WinFindTorr, 0x0010, 0, 0)
If DllCall("DestroyWindow", "handle", h_WinFindTorr) Then h_WinFindTorr = 0
If gTorrentCount = 0 Then       
   MsgBox("Найдено: " & gTorrentCount & auCRLF & auCRLF & found, "Autorun", 64)
Else
[/cut]
[cut]

Код: Выделить всё

g_FindTorrTask = 0
SendMessage(h_WinFindTorr, 0x0010, 0, 0)
If DllCall("DestroyWindow", "handle", h_WinFindTorr) Then h_WinFindTorr = 0
If gTorrentCount = 0 Then
   Local mode = files > 1 ? 0 : 1
   Local sMode = (mode = 0 ? "«Имя и размер»" : "«Только имя»")
   MsgBox("Найдено: " & gTorrentCount & auCRLF & auCRLF & _
          "Файлов в торренте: " & files & " " & auCRLF & auCRLF & _         
          "Продолжить в режиме " & sMode & "?", "Autorun", 3+64+0)
   If EXTENDED = 6 Then Return FindTorrentParts(mode)
Else
[/cut]
Я не стал пркидывать весь модуль, там сейчас лишние строки есть для отладки, которые использовались для поиска причин зависаний. Могут еще пригодится, пока проблема не будет решена.
A55555 писал(а):А так всё тихо, не зависало
В ТС х32 действительно не виснет, и это хорошо. Можете пока этой версией Тотала попользоваться, думаю, разницы между 64-битной не заметите. Зато в 32-битном ТС подсказку можно по Ctrl вызывать.
Orion9
спасибо, работает. И в данном случае, то что выдает сообщение-запрос на попробовать другим методом даже хорошо, это подсказывает, что торрент видимо не полностью скачанный, ещё и сколько файлов за этим торрентом в полном его виде закреплено можно узнать.

Да, я сейчас спокойно пользуюсь 32-битной версией, разницы пока не встречал, всё нормально.

Добавлено: Вс окт 19, 2025 23:12
Orion9
A55555 писал(а):Да, я сейчас спокойно пользуюсь 32-битной версией, разницы пока не встречал, всё нормально.
В wiki есть статья, которая хорошо описывает разницу между ТСх64 и ТСх32, на случай если будет интересно:
https://tckb.ru/wiki/total_commander_x64/
Очень хорошая статья. Всё просто по полочкам.

Loopback
Вопрос немного внезапный: а почему идентификаторы пунктов меню не перехватываются в WM_SYSCOMMAND? Сами пункты нормально создаются и отображаюся, но вот с их обработкой ничего не получается:

Код: Выделить всё

hMenu = DllCall("GetSystemMenu", "handle", AUTORUN_TCHANDLE, "bool", 0, "handle")
If hMenu > 0 Then
   DllCall("AppendMenuW", "handle", hMenu, "uint", 2048, "uint_ptr", 0, "ptr", 0)
   DllCall("AppendMenuW", "handle", hMenu, "uint", 0, "uint_ptr", 1000, "wstr", "About") 
   DllCall("AppendMenuW", "handle", hMenu, "uint", 1, "uint_ptr", 1001, "wstr", "Settings...") 
EndIf

# WM_SYSCOMMAND
SetMessageAction /P /DM 0x0112 "SysMenuCallback" 	

Func SysMenuCallback(hWnd, uMsg, wParam, lParam)
    OutputDebugString(wParam)
    If wParam = 1000 Then 
       MsgBox("About")
    ElseIf wParam = 1001 Then 
       MsgBox("Settings")
    EndIf
EndFunc
Можно накидать интересных пунктов меню, включая логотип, который прикольно кстати смотрится:

Код: Выделить всё

hBitFile = DllCall("LoadImageW", "handle", 0, _
                                 "wstr", "d:\Temp\Images\logo.bmp", _
                                 "uint", 0, "int", 0, "int", 0, _
                                 "uint", 0x00000010, "handle")
hMenu = DllCall("GetSystemMenu", "handle", AUTORUN_TCHANDLE, "bool", 0, "handle")
If hMenu > 0 Then
   DllCall("AppendMenuW", "handle", hMenu, "uint", 2048, "uint_ptr", 0, "ptr", 0)
   DllCall("AppendMenuW", "handle", hMenu, "uint", 0, "uint_ptr", 1000, "wstr", "About") 
   DllCall("AppendMenuW", "handle", hMenu, "uint", 4, "uint_ptr", 1001, "ptr", hBitFile)
   DllCall("AppendMenuW", "handle", hMenu, "uint", 1, "uint_ptr", 1002, "wstr", "Settings...") 
EndIf

Добавлено: Пн окт 20, 2025 11:50
Loopback
Orion9 писал(а):а почему идентификаторы пунктов меню не перехватываются в WM_SYSCOMMAND?
В обработчик есть вход, но только при открытии и закрытии меню - любого. Возможно, тут нужен другой хук, например WH_GETMESSAGE (в основе функции лежит WH_CALLWNDPROC). Вроде по коду каких-то серьезных ошибок не видно.

Так что конкретно не готов ответить.
Orion9 писал(а):Можно накидать интересных пунктов меню, включая логотип, который прикольно кстати смотрится:
У меня в todo какое-то время даже был пункт по добавлению своих пунктов в меню, но все-таки польза этого не столь велика, а с появлением DllCall необходимость и вовсе отпала.

Добавлено: Пн окт 20, 2025 13:09
Orion9
Loopback писал(а):Вроде по коду каких-то серьезных ошибок не видно.
По вашему коду, или по моему?

Если по моему, то код должен работать. Например, если всё повесить на окно диалога выбора цвета, то он работает.
[cut]

Код: Выделить всё

# системный диалог выбора цвета
RegisterCommand 70053 "ColorSelect"

Func ColorSelect(lParam)
    Local occ = Callback("CChooseProc", "hwnd;uint;wparam;lparam")
    Local cls = Buffer(64), buf = Buffer(auX64 ? 72 : 36)
    cls.Zero()
    cls.SetNum(0, "dword", 0x000000FF, _
                  "dword", 0x0000FF00, _
                  "dword", 0x00FF0000)
    buf.Zero()
    If auX64 Then
        buf.SetNum(0,  "dword", buf.size)
        buf.SetNum(8,  "hwnd", AUTORUN_TCHANDLE)
        buf.SetNum(16, "hwnd", 0, _
                       "uint", 0x00808080)
        buf.SetNum(32, "ptr", cls.ptr, _                      
                       "dword", 0x00000113)
        buf.SetNum(48, "lparam", 0, _
                        "ptr", occ.ptr)
    Else
        buf.SetNum(0, "dword", buf.size, _   
                      "hwnd", AUTORUN_TCHANDLE, _         
                      "hwnd", 0, _
                      "uint", 0x00808080, _
                      "ptr", cls.ptr, _
                      "dword", 0x00000113, _
                      "lparam", 0, _
                      "ptr", occ.ptr)
    EndIf    
    DllCall("comdlg32.dll\ChooseColorW", "ptr", buf.ptr, "bool")
    Free(buf, cls, occ)
EndFunc

Func CChooseProc(hWnd, uMsg, wParam, lParam)
    Static IDOK = 1, IDCANCEL = 2, WM_GETICON =  0x7f, WM_SETICON =  0x80, _
           WM_INITDIALOG = 0x0110, WM_COMMAND = 0x0111, WM_SYSCOMMAND = 0x0112           
    Static r, g, b
    If uMsg = WM_INITDIALOG Then
       Static hIco = SendMessage(AUTORUN_TCHANDLE, WM_GETICON, 2, 0)                     
       SendMessage(hWnd, WM_SETICON, 0, hIco)
       Local hMenu = DllCall("GetSystemMenu", "handle", hWnd, "bool", 0, "handle")
       Local hBitMap = CreateColor()
       If hMenu > 0 Then
          DllCall("AppendMenuW", "handle", hMenu, "uint", 2048, "uint_ptr", 0, "ptr", 0)
          DllCall("AppendMenuW", "handle", hMenu, "uint", 0, "uint_ptr", 1000, "wstr", "About") 
          DllCall("AppendMenuW", "handle", hMenu, "uint", 4, "uint_ptr", 1001, "ptr", hBitMap)                    
          DllCall("AppendMenuW", "handle", hMenu, "uint", 0, "uint_ptr", 1002, "wstr", "Settings...")          
       EndIf
    ElseIf uMsg = WM_SYSCOMMAND Then
       If wParam = 1000 Then
          MsgBox("About")       
          Return 1
       ElseIf wParam = 1001 Then 
          MsgBox("Bitmap")
          Return 1
       ElseIf wParam = 1002 Then 
          MsgBox("Settings")
          Return 1          
       EndIf          
    ElseIf uMsg = WM_COMMAND Then
       If wParam = IDOK Or wParam = IDCANCEL Then
          Return 0
       EndIf   
       r = WinGetText(WinFind(hWnd, "Edit", 4))
       g = WinGetText(WinFind(hWnd, "Edit", 5))
       b = WinGetText(WinFind(hWnd, "Edit", 6))
       WinSetText("RGB: " & r & ", " & g & ", " & b, hWnd)
    EndIf  
    Return 0
EndFunc

Func CreateColor()
    Local buf = Buffer(16)    # RECT
    buf.Zero()
    buf.SetNum(0, "Int", 0, _
                  "Int", 0, _
                  "Int", 160, _
                  "Int", 160)
    Local hdcScreen  = DllCall("GetDC", "ptr", 0, "ptr")
    Local hdcMem = DllCall("CreateCompatibleDC", "ptr", hdcScreen, "ptr")
    Local hbmColor = DllCall("CreateCompatibleBitmap", "ptr", hdcScreen, "int", 160, "int", 160,"ptr")
    Local hbmOld = DllCall("SelectObject", "ptr", hdcMem, "ptr", hbmColor, "ptr")  
    Local hBrush = DllCall("CreateSolidBrush", "UInt", 0xAF0000, "Ptr")       
    DllCall("FillRect", "Ptr", hdcMem, "Ptr", buf.ptr, "Ptr", hBrush)
    DllCall("DeleteObject", "Ptr", hBrush)
    DllCall("SelectObject", "ptr", hdcMem, "ptr", hbmOld, "ptr")
    DllCall("DeleteDC", "Ptr", hdcMem)    
    DllCall("ReleaseDC", "Ptr", 0, "Ptr", hdcScreen)    
    Free(buf)    
    Return hbmColor
EndFunc
[/cut]
Здесь правда вместо логотипа отображается синий квадрат малевича, но для тестов самое то.

Однако вижу, что с отображением графического элемента в ТС х64 есть проблема - элемент не всегда появляется. Кстати, заметил, что в ТС х64 поток заголовка тоже вылетает без ошибки и всё зависает. Мне кажется, раньше такого не было, хотя я не и не часто пользуюсь этой версией, чтобы судить.
Loopback писал(а):все-таки польза этого не столь велика
Да нормальная польза. Своеобразное меню со специальными для этого пунктами.

Добавлено: Вт окт 21, 2025 12:30
Loopback
Orion9 писал(а):По вашему коду, или по моему?
Да, неоднозначно вышло. По вашему, конечно.
Orion9 писал(а):Если по моему, то код должен работать. Например, если всё повесить на окно диалога выбора цвета, то он работает.
Тут все-таки проще, поскольку вы сами реализуете оконную функцию.

В случае тотала сложнее, нужно либо делать сабклассинг (без возможности последующего его снятия), либо для отлова сообщений приходится ставить хуки. SetMessageAction ставит хук WH_CALLWNDPROC, и он даже что-то ловит, иначе OutputDebugString(wParam) ничего бы не выводил. Но по какой-то причине никакие сообщения от пунктов системного меню в него не приходят. Не думаю, что это проблема SetMessageAction, возможно тут требуется другой тип хука.
Orion9 писал(а):Кстати, заметил, что в ТС х64 поток заголовка тоже вылетает без ошибки и всё зависает. Мне кажется, раньше такого не было, хотя я не и не часто пользуюсь этой версией, чтобы судить.
К сожалению, я тоже ей пользуюсь нечасто, поэтому времени тестирования ей уделяется гораздо меньше. Но как уже писал раньше, сам по себе поток не может вылететь. Возможно в какой-то функции или даже структурном элементе возникло исключение.

Добавлено: Вт окт 21, 2025 16:33
Orion9
Loopback писал(а):возможно тут требуется другой тип хука.
Да ладно. Кликать, конечно, хотелось бы, но даже в таком виде можно извлечь пользу, например использовать это меню как информационную панель.
Loopback писал(а):сам по себе поток не может вылететь.. возникло исключение
Что именно происходит не важно, поток с заголовком вылетает - я проверял два раза. Косвенно это указывает, что Everything тут ни при чем (правда я и раньше писал, при поиске торрентов вылететь может на любом из восьми шагов цикла, не только на FileFindEv, если верить данным лога).

Добавлено: Ср окт 22, 2025 10:51
Loopback
Orion9 писал(а): поток с заголовком вылетает - я проверял два раза
Ну что ж, надо будет побольше тестировать на x64. Вчера вечером запустил его на всю ночь с потоком, меняющим заголовок, пока проблем не возникло.

Добавлено: Ср окт 22, 2025 11:34
Orion9
Loopback писал(а):Вчера вечером запустил его на всю ночь с потоком, меняющим заголовок, пока проблем не возникло.
Заголовок так просто не валится, здесь какие-то факторы должны сойтись. Однако он тоже завершается аварийно, как и поток с FileFindEv. И кстати, я вспомнил, что и ранее наблюдал эту проблему, но очень редко, поэтому и писать об этом ничего не стал.

Однако с поиском торрентов всё иначе. Поток с FileFindEv вылетает очень очень часто. Иногда бывает только его запустишь, еще только 0%, а он уже вылетел.

Не поленился с утра, провел немного тестов. Сначала о хорошем. Раз 30 запускал и перезапускал х64, но MsgBox, поставленный в начало autorun.cfg, ни разу никуда не пропадал. При этом увеличивал нагрузку на проц от 80% до 100%, а также ставил MsgBox перед секцией финализации, и все-равно всё нормально. Неужели проблема решена?

Заголовок у меня так и не завис, а вот поиск торрентов - как пить дать. Думаю, с этого и нужно начинать поиски причины: именно здесь проблема регулярно себя проявляет и легко воспроизводится.

Эмблема logo.bmp загружается как пункт меню в лучшем случае через раз. Синий квадрат иногда загружается, а иногда не загружается, а иногда загружается как чёрный. В ТС х32 всё намного стабильнее в этом плане.

В общем всё это похоже на какие-то дурацкие приколы ленивого Lazarus, других объяснений пока нет.

Кстати, в GetState не хватает отчета о состоянии SetHotkeyAction - какие комбинации заняты и какие функции на них висят. Эти функции тоже выполняются отдельных потоком, но во время своего исполнения их не видно в GetState("threads").

Добавлено: Ср окт 22, 2025 23:14
Orion9
Loopback
Если это как-то поможет, заголовок вылетает при дополнительной нагрузке на плагин. Я запустил нескольких потоков и параллельно запустил подсчёт времени воспроизведения каталога - заголовок вылетел и перестал отображаться в GetState. Ещё одна попытка это сделать привела к тому, что сам тотал (х64) завалился. В обшем как ни крути а какая-то проблема с этой версией ТС все-таки есть. ТС х32 к подобным манипуляциям устойчив.

Добавлено: Чт окт 23, 2025 10:26
Loopback
Orion9 писал(а): Раз 30 запускал и перезапускал х64, но MsgBox, поставленный в начало autorun.cfg, ни разу никуда не пропадал. Неужели проблема решена?
Хотелось бы сказать да, но нет. Скажем так - у меня вероятность срабатывания более 90%. Думаю, можно и полностью исправить, но тогда придется отказаться от создания диалога с помощью DialogBox и полностью создать свое окно с нуля.
Orion9 писал(а):В общем всё это похоже на какие-то дурацкие приколы ленивого Lazarus, других объяснений пока нет.
Что-то может быть, конечно, но проблема с потоком маловероятно что связана с ним.
Orion9 писал(а):Кстати, в GetState не хватает отчета о состоянии SetHotkeyAction - какие комбинации заняты и какие функции на них висят.
Из плагинов статистики нет, слишком много затрат для реализации.
Orion9 писал(а): В обшем как ни крути а какая-то проблема с этой версией ТС все-таки есть.
Да я не отрицаю, но мне сначала придется её повторить. Время появится, буду тестировать.

Добавлено: Вс окт 26, 2025 11:28
Orion9
Эх, маэстро маэстро... А я ведь так верил.

Ну и где то самое "случайное" перетаскивание кнопок на панели?

Код: Выделить всё

Global gBarWndProc, hBarWnd
Global bwp = Callback("BarWndProc", "hwnd;uint;wparam;lparam")

hBarWnd = RequestInfo(28)
gBarWndProc = DllCall("SetWindowLong" & (auX64 ? "PtrW" : "W"), _
                      "hwnd", hBarWnd, "int", -4, "long_ptr", bwp.Ptr, "ptr")
                      
Func BarWndProc(hWnd, uMsg, wParam, lParam)
    Static IsDrag = 0, _
           hTotal = DllCall("GetModuleHandleW", "Ptr", 0, "handle"), _
           WM_MOUSEMOVE    = 0x0200, _
           WM_LBUTTONDOWN  = 0x0201, _
           WM_LBUTTONUP    = 0x0202           
    Static drag_system = DllCall("LoadCursor", "Ptr", 0, "Ptr", 32646), _
           stop_system = DllCall("LoadCursor", "Ptr", 0, "Ptr", 32648), _
           drag_cursor = DllCall("LoadCursor", "Ptr", hTotal, "Ptr", 22), _
           stop_cursor = DllCall("LoadCursor", "Ptr", hTotal, "Ptr", 21)            
    Switch uMsg
       Case WM_LBUTTONDOWN 
          If BitAND(wParam, 0x0008) Then
             MouseGetPos("x","y")          
             IsDrag = 0      
             If DllCall("DragDetect", "hwnd", hWnd, "int64", MakeInt(y, x, 2)) Then
                IsDrag = 1
                WinSetStyle(0x80000, 3, hWnd)
                DllCall("SetLayeredWindowAttributes", "hwnd", hWnd, "ptr", 0, "byte", 128, "dword", 2)          
                DllCall("SetCursor", "Ptr", drag_cursor)
             EndIf
          EndIf    
       Case WM_LBUTTONUP
          If IsDrag = 1 Then
             WinSetStyle(0x80000, 5, hWnd)
             IsDrag = 0             
          EndIf  
       Case WM_MOUSEMOVE
          If IsDrag = 1 And BitAND(wParam, 0x0001) Then
             If WinFromPoint() = hWnd Then
                DllCall("SetCursor", "Ptr", drag_cursor)
             Else
                DllCall("SetCursor", "Ptr", stop_cursor)               
             EndIf
             WinSetText(BitAND(lParam, 0xFFFF) & " - " & BitAND(BitShift(lParam,16), 0xFFFF))
          EndIf   
    EndSwitch
    Return DllCall("CallWindowProcW", _
                   "ptr", gBarWndProc, "hwnd", hWnd, "uint", uMsg, "wparam", wParam, "lparam", lParam)
EndFunc

Добавлено: Пн окт 27, 2025 22:20
Loopback
Orion9 писал(а):Ну и где то самое "случайное" перетаскивание кнопок на панели?
Ну да, с Ctrl вряд ли удастся случайно что-то утянуть :)