ReallyUltraFast 320mph, sorry 3200mph
10x-1000x more fast
I rewrote parsing loop. Now it not only checks first and last occurence thus narrowing field of search, but more than this: It splits searchList in e.g. 32 parts (number is configurable) and in each slice finds first and last occurence of *any* of words in text entered by user, narrowing searchField or totally excluding slice. Due to using such instantly working commands as inStr and stringMid, time to decide whether to parse given slice is reduced to milliseconds. Thus total time to seek text is further reduced.
[new::AHK extender::] Full string file version info fetching for executables/dlls
Code:
/*
___________________________________________
[AHK 1.0.42+]
320MPH by Rajat
Ultra Fast Anything-Launcher
Order of Results:
- Recently used items
- Items with name starting with search querry
- Items with search querry anywhere in their name
- Items with search querry anywhere in their path
---=[ extra 1000% speedUp by wOxxOm @11/Mar/2006]=--
On heavyload runLists with 5'000-50'000 files
the gain is typically several times,
and in cases you look for file with rather
unique name the gain may be up to
1sec/1msec = 1000 times faster (and even more)
CODE WAS OPTIMIZED very carefully,
with time-profiling.
0. ListView is assembled in one pass in main and the only now
loop,parse. No double pass. No waste of time
1. Icons for exe/lnk are loaded after all the search is done
[DEFERRED icon loading]
2. Icons for other filetypes are cached in just one instance
throughout the ListView
3. script doesn't re-queries files of same ext.
on scanning files, except lnk/exe/+user-defined
4. simple handling of file-types with iconHandler in CLSID-
they don't show their custom icons as in explorer
but instead of white plug you will see creator-app icon
5. Keys added for easier navigation: PgUp/PgDn,Ctrl-Home/End
WheelUp/Down and shift-WheelUp/Down to scroll a page
Ctrl-Enter opens folder containing item
Shift-Enter always runs command via ComSpec command prompt
6. Keys are not system-wide, they bound to script window only
7. Response time increased, now script may use 10msec delay
without problems on fast machines, change it if you like
8. Comments are almost gone. I don't like them.
Script is very self-explantory ;-)
9. Some simple functions - wrappers for commands added -
I suppose that readability of script increased greatly
10. fileSize & DateModified columns added.
Updated by default only on deferred icon loading
in order to not make us wait and experience lags
11. I rewrote parsing loop. Now it not only checks first and last occurence
thus narrowing field of search, but more than this: It splits searchList
in e.g. 32 parts (number is configurable) and in each slice finds first
and last occurence of *any* of words in text entered by user, narrowing
searchField or totally excluding slice. Due to using such instantly
working commands as inStr and stringMid, time to decide whether to
parse given slice is reduced to milliseconds.
Thus total time to seek text is further reduced.
12. Full string file version info fetching for executables/dlls
___________________________________________
*/
menu,tray,icon,%systemRoot%\system32\shell32.dll,131
ListFile=320mph.lst
PathList=%A_StartMenu%|%A_StartMenuCommon%|%programFiles%|%systemRoot%|d:\|e:\
PathExclude=%A_Desktop%\Osman|%A_Desktop%\2006|E:\Games\
; exclude some not needed subpaths of previously defined list
TypeList:="|exe|lnk|cdr|ai|ahk|tif|jpg|psd|mp3|wma|ogg|fdb|" ; must begin & end with |
FetchFullVerExt:="|exe|dll|drv|fon|ttf|vxd|sys|cpl|ocx|" ; in case you include them in TypeList
; or if lnk-file points to them
; then Full VerInfo will be shown
ExcludeList="about|history|readme|remove|uninstall|license|unins"
ExcludeEmptyEXT=1 ; exclude items with empty extensions - usually some preferences/settings
AlwaysScan=%UserProfile%\Recent
;|%A_Desktop%
guiWidth:=620 ; window width
guiLines#:=30 ; window height in rows of text
MainWnd=320MPH -- Rajat
MaxLastUsed=30 ; recent list of our launched items
WaitTime=10 ; 10ms: how long to wait till search is performed after typing text
LaunchIfOnly1=0 ; if only one item found then launch it immediately
ShowIcons=1
ShowDeferred=1 ; show slow [exe/lnk] icons after search is done + get fileSize/Date
ShowFirst#Icons:=guiLines#
; these number of EXE/LNK icons will be showed if ShowIcons is true
; during main scan - this option is for fine tuning mainloop performance.
; Bear in mind that during time to fetch exe/lnk icon
; we can find about 100-1000 items
NoCacheExt:="|EXE|LNK|" ; these icons are always different from each other so don't cache them
SplitParts=32 ; speed-up splitting of searchList for finding dispersed text
setFormat,float,0.1
AutoTrim, Off
SetBatchLines, -1
SetKeyDelay, 0
dllCall("charUpper",str,NoCacheExt)
IconCache= ;will be: |<ext>|hIcon|<ext>|hIcon|...
; - ext is surrounded by <> to cache blank icon for files with no Ext
;use external ini file for compiled scripts
IniFile:=iif(inStr(A_ScriptName,".ahk",0),A_ScriptFullPath,A_ScriptDir "\320MPH.ini")
IniRead, UsedList, %IniFile%, Settings, UsedList
IfEqual, UsedList, ERROR
UsedList =
;___________________________________________
startLoading:=A_TickCount
;create scanned result list on first run
IfExist, %A_ScriptDir%\%ListFile%
{ ItemList =
fileGetSize,ILsize,%A_ScriptDir%\%ListFile%
if (ILsize>50000) ; large lists are opened a few seconds
SplashImage,, W190 H30 B1,, Reading RunList...,
Loop, Read, %A_ScriptDir%\%ListFile%
{ IfEqual, A_LoopReadLine,, Continue
ItemList=%ItemList%|%A_LoopReadLine%
}
}
Else
Gosub, ButtonScan
TimeToLoad:=(A_TickCount-startLoading)/1000
startLoading:=A_TickCount
;scan always updated list
Loop, Parse, AlwaysScan, |
{ SplashImage,, B1,, Scanning %A_LoopField%...,
Loop, % addSlash(A_LoopField) "*.*", 0, 1
{ SplitPath, A_LoopFileFullPath, FName, FDir, FExt
Cont=0 ; check if in path-excludes
loop,parse,PathExclude,|
if (strCompare(A_LoopField,strLeft(FDir,strLen(A_LoopField)))=2)
{ cont=1
continue
}
IfEqual, Cont, 1, Continue
if (!FExt and ExcludeEmptyEXT) or !strPos(TypeList,"|" FExt "|")
Continue ; not-allowed filetype
Cont=0 ; check for file-name excludes
Loop, Parse, ExcludeList, |
If strPos(FName,A_LoopField)>0
{ Cont=1
Break
}
IfEqual, Cont, 1, Continue
; all checks passed, file is ours
RecentList=%RecentList%|%A_LoopFileFullPath%
}
}
StringTrimLeft, RecentList, RecentList, 1
ItemList=%RecentList%%ItemList%
ItemListU:=strUpper(ItemList) ; to speed-up further *case-Sens* checks.
; strUpper is LanguageLocale-friendly
TimeToScan:=(A_TickCount-startLoading)/1000
Gui, -Caption +Border
Gui, Add, Edit, x6 y5 w300 h20 vCurrText,
Gui, Add, ListView, x6 y35 w%guiWidth% r%guiLines#% vSelItem HScroll gSelection AltSubmit count1000, Name|Ext|Size|Date|Folder
guiControlGet,SelItem,pos
y:=SelItemY+SelItemH+8
Gui, Add, Text, x6 y%y% w400 h20 vResults,
Gui, Font, S10 CDefault Italic Bold, Verdana
Gui, Add, Text, x450 yp-3 w150 h20 Right, %MainWnd%
Gui, Add, Button, 0x8000 x316 y5 w50 h20 Default, &Open
Gui, Add, Button, 0x8000 x376 y5 w50 h20, &Scan
LV_ModifyCol(1, 100) ; name
LV_ModifyCol(2, 60) ; ext
LV_ModifyCol(3, "right 60") ; size is right-aligned
LV_ModifyCol(4, 60) ; date
LV_ModifyCol(5, 250) ; path
ImageListID1:=IL_Create(20, 100) ; step +100 for faster growth
LV_SetImageList(ImageListID1)
NotCachedIconsCount:=0
if (A_AhkVersion >= "1.0.42")
HotKey,ifWinActive,%mainWnd%
HotKey,~Up,keyMovement
HotKey,~Down,keyMovement
HotKey,~PgUp,keyMovement
HotKey,~PgDn,keyMovement
HotKey,~^Home,keyMovement
HotKey,~^End,keyMovement
HotKey,~*WheelUp,ScrollWheel
HotKey,~*WheelDown,ScrollWheel
HotKey,^Enter,ButtonOpen ; Control-Enter I use for folder-open
HotKey,^a,keyACDSee
HotKey,!a,keyACDSee
HotKey,^Del,keyControlDel ; Ctrl-Del/BkSp erase input text.
HotKey,^BackSpace,keyControlDel ; If there is no input text, then remove file from recent
SplashImage,OFF
Gui, Show, , %MainWnd%
lastText:="fadsfSDFDFasdFdfsadfsadFDSFDf"
SetTimer, GetText, 200
Sleep, 200
Control, Choose, 1, SysListView321, %MainWnd%
Return
keyMovement:
ifWinActive %mainWnd%
{ ControlGetFocus, CurrCtrl, %MainWnd%
If (CurrCtrl="Edit1")
{ key:=strMid(A_ThisHotKey,iif(strLeft(A_ThisHotKey,1)="~",2,1))
ControlSend, SysListView321, {%key%}, %MainWnd%
}
}
return
ScrollWheel:
ifWinActive %mainWnd%
{ ControlGetFocus, CurrCtrl, %MainWnd%
stringMid,dir,A_ThisHotKey,% inStr(A_ThisHotKey,"Wheel")+5,4
ControlSend, SysListView321
, % iif(GetKeyState("LShift")
,iif(dir="Up","{PgUp}","{PgDn}")
,iif(CurrCtrl="Edit1","{" dir "}",""))
, %MainWnd%
}
return
keyControlDel:
ifWinActive %mainWnd%
{ If (ctlGetText("Edit1",MainWnd))
{ ControlSetText,Edit1
Return
}
SelItem:=LV_GetNext()
LV_GetText(FName, SelItem, 1)
LV_GetText(FExt, SelItem, 2)
LV_GetText(FDir, SelItem, 5)
Pth=%FDir%\%FName%.%FExt%
StringReplace, UsedList, UsedList, |%pth%,, A
IniWrite, %UsedList%, %IniFile%, Settings, UsedList
LastText=x
}
Return
keyControlEnter:
ifWinActive %mainWnd%
{ SelItem:=LV_GetNext()
LV_GetText(FDir, SelItem, 5)
run,%FDir%
}
return
GetText:
IfLess, A_TimeIdlePhysical, %WaitTime%, Return
ControlGetText, CurrText, Edit1, %MainWnd%
If strCompare(CurrText,LastText)=2
return
toolTip
LastText:=CurrText
CurrTextU:=strUpper(CurrText) ; for further speed-ups of *case-Sens* comparing
startSearchTime:=A_TickCount
;text is empty so display recent
If !CurrText
{ lvCleanup()
StringTrimLeft, UsedList0, UsedList, 1
Loop, Parse, UsedList0, |
{ addToList(A_LoopField)
IfGreater, Count, %MaxLastUsed%, Break
}
}
;from all items_____________________________
timeToAddList:=0
TimeToAddIcon:=0
If (CurrText)
{ lvCleanup()
GuiControl,,Results,Starting search...
allList:=usedList . itemList
stringLen,allLen,allList
partLen:=round(allLen/splitParts+0.99,0) ; split field of search
partStart:=1 ; current slice offset in AllList
Count:=0 ; items found
r1:=1 ; item stuffing index for Results type1 (name begins with currText)
r2:=1 ; Results type2 (name contains currText)
r3:=1 ; Results type3 (fullPath contains currText)
StringLen, Len, CurrText ; speed-up
stringReplace,usedList,usedList,|,|,A useErrorLevel ; count lines in UsedItems
uLines:=errorlevel
;msgbox %usedList%`n%uLines%
loop,%splitParts%
{ i:=inStr(allList,"|",1,partStart+partLen) ; find EOL in end of text-partition
; rip partition
stringMid,searchList,allList,%partStart%,% iif(i=0,allLen,i)-partStart+1
stringLen,step,searchList
partStart+=step ; next loop will take next slice
searchListU:=searchList ; for speed-Up comparing in case-Sens mode
dllCall("CharUpper",str,searchListU) ; non US-LOCALE-friendly
iL:=inStr(searchListU,currTextU,1)
if !(iL)
{ if !inStr(currText," ")
continue ; current slice contains no currText and currText is one word
iL:=step+1 ; length of slice+1
; check individually each word inside CurrText for presence in current slice
loop,parse,currTextU,%A_Space%
{ i:=inStr(searchListU,A_LoopField,1)
iL:=iif(i<iL,i,iL)
}
ifEqual,iL,0,continue ; words from CurrText aren't in current slice
}
; now backward find beginning of line with found text
i:=inStr(strLeft(searchList,iL),"|",1,0)
; backwards find last occurence of currText
j:=inStr(searchListU,currTextU,1,0)
; check for fuzzy search
if !(j)
{ j:=step+1 ; we know by now that somewhere in slice we have all the words from currText
loop,parse,currTextU,%A_Space%
{ jj:=inStr(searchListU,A_LoopField,1,0)
j:=iif(jj<j,jj,j)
}
; now j is at least as first occurence (i), or greater
}
j:=inStr(searchList,"|",1,j+1) ; find EOL of last occurence
j:=iif(j=0,step+1,j)
; now we will have clean shaven piece of text containing our currText
stringMid,SearchList,SearchList,% i+1,% j-i-1
;___________________________________________
; Advanced Search;;
Loop, Parse, SearchList, |
{ ;check for change in search querry
if (A_TickCount-startSearchTime>A_TimeIdlePhysical) ;user activity
{ ControlGetText, CurrText, Edit1, %MainWnd%
If strCompare(CurrText,LastText)<>2
Return
}
CurrItem:=A_LoopField
CurrItemU:=strUpper(A_LoopField) ; speed-up comparing
;remove duplicate entry that exists both in usedlist and itemlist
if (count>=uLines)
IfInString,usedList,|%CurrItem%|,Continue
SplitPath, CurrItemU, FName, FDir, FExt, FNameNoExt, FDrive
;Matching leftmost text using winAPI function that can look at specified length
If strCompare(FName,CurrTextU,Len)=2
{ AddToList(CurrItem,r1)
r1 ++
r2 ++ ; Res2/3 insertion points are increased
r3 ++
Continue
}
;Matching file name only :: fuzzy search
MatchName=1
MatchFull=1
Loop, Parse, CurrTextU, %A_Space%
{ MatchName:=iif(inStr(FName,A_LoopField),MatchName,0)
MatchFull:=iif(inStr(CurrItemU,A_LoopField),MatchFull,0)
if !MatchName and !MatchFull
break
}
If (MatchName)
{ AddToList(CurrItem,r2)
r2 ++
r3 ++ ; Res3 insertion point is shifted
Continue
}
If (MatchFull)
{ AddToList(CurrItem,r3)
r3 ++
Continue
}
}
}
if !Count
lvCleanup()
}
;post results
Results:=LV_GetCount()
timeTotal:=(A_TickCount-startSearchTime)/1000
timeInList:=timeToAddList/1000
timeInIcons:=TimeToAddIcon/1000
GuiControl,, Results
,Results=%Results% - time %timeTotal% s`, listAdd: %timeInList% s`, icons: %timeInIcons% s
GuiControl,+Redraw,SelItem
LV_ModifyCol()
if (results=1) and LaunchIfOnly1
send {Enter}
if (ShowIcons and ShowDeferred and r1<Results)
{ timeSpent:=0 ; time-profiling for fun
TimeToAddIcon:=0
lvIndex:=0
setTimer,UpdateIcons,5 ; Go fetch the icons!
}
Return
UpdateIcons:
; deferred icon loading routine. Runs not more than 100ms
timeEnter:=A_TickCount
if (A_TickCount-startSearchTime>A_TimeIdlePhysical) ;user activity
{ ControlGetText, CurrText, Edit1, %MainWnd%
If strCompare(CurrText,LastText)<>2
{ setTimer,UpdateIcons,OFF
Return
}
}
loop,
{
if (lvIndex>=Count)
{ setTimer,UpdateIcons,OFF
lvIndex:=0
lv_ModifyCol()
guiControl,+redraw,SelItem
timeSpent:=timeSpent/1000
timeInIcons:=TimeToAddIcon/1000
timeTotal2:=timeTotal+timeSpent
GuiControl,, Results,% "Results=" Results ", total time: " timeTotal2
. "s, deferred: " timeSpent "s, listAdd: " timeInList "s, icons: " timeInIcons "s"
return
}
lvIndex ++
lv_GetText(uiFName,lvIndex,1)
lv_GetText(uiFExt, lvIndex,2)
lv_GetText(uiFDir,lvIndex,5)
Pth=%uiFDir%\%uiFName%.%uiFExt%
fileGetTime,mtime,%Pth%,M
formatTime,mtime,%mtime%,dd.MM.yy HH:mm
fsize=
if (strCompare(uiFExt,"LNK")<>2)
{ fileGetSize,fsize,%Pth%
fsize:=iif(fsize<=9999,fsize
,iif(fsize<=999999,fsize/1024 . "k"
,iif(fsize<=999999999,fsize/1048576 . "M"
,fsize/1073741824 . "G")))
}
; if icon was not cached in prevous runs then take it now
if !strPos(iconCache,"|<" uiFExt ">|")
{ showDeferred:=0
lv_Modify(lvIndex,"icon" setLVIcon(Pth, uiFDir, uiFName, uiFExt))
showDeferred:=1
}
lv_Modify(lvIndex,"col3",fsize,mtime)
if (lvIndex<=ShowFirst#Icons) and (mod(lvIndex,10)=0)
{ lv_ModifyCol()
guiControl,+redraw,SelItem
}
if (A_TickCount-timeEnter>100)
break
}
timeSpent+=A_TickCount-timeEnter
return
ButtonScan:
SplashImage,, W190 H30 B1,, Scanning..,
FileDelete, %A_ScriptDir%\%ListFile%
;generating file list
Loop, Parse, PathList, |
{ IfNotExist, %A_LoopField%, Continue
SplashImage,, B1,, Scanning %A_LoopField%...,
Loop, % addSlash(A_LoopField) "*.*", 0, 1
{ SplitPath, A_LoopFileFullPath, FName, FDir, FExt, FNameNoExt, FDrive
Cont=0
loop,parse,PathExclude,|
{ if (strCompare(A_LoopField,FDir,strLen(A_LoopField))=2)
{ cont:=1
continue
}
}
IfEqual, Cont, 1, Continue
;only filetypes defined are added
if (!FExt and ExcludeEmptyEXT) or !strPos(TypeList,"|" FExt "|")
Continue
;excluding items based on ExcludeList
Cont=0
Loop, Parse, ExcludeList, |
{ IfInString, FName, %A_LoopField%
{ Cont=1
Break
}
}
IfEqual, Cont, 1
Continue
;reaching here means that file is not to be excluded and
;has a desired extension
FileAppend, %A_LoopFileFullPath%`n, %A_ScriptDir%\%ListFile%
}
}
ItemList =
Loop, Read, %A_ScriptDir%\%ListFile%
{ IfEqual, A_LoopReadLine,, Continue
ItemList=%ItemList%|%A_LoopReadLine%
}
ItemList=%RecentList%%ItemList%
ItemListU:=strUpper(ItemList)
LastText =
SplashImage, Off
Return
keyACDsee:
launchItem(ProgramFiles "\ACD Systems\ACDSee\5.0\ACDSee5.exe")
ButtonOpen:
launchItem()
LaunchItem(cmdPrefix="") ;;
{ global MaxLastUsed,MainWnd,UsedList,MaxLastUsed,IniFile
SetTimer, GetText, Off
ShKey:=GetKeyState("Shift")
CtlKey:=GetKeyState("Ctrl")
ControlFocus, SysListView321, %MainWnd%
SelItem:=LV_GetNext()
LV_GetText(FName, SelItem, 1)
LV_GetText(FExt, SelItem, 2)
LV_GetText(FDir, SelItem, 5)
RunItem=%FDir%\%FName%.%FExt%
Gui, Cancel
if (cmdPrefix)
{ dllCall("shell32\ShellExecuteA",int,0,int,0,str,cmdPrefix,str,"""" FDir """",int,0,uint,10)
exitApp
}
;run unrecognised cmd on cmd prompt
If !SelItem or ShKey
{ dllCall("shell32\ShellExecuteA",int,0,int,0,str,ComSpec,str," /c " CurrText " & pause",int,0,uint,10)
;replace /k with /c to remove cmd window after execution
ExitApp
}
;control key down opens host folder
If (CtlKey)
{ StringRight, check, RunItem, 4
IfEqual, check, .lnk
FileGetShortcut, %RunItem%, RunItem
SplitPath, RunItem, FName, FDir, FExt, FNameNoExt, FDrive
Run, Explorer %FDir%,, UseErrorLevel
ExitApp
}
;or simple run
Run, %RunItem%,, ;UseErrorLevel
StringReplace, UsedList, UsedList, |%RunItem%,, A
UsedList=|%RunItem%%UsedList%
;leave only max items in list
StringSplit, UsedItem, UsedList, |
UsedList =
Loop, %MaxLastUsed%
{ CurrItem:=UsedItem%A_Index%
IfEqual, CurrItem,, Continue
UsedList=%UsedList%|%CurrItem%
}
IniWrite, %UsedList%, %IniFile%, Settings, UsedList
ExitApp
}
Selection:
if (A_GuiEvent="DoubleClick")
goto ButtonOpen
SelItem:=LV_GetNext()
LV_GetText(FName, SelItem, 1)
LV_GetText(FExt, SelItem, 2)
LV_GetText(FDir, SelItem, 5)
Pth=%FDir%\%FName%.%FExt%
info=
If (strPos("|lnk|" FetchFullVerExt,"|" FExt "|"))
{ oldErrMode:=dllCall("SetErrorMode",uint,1) ; SEM_FAILCRITICALERRORS=0x0001
ifEqual,FExt,lnk
{ FileGetShortcut, %Pth%, FTarget
FExists:=fileExist(FTarget)
info:=iif(FExists,"","[NOT exists] ") . FTarget
if (FExists)
{ splitPath,FTarget,,,FExt
if (strPos(FetchFullVerExt,"|" FExt "|"))
info := FTarget "`n" FileGetFullVer(FTarget,1|4|8|0x8000,". ")
}
}else
info:=pth "`n" FileGetFullVer(pth,1|4|8|0x8000,". ")
dllCall("SetErrorMode",uint,oldErrMode)
}
if (info)
{ WinGetPos,,,,wH,%MainWnd%
ToolTip, %info%, 8, % wH-12
} else
ToolTip
Return
GuiEscape:
GuiClose:
ExitApp
AddToList(item,row=0) ;;
{ global count,mainWnd,Results,startSearchTime,iconCache
global showIcons,ShowDeferred,timeToAddList,ShowFirst#Icons
static lastResUpd
enterTime:=A_TickCount
Count ++
row:=iif(row=0,Count+1,row)
SplitPath, Item, , FDir, FExt, FNameNoExt
; icons in deferred mode are fetched only if item is in windowview or is already cached
gotIcon:=iif(ShowIcons and (row<ShowFirst#Icons or !ShowDeferred or strPos(iconCache,"|<" FExt ">|"))
,SetLVicon(item,FDir,FNameNoExt,FExt)
,0)
; please note that "gotIcon" = imageList index
; (so it may be the same for a lot of items) and not hIcon handle!!!!
; thus greatly reducing time/memory usage
LV_Insert(row,"Icon" gotIcon, FNameNoExt, FExt, "", "", FDir)
if (Count<ShowFirst#Icons)
{ IfEqual,Count,1, ControlSend, SysListView321, {Down}, %MainWnd%
ifNotEqual,gotIcon,0, guiControl,+redraw,SelItem
LV_ModifyCol() ; update widths only if total num of items is less than ShowFirst#Icons
}
if (row<ShowFirst#Icons) or (A_TickCount-lastResUpd>20)
{ lastResUpd:=A_TickCount
GuiControl,, Results, Searching...%Count%
}
TimeToAddList+=A_TickCount-enterTime
return
}
setLVIcon( byref item, byref FDir, byref FNameNoExt, FExt) ;; FExt mustn't be byref - it changes here ;;
{ global ImageListID1,NoCacheExt,iconCache,TimeToAddIcon
global NotCachedIconsCount,ShowDeferred
enterTime:=A_TickCount
iC#=
hIcon=
iconIndex=
AllowCache:=(strPos(NoCacheExt,"|" FExt "|")=0)
if (AllowCache) or (strCompare(FExt,"LNK")=2 and !ShowDeferred)
{ iC#:=strPos(iconCache,"|<" FExt ">|")
if (iC#)
{ hIcon:=strMid(iconCache,iC#+strLen(FExt)+4,8) ; |<EXT>|dddd|
iconIndex:=hIcon
}
else ; check for IconHandler which is not supported, so workaround needed
{ if !AllowCache
{ FileGetShortcut, %item%, FTarget
splitPath,FTarget,,,FExt
AllowCache:=(strPos(NoCacheExt,"|" FExt "|")=0)
}
regRead,asso,HKCR,.%FExt%
if (asso)
{ regRead,ihGUID,HKCR,%asso%\shellex\IconHandler
if (ihGUID)
{ regRead,assoIcon,HKCR,%asso%\DefaultIcon
if (assoIcon)
{ i:=inStr(assoIcon,",",0)
if !i and inStr(assoIcon,".",1)=0
{ regRead,assoIcon,HKCR,%asso%\shell\open\command
assoIcon:=iif(strLeft(assoIcon,1)=""""
,strMid(assoIcon,2,inStr(assoIcon,"""",1,2)-2), assoIcon) ",0"
i:=strLen(assoIcon)-1
}
i:=iif(i>inStr(assoIcon,".",0),i,strLen(assoIcon)+1)
hIcon:=dllCall("shell32\ExtractIconA",int,0
,str,strLeft(assoIcon,i-1), int,strMid(assoIcon,i+1))
}
}
}
}
}
if !hIcon
hIcon:=DllCall("Shell32\ExtractAssociatedIconA", Int,0, Str,Item, UShortP,iIndex)
; not cached before, so add to imageList, note that exe/lnk will have iC#=0
if !iC#
iconIndex:=DllCall("ImageList_ReplaceIcon", UInt,ImageListID1, Int,-1, UInt,hIcon)+1
if !AllowCache
{ NotCachedIconsCount ++
DllCall("DestroyIcon", Uint, hIcon)
}
else if (iconIndex) and !iC# ; valid hIcon, not cached before, caching allowed
iconCache=%iconCache%|<%FExt%>|%iconIndex%
TimeToAddIcon+=A_TickCount-enterTime
return iconIndex
}
lvCleanup() ;;
{ global count, NotCachedIconsCount, iconCache, ImageListID1
LV_Delete()
Count =
; if too much exe/lnk icons grabbed, rebuild imageList
if (NotCachedIconsCount>1000)
{ NotCachedIconsCount:=0
loop,parse,iconCache,|
if (A_index & 2=0)
dllCall("DestroyIcon",uint,strMid(A_LoopField,2,strLen(A_LoopField)-2))
iconCache=
IL_Destroy(ImageListID1)
ImageListID1:=IL_Create(20, 100)
LV_SetImageList(ImageListID1)
traytip,,Destroying iconCache....,2
}
}
iif(expr, a, b) ;;
{
if (expr)
return a
else
return b
}
strLeft(s, n) ;;
{
stringLeft,s,s,%n%
return s
}
strRight(s, n) ;;
{
stringRight,s,s,%n%
return s
}
strMid(s, begin, n=0x7FFF, Left=0) ; if L<>0 then mid to the left ;;
{
if Left=0
stringMid,s,s,%begin%,%n%
else
stringMid,s,s,%begin%,%n%,L
return s
}
strReplace(s, searchText,replaceText="",replaceAll=1) ;;
{ if (replaceAll)
stringReplace,s,s,%searchText%,%replaceText%,All
else
stringReplace,s,s,%searchText%,%replaceText%
return s
}
strCompare(s1, s2,Left#=0) ; 1-less, 2-equal, 3-greater ;;
{ ; LOCALE_USER_DEFAULT=0x400, NORM_IGNORECASE=1, resultEQUAL=2
return dllCall("CompareString",int,0x400,int,1
,str,s1,int,iif(left#=0,strLen(s1),left#)
,str,s2,int,iif(left#=0,strLen(s2),left#))
}
strPos(where, what, start=1) ; inStr w/LOCALE support ;;
{ L0:=strLen(where)
L:=strLen(what)
ifGreater,L,% L0-start+1,return 0
dllCall("CharUpper",str,where)
dllCall("CharUpper",str,what)
return inStr(where,what,1,start)
}
strUpper(s) ;;
{
dllCall("CharUpper",str,s)
return s
}
addSlash(s) ;;
{ return iif(s="","",iif(strRight(s,1)="\",s,s "\"))
}
ctlGetText(ctl="", WinTitle="", WinText="", ExcludeTitle="", ExcludeText="") ;;
{
ControlGetText,s,%ctl%, %WinTitle%, %WinText%, %ExcludeTitle%, %ExcludeText%
return s
}
ExtractInteger(ByRef pSource, pOffset = 0, pIsSigned = false, pSize = 4) ;;
; pSource is a string (buffer) whose memory area contains a raw/binary integer at pOffset.
; The caller should pass true for pSigned to interpret the result as signed vs. unsigned.
; pSize is the size of PSource's integer in bytes (e.g. 4 bytes for a DWORD or Int).
; pSource must be ByRef to avoid corruption during the formal-to-actual copying process
; (since pSource might contain valid data beyond its first binary zero).
{
Loop %pSize% ; Build the integer by adding up its bytes.
result += *(&pSource + pOffset + A_Index-1) << 8*(A_Index-1)
if (!pIsSigned OR pSize > 4 OR result < 0x80000000)
return result ; Signed vs. unsigned doesn't matter in these cases.
; Otherwise, convert the value (now known to be 32-bit) to its signed counterpart:
return -(0xFFFFFFFF - result + 1)
}
InsertInteger(pInteger, ByRef pDest, pOffset = 0, pSize = 4) ;;
; The caller must ensure that pDest has sufficient capacity. To preserve any existing contents in pDest,
; only pSize number of bytes starting at pOffset are altered in it.
{
Loop %pSize% ; Copy each byte in the integer into the structure as raw binary data.
DllCall("RtlFillMemory", UInt, &pDest + pOffset + A_Index-1, UInt, 1, UChar, pInteger >> 8*(A_Index-1) & 0xFF)
}
switch(idx,val1="",val2="",val3="",val4="",val5="",val6="",val7="",val8=""
,val9="",val10="",val11="",val12="",val13="",val14="",val15="",val16="") ;;
{
return val%idx%
}
FileGetFullVer(file,verFlags=1,delim="") ;;
{ ifEqual,delim,,delim=|
; FLAGS: 0x0001 - numeric info
; 0x0002 - file type
; 0x0004 - file description
; 0x0008 - company name
; 0x8000 - clean-up empty fields
; VersionInfoStrings
; viPredefinedFirst = 0
; viLanguage = 0
; viComments = 1
; viCompanyName = 2
; viFileDescription = 3
; viFileVersion = 4
; viInternalName = 5
; viLegalCopyright = 6
; viLegalTrademarks = 7
; viOriginalFilename = 8
; viProductName = 9
; viProductVersion = 10
; viPrivateBuild = 11
; viSpecialBuild = 12
; viLegalTrademarks1 = 13 'Used by Office apps only?
; viLegalTrademarks2 = 14 'Used by Office apps only?
; viPredefinedLast = 14
;typedef struct tagVS_FIXEDFILEINFO
; 0 DWORD dwSignature;
; 4 DWORD dwStrucVersion;
; 8 DWORD dwFileVersionMS;
; 12 DWORD dwFileVersionLS;
; 16 DWORD dwProductVersionMS;
; 20 DWORD dwProductVersionLS;
; 24 DWORD dwFileFlagsMask;
; 28 DWORD dwFileFlags;
; 32 DWORD dwFileOS;
; 36 DWORD dwFileType;
; 40 DWORD dwFileSubtype;
; 44 DWORD dwFileDateMS;
; 48 DWORD dwFileDateLS;
; MAX_PATH = 260
; ; ----- VS_VERSION.dwFileFlags -----
; VS_FFI_SIGNATURE = 0xFEEF04BD
; VS_FFI_STRUCVERSION = 0x10000
; VS_FFI_FILEFLAGSMASK = 0x3F
; ; ----- VS_VERSION.dwFileFlags -----
; VS_FF_DEBUG = 0x1
; VS_FF_PRERELEASE = 0x2
; VS_FF_PATCHED = 0x4
; VS_FF_PRIVATEBUILD = 0x8
; VS_FF_INFOINFERRED = 0x10
; VS_FF_SPECIALBUILD = 0x20
; ; ----- VS_VERSION.dwFileOS -----
; VOS_UNKNOWN = 0x0
; VOS_DOS = 0x10000
; VOS_OS216 = 0x20000
; VOS_OS232 = 0x30000
; VOS_NT = 0x40000
; VOS_DOS_WINDOWS16 = 0x10001
; VOS_DOS_WINDOWS32 = 0x10004
; VOS_OS216_PM16 = 0x20002
; VOS_OS232_PM32 = 0x30003
; VOS_NT_WINDOWS32 = 0x40004
; ; ----- VS_VERSION.dwFileType -----
; VFT_UNKNOWN = 0x0
; VFT_APP = 0x1
; VFT_DLL = 0x2
; VFT_DRV = 0x3
; VFT_FONT = 0x4
; VFT_VXD = 0x5
; VFT_STATIC_LIB = 0x7
; ; **** VS_VERSION.dwFileSubtype for VFT_WINDOWS_FONT ****
; VFT2_FONT_RASTER = 0x1
; VFT2_FONT_VECTOR = 0x2
; VFT2_FONT_TRUETYPE = 0x3
; ; ----- VS_VERSION.dwFileSubtype for VFT_WINDOWS_DRV -----
; VFT2_UNKNOWN = 0x0
; VFT2_DRV_PRINTER = 0x1
; VFT2_DRV_KEYBOARD = 0x2
; VFT2_DRV_LANGUAGE = 0x3
; VFT2_DRV_DISPLAY = 0x4
; VFT2_DRV_MOUSE = 0x5
; VFT2_DRV_NETWORK = 0x6
; VFT2_DRV_SYSTEM = 0x7
; VFT2_DRV_INSTALLABLE = 0x8
; VFT2_DRV_SOUND = 0x9
; VFT2_DRV_COMM = 0xA
version=
dummy:=1
fiSize:=dllCall("version\GetFileVersionInfoSizeA",str,file,uint,&dummy)
varSetCapacity(fi,fiSize,0)
loop,1
{ if !dllCall("version\GetFileVersionInfoA",str,file, int,0, int,fiSize, uint,&fi)
break
if !dllCall("version\VerQueryValueA",uint,&fi, str,"\", uintp,fiFFI#, uintp,dummy)
break
varSetCapacity(fiFFI,13*4)
dllCall("RtlMoveMemory",uint,&fiFFI,uint,fiFFI#,uint,13*4)
version:=iif(verFlags & 1=0,""
,"v" extractInteger(fiFFI,10,0,2) "." extractInteger(fiFFI,8,0,2)
. iif(extractInteger(fiFFI,12,0,2)=0,""
,"." extractInteger(fiFFI,14,0,2) "." extractInteger(fiFFI,12,0,2)))
. iif(verFlags & 2=0,""
,"|" switch(extractInteger(fiFFI,36)+1,""
,"Application","DLL"
,"|" switch(extractInteger(fiFFI,40)+1,"","Printer ","Keyboard "
,"Language ","Display ","Mouse ","Network ","System "
,"Installable ", "Sound ","Comm. ") "driver"
,"|" switch(extractInteger(fiFFI,40),"Raster","Vector","TrueType") " font"
,"|VxD driver", "|Static Lib"))
if (verFlags & (4|8))
{ if !dllCall("version\VerQueryValueA",uint,&fi, str,"\VarFileInfo\Translation", uintp,fiTrans#, uintp,dummy)
break
ifEqual,dummy,0, break
fiTrans:=0
dllCall("RtlMoveMemory",uintP,fiTrans,uint,fiTrans#,uint,4)
Lang#:=fiTrans & 0xFFFF
CP#:=fiTrans>>16
varSetCapacity(lang,256,0)
dummy:=dllCall("VerLanguageNameA",uint,Lang#, str,lang, uint,256)
stringLeft,lang,lang,%dummy%
sSubBlock:= "\StringFileInfo\" FmtHex(Lang#,4) . FmtHex(CP#,4) "\"
Company:=verGetStdValue(fi,sSubBlock "CompanyName")
if !Company
{ ; Try U.S. English...?
dummy:="\StringFileInfo\0409" FmtHex(CP#,4) "\"
Company:=verGetStdValue(fi,dummy "CompanyName")
if (Company)
sSubBlock:=dummy ; We probably found the MS version bug.
}
version:=version
. iif(verFlags & 4=0,"","|" verGetStdValue(fi,sSubBlock "FileDescription"))
. iif(verFlags & 8=0,"","|" Company)
}
}
if (verFlags & 0x8000)
{ dummy=
version=|%version%|
stringReplace,version,version,|v0.0|
loop,parse,version,|
if (A_LoopField)
dummy:=dummy . delim . A_LoopField
return strMid(dummy,2)
}
if (delim<>"|")
stringReplace,version,version,|,%delim%,all
return version
}
verGetStdValue(byref fi, value) ;;
{ fiValue#:=0
dummy:=0
if !dllCall("version\VerQueryValueA",str,fi, str,value, uintp,fiValue#, uintp,dummy)
return
ifEqual,dummy,0, return
len:=dllCall("lstrlenA",uint,fiValue#)
varSetCapacity(fiValue,len+1,0)
dllCall("RtlMoveMemory",str,fiValue, uint,fiValue#, uint,len)
__trim:=A_AutoTrim
AutoTrim,on
fiValue=%fiValue%
AutoTrim,%__trim%
return fiValue
}
fmtHex(num,digits=8) ;; without "0x" padded with "0" ;;
{ varSetCapacity(s,digits+8,asc("0"))
__format:=A_FormatInteger
setformat,integer,hex
num+=0
s:=s . num
stringReplace,s,s,0x
setformat,integer,%__format%
return strRight(s,digits)
}
/*
___________________________________________
[Settings]
UsedList=
___________________________________________
*/