You can download the compiled exe here

The main procedures are these, one for executing files (if program parameters are present) and one for defining them (and building a shortcut file:
Code: Select all
Procedure ExecuteProgramList()
Protected i,n,x,index
Protected GlobalPath.s,LocalPath.s
Protected WindowX
index=CountProgramParameters()-1
WindowX=#IconBox+(#IconBox+#IconSpace)*index+#IconBorder<<1
#WindowY=#IconBox+#IconBorder<<1
For i=0 To index
LocalPath=ProgramParameter(i)
StartMode(i)=FindString(LocalPath,"*",1)
If StartMode(i)=1
LocalPath=Mid(LocalPath,2)
Else
StartMode(i)=0
EndIf
If PeekC(@LocalPath)='>'
n=PeekC(@LocalPath+#CharLen)-'A'
LocalPath=Mid(LocalPath,3)
If n>=0 And n<i
LocalPath=Zip(n)+LocalPath
EndIf
EndIf
n=FindLastSlash(LocalPath)
Zip(i)=Left(LocalPath,n)
Programs(i)=LocalPath
LoadIcon(LocalPath)
Next i
OpenWindow(#WinExe,0,0,WindowX,#WindowY,#Program,#PB_Window_ScreenCentered|#PB_Window_SystemMenu)
x=#IconBorder
For i=0 To index
TextGadget(i,x,#IconBorder,#IconBox,#IconBox,"",#PB_Text_Border)
TextGadget(i+#MaxList<<1,x+#IconColorFrame,#IconBorder+#IconColorFrame,#IconBox-#IconColorFrame<<1,#IconBox-#IconColorFrame<<1,"")
SetGadgetColor(i,#PB_Gadget_BackColor,#Red)
SetGadgetColor(i+#MaxList<<1,#PB_Gadget_BackColor,#White)
ImageGadget(i+#MaxList,x+#IconFrame+#IconHalf-Icons(i)\X>>1,#IconBorder+#IconFrame+#IconHalf-Icons(i)\Y>>1,#IconSize,#IconSize,Icons(i)\Image)
x+#IconBox+#IconSpace
Next i
StickyWindow(#WinExe,#True)
Wait(500)
For i=0 To index
LocalPath=Programs(i)
If StartMode(i)=0
StartMode(i)=IsNull(ProcessNamePresent(LocalPath))
EndIf
If StartMode(i)
SetCurrentDirectory(GlobalPath)
SetCurrentDirectory(GetPathPart(LocalPath))
RunProgram(GetFilePart(LocalPath))
EndIf
wait(250)
;DisableGadget(i,1)
SetGadgetColor(i,#PB_Gadget_BackColor,#Green)
SetGadgetColor(i+#MaxList+#MaxList,#PB_Gadget_BackColor,#White)
SetGadgetState(i+#MaxList,Icons(i)\Image)
wait(750)
Next i
wait(2000)
EndProcedure
Procedure DefineProgramList()
Protected i,event
Protected information.s
Protected parameter.s
Protected text.s
#Border=10
#Gap=10
#DefHeight=180
#InfoTop=#DefHeight+#Border+#Gap>>1
#InfoHeight=20
#ButHeight=25
#ButTop=#Border+#DefHeight+#Gap<<1+#InfoHeight
WinDef=OpenWindow(#WinDef,0,0,480,#DefHeight+#Border<<1+#InfoHeight+#Gap<<1+#ButHeight,#Program+" - Configuration",#PB_Window_ScreenCentered|#PB_Window_SystemMenu)
TextGadget(#DefName,10,#InfoTop,70,#InfoHeight,"Information:",#SS_CENTERIMAGE)
StringGadget(#DefInfo,80,#InfoTop,390,#InfoHeight,#Program)
ListIconGadget(#DefList,10,#Border,460,#DefHeight,"Program",136,#PB_ListIcon_CheckBoxes|#PB_ListIcon_GridLines|#PB_ListIcon_FullRowSelect|#PB_ListIcon_AlwaysShowSelection )
AddGadgetColumn(#DefList,1,"Path",320)
ButtonGadget(#DefAdd,10,#ButTop,100,#ButHeight,"Add",#PB_Button_Default)
ButtonGadget(#DefDel,130,#ButTop,100,#ButHeight,"Del")
ButtonGadget(#DefCreate,250,#ButTop,100,#ButHeight,"Create Batch")
ButtonGadget(#DefCancel,370,#ButTop,100,#ButHeight,"Cancel")
DisableGadget(#DefDel,#True)
AddKeyboardShortcut(#WinDef,#PB_Shortcut_Alt|#PB_Shortcut_A,#DefAdd)
AddKeyboardShortcut(#WinDef,#PB_Shortcut_Alt|#PB_Shortcut_B,#DefCreate)
AddKeyboardShortcut(#WinDef,#PB_Shortcut_Alt|#PB_Shortcut_C,#DefCancel)
AddKeyboardShortcut(#WinDef,#PB_Shortcut_Escape,#DefCancel)
AddKeyboardShortcut(#WinDef,#PB_Shortcut_Alt|#PB_Shortcut_D,#DefDel)
AddKeyboardShortcut(#WinDef,#PB_Shortcut_Shift|#PB_Shortcut_Delete,#DefDel)
Repeat
i=CountGadgetItems(#DefList)
DisableGadget(#DefCreate,IsNull(i))
DisableGadget(#DefDel,IsNull(GetGadgetState(#DefList)+1))
DisableGadget(#DefAdd,IsNull(i-#MaxList))
event=WaitWindowEvent()
Select event
Case #PB_Event_Menu
event=EventMenu()
If GetGadgetEnabledState(event)
PostMessage_(WinDef,#PB_Event_Gadget,#PB_Shortcut_SrodFlag|event,0)
EndIf
Case #PB_Event_Gadget
Select EventGadget()
Case #DefAdd
Program=OpenFileRequester("Add Program File","","Program Files (*com, *.exe)|*.com;*.exe|Shortcut Files (*.lnk)|*.lnk|Batch Files (*.bat, *.cmd)|*.bat;*.cmd|Executable Files|*.com;*.exe;*.lnk;*.bat;*.cmd|All Files (*.*)|*.*",0)
If Len(Program)
Path=GetPathPart(Program)
Program=GetFilePart(Program)
AddGadgetItem(#DefList,#LastPosition,Program+#LF$+Path)
SetActiveGadget(#DefList)
SetListColor()
EndIf
Case #DefDel
RemoveGadgetItem(#DefList,GetGadgetState(#DefList))
SetListColor()
Case #DefCreate
Program=OpenFileRequester("Create Shortcut File","","Shortcut Files (*.lnk)|*.lnk|All Files (*.*)|*.*",0)
If Len(Program)
If LCase(Right(Program,4))<>".lnk"
Program+".lnk"
EndIf
CreateShellLink(Quoter(ProgramFilename()),Program,CreateParameter(),GetGadgetText(#DefInfo),GetPathPart(ProgramFilename()),#WM_SHOWWINDOW,0,ProgramFilename(),0)
EndIf
Case #DefCancel
Break
EndSelect
Case #WM_CLOSE
Break
EndSelect
ForEver
EndProcedure
If CountProgramParameters()
ExecuteProgramList()
Else
DefineProgramList()
EndIf
Code: Select all
; Define
EnableExplicit
#Program="Bashful Bat"
#LastPosition=-#True
#PB_Shortcut_SrodFlag=1<<16
#MaxList=3
#IconSize=64
#IconHalf=32
#IconColorFrame=4
#IconWhiteFrame=1
#IconFrame=#IconColorFrame+#IconWhiteFrame
#IconBox=#IconSize+#IconFrame<<1
#IconBorder=20
#IconSpace=16
Enumeration
#WinExe
#WinDef
#DefName
#DefInfo
#DefList
#DefAdd
#DefDel
#DefCreate
#DefCancel
EndEnumeration
Global Program.s
Global Path.s
Global WinDef
Structure IconType
Image.l
X.l
Y.l
EndStructure
Global Dim Zip.s(#MaxList)
Global Dim Programs.s(#MaxList)
Global Dim StartMode(#MaxList)
Global Dim Icons.IconType(#MaxList)
Global NewList Icon_List.i()
Global IconCount
; EndDefine
Procedure IsNull(value)
If value
ProcedureReturn #False
Else
ProcedureReturn #True
EndIf
EndProcedure
Procedure GetGadgetEnabledState(Gadget)
ProcedureReturn IsWindowEnabled_(GadgetID(Gadget))
;ProcedureReturn GetWindowLong_(GadgetID(Gadget),#GWL_STYLE)&#WS_DISABLED
EndProcedure
Procedure CreateShellLink(Prog.s,Link.s,Args.s,Info.s,Dir.s,Show.l,HotKey.l,Icon.s,IconNr.l)
; CreateLink
; Prog for the Link "c:\PureBasic\purebasic.exe"
; Link name of the Link "c:\pb.lnk"
; Args for the target "%1"
; Info Description & Tooltip "Start PureBasic"
; Dir Working Directory "c:\PureBasic\"
; Show Show Flags #SW_SHOWNORMAL, #SW_SHOWMAXIMIZED, #SW_SHOWMINIMIZED
; HotKey Shortcut ?
; Icon/Nr Icon file & Index "c:\PureBasic\purebasic.exe",1
Protected mem.s
Protected hres
Protected result
Protected ppf.IPersistFile
CompilerIf #PB_Compiler_Unicode
#CharLen=2
Protected psl.IShellLinkW
CompilerElse
#CharLen=1
Protected psl.IShellLinkA
CompilerEndIf
CoInitialize_(0)
If CoCreateInstance_(?CLSID_ShellLink,0,1,?IID_IShellLink,@psl)=0
; Set_ShellLink_preferences
psl\SetPath(Prog); 4.4x: @Prog the file to which is linked (target for the Link)
psl\SetArguments(Args); 4.4x: @Args Arguments for the Target
psl\SetWorkingDirectory(Dir); 4.4x: @Dir Working Directory
psl\SetDescription(Info); 4.4x: @Info Description (also used as Tooltip for the Link)
psl\SetShowCmd(Show); Show command (default=SW_SHOWNORMAL, SW_SHOWMAXIMIZED, SW_SHOWMINIMIZED)
psl\SetHotkey(HotKey); Hotkey (Virtual key code in low-order byte, modifier in the high-order byte)
; HOTKEYF_ALT, HOTKEYF_CONTROL, HOTKEYF_EXT, HOTKEYF_SHIFT
psl\SetIconLocation(Icon,IconNr); 4.4x: @Icon Icon file and index number of the icon
; ShellLink_Save
; Query IShellLink For the IPersistFile interface For saving the shortcut in persistent storage.
If psl\QueryInterface(?IID_IPersistFile,@ppf)=0
;CompilerIf #PB_Compiler_Unicode=0
;mem=Space(1000); AllocateMemory(1,1000)
;MultiByteToWideChar_(#CP_ACP,0,Link,-1,mem,1000); Ensure that the string is Unicode.
;hres=ppf\Save(mem,#True); 4.4x: @mem Save the link by calling IPersistFile::Save.
;CompilerElse
hres=ppf\Save(Link,#True); 4.4x: @mem Save the link by calling IPersistFile::Save.
;CompilerEndIf
ppf\Release()
result=#True
EndIf
psl\Release()
EndIf
CoUninitialize_()
ProcedureReturn result
DataSection
CLSID_ShellLink:
; 00021401-0000-0000-C000-000000000046
Data.l $00021401
Data.w $0000,$0000
Data.b $C0,$00,$00,$00,$00,$00,$00,$46
IID_IShellLink:
; DEFINE_SHLGUID(IID_IShellLinkA, 0x000214EEL, 0, 0);
; C000-000000000046
CompilerIf #PB_Compiler_Unicode
Data.l $000214F9
CompilerElse
Data.l $000214EE
CompilerEndIf
Data.w $0000,$0000
Data.b $C0,$00,$00,$00,$00,$00,$00,$46
IID_IPersistFile:
; 0000010b-0000-0000-C000-000000000046
Data.l $0000010b
Data.w $0000,$0000
Data.b $C0,$00,$00,$00,$00,$00,$00,$46
EndDataSection
EndProcedure
Procedure.s ProcessNameFromHwnd(hwnd)
Protected processid.l,psapilib.l
Protected GetModuleFileNameEx,hprocess.l
Protected processname.s
psapilib=OpenLibrary(#PB_Any,"psapi.dll")
If psapilib
CompilerIf #PB_Compiler_Unicode
GetModuleFileNameEx=GetFunction(psapilib,"GetModuleFileNameExW")
CompilerElse
GetModuleFileNameEx=GetFunction(psapilib,"GetModuleFileNameExA")
CompilerEndIf
If GetModuleFileNameEx
If GetWindowThreadProcessId_(hwnd,@processid)
hprocess=OpenProcess_(#PROCESS_QUERY_INFORMATION|#PROCESS_VM_READ,#Null,processid)
If hprocess
processname=Space(#MAX_PATH)
If Not CallFunctionFast(GetModuleFileNameEx,hprocess,#Null,@processname,#MAX_PATH)
processname=""
EndIf
CloseHandle_(hprocess)
EndIf
EndIf
EndIf
CloseLibrary(psapilib)
EndIf
ProcedureReturn processname
EndProcedure
Procedure ProcessNamePresent(process.s)
Protected handle=FindWindow_(0,0)
Protected PID.l
process=LCase(GetFilePart(process))
handle=GetWindow_(handle,#GW_HWNDFIRST)
While handle
GetWindowThreadProcessId_(handle,@PID)
If LCase(GetFilePart(ProcessNameFromHwnd(handle)))=process
ProcedureReturn #True
EndIf
handle=GetWindow_(handle,#GW_HWNDNEXT)
Wend
ProcedureReturn #False
EndProcedure
Procedure.i Enum_IconList(ResHandle.i,ResType.i,ResName.i,AppParam.i)
AddElement(Icon_List())
Icon_List()=ResName
ProcedureReturn #True
EndProcedure
Procedure.i Enum_IconType(ResHandle.i,ResType.i,AppParam.i)
If ResType=#RT_GROUP_ICON
EnumResourceNames_(ResHandle,ResType,@Enum_IconList(),0)
EndIf
ProcedureReturn #True
EndProcedure
Procedure LoadIcon(IconFile.s)
Global Icon_Lib.i
Global Icon_Count.i
Global *Icon_Group.GRPIconDIR
Global *Icon_Image.IconIMAGE
Protected n,saven,m,maxm
Icon_Lib=LoadLibraryEx_(@IconFile,0,#LOAD_LIBRARY_AS_DATAFILE)
If Icon_Lib
EnumResourceTypes_(Icon_Lib,@Enum_IconType(),0)
EndIf
ForEach Icon_List()
*Icon_Group=LoadResource_(Icon_Lib,FindResource_(Icon_Lib,Icon_List(),#RT_GROUP_ICON))
If *Icon_Group
maxm=0
saven=0
For n=0 To *Icon_Group\idCount-1
m=*Icon_Group\idEntries[n]\bWidth
If (m>maxm) And (m<=#IconSize)
saven=n
maxm=m
EndIf
Next n
*Icon_Image=LoadResource_(Icon_Lib,FindResource_(Icon_Lib,*Icon_Group\idEntries[saven]\nID,#RT_ICON))
With Icons(IconCount)
\Image=CreateIconFromResourceEx_(*Icon_Image,*Icon_Image\icHeader\biBitCount,#True,$00030000,maxm,*Icon_Group\idEntries[saven]\bHeight,0)
\X=maxm
\Y=*Icon_Group\idEntries[saven]\bHeight
;DestroyIcon_(\Image)
EndWith
IconCount+1
Break
EndIf
Next
EndProcedure
Procedure.l FindLastSlash(s.s)
Protected p=Len(s)
While p
p-1
If PeekA(@s+p)='\'
ProcedureReturn p+1
EndIf
Wend
ProcedureReturn 0
EndProcedure
Procedure.s Quoter(text.s)
If FindString(text," ",1)
ProcedureReturn #DQUOTE$+text+#DQUOTE$
Else
ProcedureReturn text
EndIf
EndProcedure
Procedure.i EqualPathLen(s1.s,s2.s)
; -- return length of identical part of the paths in s1$ and s2$
Protected maxEqual, temp, equal, ret
Protected *p1.Character, *p2.Character
#Slash='\'
#BadSlash='/'
s1=UCase(s1)
s2=UCase(s2)
maxEqual=Len(s1)
temp=Len(s2)
If maxEqual > temp
maxEqual=temp
EndIf
*p1=@s1
*p2=@s2
equal=0
ret=0
While equal < maxEqual And *p1\c=*p2\c
equal + 1
If *p1\c=#Slash
ret=equal
EndIf
*p1 + SizeOf(character)
*p2 + SizeOf(character)
Wend
ProcedureReturn ret
EndProcedure
Procedure.s RelativePath(baseDir.s,absPath.s)
; -- convert an absolute path to a relative one
; in : baseDir$: full name of a directory, with trailing (back)slash
; absPath$: full name of a path to a directory or file
; out: absPath$ converted, so that it is relative to baseDir$
Protected equal,s,i
Protected parent.s,ret.s=""
If UCase(Left(baseDir,1)) <> UCase(Left(absPath,1))
ProcedureReturn absPath; can't build a relative path
EndIf
ReplaceString(baseDir,Chr(#BadSlash),Chr(#Slash),#PB_String_InPlace)
ReplaceString(absPath,Chr(#BadSlash),Chr(#Slash),#PB_String_InPlace)
equal=EqualPathLen(baseDir,absPath)
s=CountString(Mid(baseDir,equal+1),Chr(#Slash))
parent=".."+Chr(#Slash)
For i=1 To s
ret+parent
Next
ProcedureReturn ret+Mid(absPath,equal+1)
EndProcedure
Procedure.s CompactPath(index,main.s,new.s)
Protected samedrive
Protected i,n,best,bestlen
If Mid(new,2,1)=":" And (Left(new,2)=Left(main,2))
samedrive=#True
EndIf
If CountString(new,"\")=1 And samedrive
Zip(index)="*"
ProcedureReturn Mid(new,3)
Else
main=RelativePath(main,new)
If samedrive
new=Mid(new,3)
EndIf
If Len(main)<Len(new)
new=main
EndIf
i=index
best=-1
bestlen=#MAXLONG
While i>0
i-1
n=Len(Zip(i))
If Left(new,n)=Zip(i)
If (best=-1) Or (n>bestlen)
best=i
bestlen=n
EndIf
EndIf
Wend
If best=-1
n=FindLastSlash(new)
If n
Debug Left(new,n)
Zip(index)=Left(new,n)
Else
Zip(index)="*"
EndIf
Else
new=">"+Chr(best+'A')+Mid(new,bestlen+1)
n=FindLastSlash(new)
If n
Zip(index)=Zip(best)+Mid(new,3,n-2)
Else
Zip(index)="*"
EndIf
EndIf
ProcedureReturn new
EndIf
EndProcedure
Procedure.s CreateParameter()
Protected i
Protected parameter.s
i=0
parameter=""
While i<CountGadgetItems(#DefList)
parameter+" "+Quoter(Mid("*"+CompactPath(i,GetPathPart(ProgramFilename()),GetGadgetItemText(#DefList,i,1)+GetGadgetItemText(#DefList,i,0)),2-(GetGadgetItemState(#DefList,i)&#PB_ListIcon_Checked)>>1))
i+1
Wend
ProcedureReturn parameter
EndProcedure
Procedure SetListColor()
If Len(Quoter(ProgramFilename())+CreateParameter())>=258
SetGadgetColor(#DefList,#PB_Gadget_BackColor,#Yellow)
Else
SetGadgetColor(#DefList,#PB_Gadget_BackColor,#White)
EndIf
EndProcedure
Procedure Wait(ms)
ms+ElapsedMilliseconds()
Repeat
WaitWindowEvent(5)
Until ElapsedMilliseconds()>ms
EndProcedure
• shortcuts seem to allow only 259 chars for the file name and its parameters - therefore I try to "compress" the path of all added programs. If the string would be to long, the list gadget will get a yellow background
• the checkboxes allows to define, if a new instance of a program will be started when it is already running
• anyone improving the code is welcome - just one feature I did not realize: hiding application windows during its start phase
Michael