Comment ======================================================== Copyright 1998-2000 by Marc Perkel * All right reserved. This MarxMenu script is executed from the Windows 95 or 98 StartUp group and is used in place of putting other programs in StartUp. This script is made for network supervisors with large numbers of users to centralize and control the startup of windows users on the lan. Features of using this MarxMenu script include: Drive Mapping Map your network drives from a single script Start Programs Start programs conditionally Update local files Update files or entire directories from the network Capture Print Queues Capture Queues based on Group membership Messaging Display messages as users log into the network once Run Once Lets you run programs once to update local computer Schedule Tasks Lets you run programs every so many days And more ... Powerful MarxMenu script language lets you do just about anything you need to do. How it works: In your startup group on each workstation you create a pif or link file that runs MARXMENU WINSTART on the lan and you eliminate the need to start all other programs in your startup group. This script also eliminates the need for Novell Login scripts. MarxMenu is a more powerful language and can do a better job with less effort. When this script loads, it does all the work for you. During debugging the pif or link file is set for a normal window so you can see what's taking place. When you deploy it you can set it to run minimized so the users don't know it's running. If a person is a member of the DEBUGWINSTART group then they will see the diagnostic messages as winstart runs. ======================================================== EndComment Setup LocalStart ShowUser MapNetworkDrives CaptureNetworkPrintQueues UpdateLocalFiles SpawnStartupPrograms AddInternalTasks RunScheduledPrograms Finish ;===================[ Procedures ]====================== ;======================================================= Comment ======================================================== This code is executed if run on drive C:. I use this on my laptop so that if it's not connected to the network it will start up differently than if it is. LocalStart looks to see if theres a network winstart. If so it runs the network version. If not it sets up for non-network use. ======================================================== EndComment Procedure LocalStart if CurrentDirectory contains 'C:\' if ExistFile 'F:\LOGIN\WINSTART.MNU' ;- If Network, run network version ChDir 'F:\LOGIN' Execute 'MARXMENU.EXE WINSTART' else ;- If no network this executes ChDir '\WINDOWS' SpawnTask 'DESKMENU.EXE' endif ExitMenu endif EndProc ;-- Shows information about the user who's starting up Procedure ShowUser if not Quiet Writeln 'User Name: ' UserName Writeln 'Connection Number: ' NovConnection Writeln 'Novell Station Address: ' NovStationAddress Writeln endif EndProc Comment ======================================================== This section is used to map Novell network drives. Drives can be mapped based on many conditionals such as the existance of directories, members of groups, or environment variables. Besides Novell mapping you can execute the NET USE command for Microsoft networks. ======================================================== EndComment Procedure MapNetworkDrives if AlreadyLoggedIn then Return Sayln 'Mapping Network Drives' Sayln MapRoot ('P' 'F:\PUBLIC') MapRoot ('L' 'F:\LOGIN') MapRoot ('U' 'P:\UTIL') MapRoot ('N' 'P:\NSK') MapRoot ('W' 'F:\WINDOWS') MapRoot ('S' 'P:\CSMART') if ExistDir ('F:\HOME\' + UserName) MapRoot ('H' 'F:\HOME\' + UserName) endif if UserName <> 'WEB' SpawnTask 'NET.EXE USE G: \\BIGDOG\GDRIVE /YES' SpawnTask 'NET.EXE USE I: \\BIGDOG\IDRIVE /YES' SpawnTask 'NET.EXE USE J: \\BIGDOG\JDRIVE /YES' SpawnTask 'NET.EXE USE Y: \\BIGDOG\CDRIVE /YES' ; SpawnTask 'NET.EXE USE X: \\MSLINUX\ROOT /YES' endif ;- delete default novell drive mapping MapRoot ('Z' 'NWLINUX/ROOT:') if UserName = 'WEB' MapRoot ('H' 'F:\HOME\MARC') MapRoot ('T' 'H:\BP7\BIN') endif if UserName = 'MARC' MapRoot ('T' 'H:\BP7\BIN') endif Sayln EndProc ;-- Captures the Novell print queues Procedure CaptureNetworkPrintQueues if AlreadyLoggedIn then Return Sayln 'Capturing Network Print Queues' Sayln Capture('LASER',2) Capture('HP-COLOR',3) Sayln EndProc Comment ======================================================== This section starts programs that you would have put in the StartUp group. Programs can be launched conditionally based on group membership, user name, operating system, or network address. ======================================================== EndComment Procedure SpawnStartupPrograms if AlreadyLoggedIn then Return Sayln 'Starting Programs' Sayln if NT SpawnIfExist 'C:\NTRESKIT\DESKMENU.EXE' else SpawnIfExist 'I:\WINAPPS\POWERTOY\DESKMENU.EXE' endif ;-- NovSync is a device driver that keeps the workstation clock ;-- syncronized with the netware file server. if ExistFile 'NOVSYNC$' PrinterName = 'NOVSYNC$' Print '1' ;-- Activates Device endif ;- Windows NPrinter in Front Office if UserName = 'WEB' SpawnIfExist 'C:\website\httpd32.exe' SpawnIfExist 'C:\Progra~1\War-ftpd\war-ftpd.exe' SpawnIfExist 'C:\website\admin\www-mon.pmc' Wait 800 SpawnIfExist 'C:\WINNT\PROFILES\WEB\DESKTOP\webser~1.bhf' endif Sayln EndProc Comment ======================================================== Compares and copies newer network files to local drive. This can be used to update individual files or whole directories. ======================================================== EndComment Procedure UpdateLocalFiles Sayln 'Updating Local Files' Sayln if UserName = 'MARC' CopyNewerFile('L:\WINSTART.MNU','N:\WINSTART.MNU') CopyNewerFile('L:\WINSTART.MNU','H:\BP7\MARX\WINSTART.MNU') CopyNewerFile('L:\WINSTART.MNU','G:\DOWNLOAD\SHARE\WINSTART.MNU') CopyNewerFile('L:\LOGIN.MNU','N:\LOGIN.MNU') CopyNewerFile('L:\LOGIN.MNU','H:\BP7\MARX\LOGIN.MNU') endif Sayln MatchDir('N:\','C:\NSK') EndProc Comment ======================================================== Internal tasks must be added before RunScheduledPrograms is executed. Internal tasks are used so that the match directoriy commands aren't executed every time you reboot. This is for command that may take a lot of time to execute and you might want to run them once a day. ======================================================== EndComment Procedure AddInternalTasks if ExistDir 'C:\BP7' AddTaskToList ('BP7','*MatchDirAndSubs H:\BP7 C:\BP7','1') endif if ExistDir 'C:\MARC\BP7' AddTaskToList ('BP7','*MatchDirAndSubs H:\BP7 C:\MARC\BP7','1') endif EndProc ;===============[ Support Procedures ]================== ;======================================================= Comment ======================================================== These are the nuts and bolts routines. They can be modified and new ones can be added. ======================================================== EndComment var MyGroups Quiet Slow NT SaveDir UserName AlreadyLoggedIn Const DebugGroup = 'DEBUGWINSTART' IniName = 'F:\LOGIN\WINSTART.INI' RanName = 'C:\WINSTART.RAN' Procedure Setup var Groups XmsOverlays StandardIO UpperCaseCompare ReadLongNames On NT = %OS% contains "NT" SaveDir = CurrentDirectory UserName = UpperCase(NovLoginName) NovGroups(Groups) NovReadGroups(UserName,MyGroups) ;Read Groups user is in. SortArray(MyGroups) ;- if group DEBUGWINSTART doesn't exist then debugging is assumed if InGroup(DebugGroup) or (PosInList(DebugGroup,Groups) = 0) Slow On Quiet Off else Slow Off Quiet On endif AlreadyLoggedIn = CurrentDirectory = 'L:\' ReadWinStartIniFile EndProc Procedure Finish ChDir SaveDir Sayln 'Done' if Slow then Wait 500 ExitMenu EndProc ;----- Returns True if in list Procedure InGroup (Group) Return PosInSortedList(UpperCase(Group),MyGroups) > 0 EndProc Procedure SpawnIfExist ($St) var Prog P P = pos(' ',St) if P = 0 then P = length(St) + 1 Prog = Left(St,P-1) if ExistFile(Prog) SpawnTask(St) Return endif Sayln 'Program [' St '] not Found! EndProc Procedure SpawnTask ($St) var DPath PathSt P ExecSt if St StartsWith '*' InternalCommand(St) Return endif Say 'Spawning Task: ' St ;- Change to Program's Directory P = length(St) while (P > 0) and (mid(St,P,1) <> '\') P = P - 1 endwhile if P > 0 PathSt = Left(St,P-1) DPath = ' /D' + PathSt + ' ' endif ;- NT Requires the START command to spawn tasks if NT ExecSt = 'CMD.EXE /C START ' + DPath + St else ChDir PathSt ExecSt = 'C:\COMMAND.COM /C ' + St endif if Quiet then ExecSt = ExecSt + ' >NUL' Execute ExecSt Sayln 'OK' EndProc Procedure InternalCommand (St) var Command Param1 Param2 Delete(St,1,1) Command = NextWordDelim(St,' ,') Param1 = NextWordDelim(St,' ,') Param2 = NextWordDelim(St,' ,') if Command = 'MatchDirAndSubs' MatchDirAndSubs(Param1,Param2) elseif Command = 'MatchDir' MatchDir(Param1,Param2) elseif Command = 'CopyNewerFile' CopyNewerFile(Param1,Param2) else Sayln Sayln 'Error: ' Command ' is invalid command!' Sayln endif EndProc Procedure MapRoot (Drive, Dir) if Dir > '' Say 'Mapping Drive ' Drive ' to ' Dir else Say 'Deleting Drive ' Drive endif NovMapRoot Drive Dir if NovResult = 0 Sayln 'OK' else Sayln 'Error: ' NovResult endif EndProc Procedure Capture (Queue, PrinterPort) Say 'Capturing: ' Queue ' to LPT' PrinterPort NovCaptureQueue(PrinterPort) = Queue NovCaptureTimeOut(PrinterPort) = 5 ;5 second timeout NovCaptureUseBanner(PrinterPort) = Off SayLn 'OK' EndProc var LastLineBlank Procedure Say ($St) if Quiet then Return Write St ' ' LastLineBlank = False EndProc Procedure Sayln ($St) if Quiet then Return if St = '' if not LastLineBlank then Writeln LastLineBlank = True else Writeln St LastLineBlank = False if Slow then Wait 50 endif EndProc Procedure CopyNewerFile (New,Old) if ExistFile(New) if FileTime(Old) < FileTime(New) CopyAFile(New,Old) endif endif EndProc Procedure CopyAFile (Source,Dest) Sayln 'Copying ' Source ' to ' Dest if NT Execute 'CMD.EXE /C COPY ' Source ' ' Dest else Execute 'COPY ' Source ' ' Dest endif EndProc Procedure DeleteAFile (Name) Sayln 'Deleting ' Name DelFile(Name) EndProc Comment ======================================================== This section of code is used to update entire directory structures from the file server. ======================================================== EndComment Var MasterNoMatch SlaveNoMatch MasterNewer MasterSubs SlaveSubs Const IndexOfDate = 2 IndexOfName = 10 Procedure MatchDirAndSubs (MasterDir,SlaveDir) Sayln 'Syncronizing ' SlaveDir ' from ' MasterDir Sayln MatchDirAndSubs1 (MasterDir,SlaveDir) EndProc Procedure MatchDirAndSubs1 (MasterDir,SlaveDir) var Subs M S MatchDir1 (MasterDir,SlaveDir) MatrixInvert(MasterSubs) Subs = MasterSubs[1] dispose(MasterSubs) Loop Subs M = MasterDir + '\' + LoopVal S = SlaveDir + '\' + LoopVal MatchDirAndSubs1 (M,S) EndLoop EndProc Procedure MatchDir (MasterDir,SlaveDir) Sayln 'Syncronizing ' SlaveDir ' from ' MasterDir Sayln MatchDir1 (MasterDir,SlaveDir) EndProc Procedure MatchDir1 (MasterDir,SlaveDir) ;- if SlaveDir doesn't exist, create it if not ExistDir(SlaveDir) Sayln 'Creating ' SlaveDir MkDir(SlaveDir) if FileResult <> 0 Sayln 'Error ' FileResult ' while creating ' SlaveDir Return endif endif CompareDirectories(MasterDir,SlaveDir) if not (MasterDir EndsWith '\') MasterDir = MasterDir + '\' endif Loop MasterNoMatch CopyAFile(MasterDir + LoopVal,SlaveDir) EndLoop Loop MasterNewer CopyAFile(MasterDir + LoopVal,SlaveDir) EndLoop if not (SlaveDir EndsWith '\') SlaveDir = SlaveDir + '\' endif Loop SlaveNoMatch DeleteAFile(SlaveDir + LoopVal) EndLoop EndProc Procedure CompareDirectories (MasterDir,SlaveDir) var MasterList SlaveList MasterKey SlaveKey SlaveMatchIndex MasterMatchIndex SlavePtr SlaveLimit WholeFileNames Off WholeDirectoryNames Off dispose(MasterNoMatch) dispose(MasterNewer) dispose(SlaveNoMatch) if not ExistDir(MasterDir) Sayln 'Error comparing directories: ' MasterDir ' does not exist!' Return endif ReadSqDirectory(MasterDir,MasterList,MasterSubs) if NumberOfElements(MasterList) = 0 then Return MatrixInvert(MasterList) MasterKey = MasterList[IndexOfName] MatrixInvert(MasterList) SortArrayLinked(MasterList,MasterKey) ReadSqDirectory(SlaveDir,SlaveList,SlaveSubs) MatrixInvert(SlaveList) SlaveKey = SlaveList[IndexOfName] MatrixInvert(SlaveList) SortArrayLinked(Slavelist,SlaveKey) SlavePtr = 1 SlaveLimit = NumberOfElements(SlaveKey) MasterMatchIndex[NumberOfElements(MasterKey)] = False if SlaveLimit > 0 SlaveMatchIndex[SlaveLimit] = False endif Loop MasterKey ;- Move SlavePtr >= LoopVal while (SlavePtr <= SlaveLimit) and (SlaveKey[SlavePtr] < LoopVal) SlavePtr = SlavePtr + 1 endwhile ;- if Matching Names if SlaveKey[SlavePtr] = LoopVal ;- Mark Master and Slave indexes as matched MasterMatchIndex[LoopIndex] = True SlaveMatchIndex[SlavePtr] = True ;- Compare File Dates if MasterList[LoopIndex,IndexOfDate] <> SlaveList[SlavePtr,IndexOfDate] AppendArray(MasterNewer,LoopVal) endif endif EndLoop ;- Process unmatched Master list Loop MasterMatchIndex if not LoopVal AppendArray(MasterNoMatch,MasterKey[LoopIndex]) endif EndLoop ;- Process unmatched Slave list Loop SlaveMatchIndex if not LoopVal AppendArray(SlaveNoMatch,SlaveKey[LoopIndex]) endif EndLoop EndProc Comment ======================================================== RunScheduledFiles is used to contol programs that run once or run on a schedule. A local file C:\WINSTART.RUN is a comma delimited ascii file and is created to keep track of what is already run. This file contains the following fields: 1 - Section Name 2 - Run Command 3 - Expire Date 4 - Date Last Run ======================================================== EndComment Comment ======================================================== This is an example WINSTART.INI file. Any text before the first section is ignored. This file is used to run programs once as Windows workstations boot up. Information about what has ran is in the C:\WINSTART.RAN file. This file is read to determine what has already ran so it doesn't get run twice. If you want to make a command run again just change the name of the command. The Groups= is used to run programs only if the user is a member of one of the listed groups. This example shows how to use this feature to display a message to the users only once or on a daily basis. [Notepad Message] Groups=People, Everyone Run=c:\windows\notepad.exe f:\home\news.txt [Wordpad Message] Groups= Days=1 Run=c:\progra~1\access~1\wordpad.exe f:\home\news.doc ;- Lines that start with ; or # are ignored ======================================================== EndComment var Ini RanList RanIndex SectionList TaskList TaskIndex Qualifier Name, Command, ExpireDate, LastRanDate Procedure ReadWinStartIniFile var T Section Params P Name Groups Command ExpireString ReadTextFile IniName T Loop T trim(LoopVal) if LoopVal StartsWith ';' then LoopVal = '' if LoopVal StartsWith '#' then LoopVal = '' if LoopVal > '' if LoopVal StartsWith '[' Section = Section + 1 delete(LoopVal,1,1) delete(LoopVal,length(LoopVal),1) trim(LoopVal) SectionList[Section] = LoopVal else if Section > 0 AppendArray(Ini[Section],LoopVal) endif endif endif EndLoop SortArrayLinked(Ini,SectionList) Loop SectionList ;- Add Tasks to TaskList Params = Ini[LoopIndex] Groups = '' P = PosInListLeft('GROUPS=',Params) if P > 0 Groups = RightOfEqual(Params[P]) endif if InGroupList(Groups) Name = LoopVal ExpireString = '' Command = '' P = PosInListLeft('RUN=',Params) if P > 0 Command = RightOfEqual(Params[P]) endif P = PosInListLeft('DAYS=',Params) if P > 0 ExpireString = RightOfEqual(Params[P]) endif AddTaskToList(Name,Command,ExpireString) endif EndLoop EndProc Procedure AddTaskToList (Name,Command,ExpireString) var Rec if Command > '' Rec.Name = Name Rec.Command = Command Rec.ExpireDate = DaysExpire(ExpireString) AppendArray(TaskList,Rec) AppendArray(TaskIndex,Name) endif EndProc Procedure DaysExpire (St) if St = '' then Return '' Return DateString(Now + (SecondsInDay * Value(St))) EndProc Procedure ReadRanDataFile var OldRan ;- text file stores programs already ran ReadAscTextFile RanName OldRan if NumberOfElements(OldRan) = 0 then Return ;- Get rid of old Ran data Loop OldRan if IsCurrent(LoopVal) AppendArray(RanList,LoopVal) endif EndLoop MatrixInvert(RanList) RanIndex = RanList.Name MatrixInvert(RanList) EndProc ;- tests if the stored list of programs run contains current information Procedure IsCurrent (Rec) var P ;- Is the program still in the list? P = PosInList(Rec.Name,TaskIndex) if P = 0 then Return False ;- Has the time expired? if Rec.ExpireDate = '' then Return True Return (Now < TimeOf(Rec.ExpireDate)) EndProc ;- Test if user is a member of any required groups Procedure InGroupList (List) var St ;- No Groups means everyone if List = '' then Return True while List > '' St = NextWordDelim(List,',; ') if InGroup(St) then Return True endwhile Return False EndProc ;- Execute the list of scheduled programs Procedure RunScheduledPrograms ReadRanDataFile Loop TaskList if PosInList(LoopVal.Name,RanIndex) = 0 SpawnTask(LoopVal.Command) AppendArray(LoopVal,DateString) AppendArray(RanList,LoopVal) AppendArray(RanIndex,LoopVal.Name) endif EndLoop WriteAscTextFile RanName RanList Endproc