Orion9

|
Posted: Wed Oct 15, 2025 19:36 Post subject: |
|
|
Ну ладно, эксперементировать можно долго, поэтому вот более менее стабильная версия
 Clusters.aucfg Code: | 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 |
Модуль подключается стандартным образом, но кроме этого в файле основной конфигурации должны быть кастомные поля, глобальные переменные и инициализация из Clusters.ini.
 autorun.cfg Code: | 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 |
Пути можно подправить на свои, а в файле Clusters.ini есть описание настроек:
 Clusters.ini Code: | [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 |
В Wincmd.ini (или в секции редиректа) необходимо добавить поисковые шаблоны и поля кастомных колонок, а также можно повесить em_команду с кодом вызова подсказки на горячую клавишу (в примере Ctrl+F)
 Hidden text Code: | [Shortcuts]
C+F=em_file_fragments
CS+F=em_file_fragments |
 Hidden text Code: | [CustomFields]
Titles=File clusters
Headers=Размер\nFrag
Contents=[=tc.size.bkMGT2]\n[=autorun.Frag] |
 Hidden text Code: | [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 |
Дополнительный набор по умолчанию называется File clusters, но его можно переименовать в usercmd.ini
 usercmd.ini Code: | [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 |
 Кнопка Code: | TOTALCMD#BAR#DATA
70401
%COMMANDER_EXE%
Кластеры файла|Ctrl - Меню|Shift - Карта диска|Alt - Скопировать в буфер
-1 |
Помимо информационно-развлекательных целей функционал может приносить пользу для поиска файлов в заранее указанных диапазонах. Но глубоких и длительных тестов пока не проводилось, поэтому могут быть косяки и ошибки.
Добавлено спустя 7 минут:
А, забыл добавить. Меню можно вызвать по Alt+Win+F. Эта комбинация жестко прописана в модуль. |
|