|
|
|||||||
Hello Kix folks. Am trying a fairly simple check in script for the existence of two files, I get the feeling my syntax is wrong as the script just runs right through it, or if $variable1 exists it displays the messagebox. Script sample: $variable1="c:\@userid1.txt" $variable2="c:\@userid2.txt" $Message="You have both files" IF EXIST ("$variable1") AND ("variable2") Mesasgebox ("$message", "***You have both files***",0,10) Exit ENDIF END script sample There's a variety of other things going on in this script, but the result of this file check is important for the rest of the script processing. Additionally, I have another minor problem with trying to use "RUN" to launch a CMD script. The CMD script has a "wait" in it, and that in turn is keeping the Kix script which launched it open. To quote the manual.... This behavior is different from the MS‑DOS – based version of KiXtart, where the RUN command also terminates the script. If you want to emulate the MS‑DOS – based version, you must add an EXIT command after the RUN command So if I'm using: RUN "c:\scripts\file.cmd" Exit where does the Exit fit in? I have it the line after and it's not being picked up. Thanks much, TS |
||||||||
|
|
|||||||
Of course as soon as I post this, I sorted out the logic needed for my first issue to go away. Logic was changed from: $variable1="c:\@userid1.txt" $variable2="c:\@userid2.txt" $Message="You have both files" IF EXIST ("$variable1") AND ("variable2") TO: $variable1="c:\@userid1.txt" $variable2="c:\@userid2.txt" $variable3=EXIST "C:\@userid1.txt" $variable4=EXIST "C:\@userid2.txt" $Message="You have both files" IF $variable3 = 1 AND $variable4 = 1 Works fine now. Any thoughts on my RUN issue? much thanks, TS |
||||||||
|
|
|||||||
Welcome to KORG! Your AND logic is wrong in your first post, and overly complex in your second.. You need to qualify each item in the AND Code: If Exist($File1) And Exist($File2) 'both files are present' @CRLF EndIf The quote from the manual is for compatibility with now ancient versions of Kixtart.. the original 16-bit versions would end the script if you used the RUN command. Basically, if you want to emulate the behaviour of the 16b version, your would have to do this Code: $Cmd = 'dos command to run' Run $Cmd Exit 0 Glenn PS - use "code" tags - [ code ] your script[ /code ] to frame your scripts.. the tags are as shown, but with no spaces inside the [ ]. |
||||||||
|
|
|||||||
Thanks for the feedback Glenn. The brief background on what I'm trying to accomplish: We launch an application for end users via a Kix script which makes environment (prod / qa / dev) type modifications to an INI file before running the app EXE. We've found out that several staff are running up to 4 copies of the application EXE, and I've gotten authorization to limit them to only 2 copies of the EXE. So what I'm looking to do with the Kix script mods is essentially EXE count tracking. By creating a txt file each time a user launches the application EXE, and deleting the created txt file each time a user closes the EXE. The script logic for creation of the txt files and checking for existing txt files prior to launching the application EXE was what I was looking to get sorted out with the 'EXIST / AND' statements. I'm actually going to redo that section in 'select - case' format as it seems a better fit than 'IF'. With regards to the RUN issue, my problem is this logic: Code: If $File1 = 1 AND $File2 = 0 copy "c:\scripts\session.txt" "$variable2" RUN "c:\scripts\session2.cmd" EXIT ENDIF The CMD file being referenced has the following in it: Code: Start "Application" /wait C:\application\app.exe \\servername\share\app.ini del \\servernameb\share\session2.txt /q exit I'm using the '/wait' in order to keep the CMD file running so that once the EXE is closed the DEL will trigger and clear the session tracker txt. I know a CMD will be open under the user session because of this, what I've found in /debug however is that using RUN to call the CMD file doesn't exit out the kix script as I would have expected. The kix script is instead waiting for the CMD file that's been called complete before it exits. This is not a huge problem, however I would prefer that the kix script call the CMD file used the run the app, and then close out without waiting for it to end. Thanks, TS |
||||||||
|
|
|||||||
By the way I've tried the modification you mentioned and am seeing the same behavior in /debug. The RUN command calls the script properly, but the Kix script waits for the batch script to finish processing before it closes out. Code: SELECT Case $File1 = 1 AND $File2 = 0 Copy "c:\scripts\session.txt" "$variable2" RUN $CMD2 Exit 0 END SELECT The rest of the script works very well. Thanks for the assistance. TS |
||||||||
|
|
|||||||
Yo TS, Well.. There is somebody from my neck of the woods.. For one conditional check, why are you using a SELECT..CASE? If you need to do a conditional check and need to to find the first true condition, then I can see the point.. This should work for you.. Code: IF $File1 = 1 AND $File2 = 0 Copy "c:\scripts\session.txt" "$variable2" RUN $CMD2 Exit 0 ENDIF HTH, Kent |
||||||||
|
|
|||||||
In a word.. ugh! Using files to track system processes is like herding cats - possible, but you don't really want to. I'll dig up some code I have and post it in a bit, but here's something to consider:
Code: $aProcessList = WMIProcessList('appname') If UBound($aProcessList) < 1 Run 'app.exe args' Else 'Sorry - can only run two instances of "appname"!' @CRLF EndIf Glenn |
||||||||
|
|
|||||||
Glenn you da man! I was initially looking to do a process count directly but couldn't see a way to do it, so I came up with the text file creation / deletion plan. I'll get the posted code tested shortly. Kyder, sorry for any confusion. I posted only the first portion of the select / case structure. TS |
||||||||
|
|
|||||||
K well that didn't work so well on first test. Code: $AppTracking = WMIProcessList ('appname.exe') If UBound ($AppTracking) < 1 Run "C:\appname\app\app.exe $app\app.ini" exit Else MessageBox ("$Message"," ***You are not allowed to run more than two App sessions*** ",0,10) ENDIf allowed me to launch 3 exes. TS |
||||||||
|
|
|||||||
Did you download and include the WMIProcessList UDF? Minor syntax - don't put spaces between function names and the parens. Kix won't mind, but other languages will throw fits. Best to be consistent. Glenn PS - WMIProcessList - latest rev is on the Resources/Kix UDF Lib on my web site. Might be a less-than-current version posted here. |
||||||||
|
|
|||||||
Ok, I'm on your site and see the WMIProcessList UDF. I'm new to the UDF stuff. So your WMIProcessList kix script is a "user developed feature" = UDF? Should I be looking to have my script call that one? Or should I be posting that code into my script to be able to reference it better? I've been a kix user for years btw, but my work with it has largely involved file moves, copies, ini adjustments, reg key stuff that's pretty simple to piece together. Thanks again, TS |
||||||||
|
|
|||||||
Copy the text from the web page and paste it into notepad, saving it as "WMIProcessList.udf" - now you have the beginning of a UDF library. I prefer to embed the UDFs in my code - so, you can paste it into your script, or you can put the UDF library folder on a network share and reference it in your script as Code: Call '\\server\KixUdfLib\WMIProcessList.udf' As I said, I prefer embedding the UDFs in my scripts, as this insures that the script will continue to function if the library is unavailable or I make a change to the UDF that isn't compatible with that script. There's a KixDev package on my site that includes KGen. This tool reviews your script for dependent UDFs, locates them in your UDF library, and generates a finished script with all dependencies included. BTW - UDF means User Defined Function. Glenn |
||||||||
|
|
|||||||
K, saved as WMIProcessList.udf. Placed call to it. Under debug I see it run as soon as the statement for $apptracking goes. WMIProcessList.udf errors out under debug @ line 130. |
||||||||
|
|
|||||||
Does the UDF require a newer version of Kix? I'm running 4.53 on the server this is testing on. TS |
||||||||
|
|
|||||||
I have tried the newest release of Kix and receive the same line error. I should probably mention that the server this is being tested on is a windows 2003 terminal server. So the EXE being looked for will be running under multiple user sessions: If you specify a name, all processeses matching that name will be returned, That comment from the remarks of the UDF concerns me, as it sounds like the script would return all EXE's under all user sessions, vs just the EXE's run by the user executing the script. From command line the following does get the output specific to the EXE I want and only the user that runs it, I just couldn't figure out how to parse the results within Kix. Code: tasklist /FI "Imagename eq app.exe" |
||||||||
|
|
|||||||
Is WMI Enabled? Without posting your entire script, we can't tell what's on line 130. You're right - it isn't user specific. It's possible to track processes per user, but it's a bit more complex. Take a look at WshPipe() UDF to run your TaskList.exe command. The version here on KORG returns a string of all output that you'll need to parse. The enhanced version on my web site returns a 2 element array of StdOut and StdErr, each with an array of lines returned from that data stream. Glenn |
||||||||
|
|
|||||||
Hi Glenn, WMI is enabled. The error @ 130 is not coming from my script (it doesn't have 130 lines), it's coming from the WSHProcessList.udf. I will take a look in a bit to see what line is generating the error and post it. I will take a looksie also at the WshPipe UDF. thanks, TS |
||||||||
|
|
|||||||
This is probably the problem: Code: ;;DEPENDENCIES WMI, TimeDiff() external UDF It's important to read the headers, as they list dependencies such as this. It's why I use KGen to create even simple scripts - it resolves all these dependencies. Glenn |
||||||||
|
|
|||||||
OK - gave an alternative a whirl.. Code: $MaxSess = 2 $UsrCmd = 'cmd.exe' $CmdArg = ' arguments...' $Cmd = 'tasklist /fi "Username eq %USERDOMAIN%\%USERID%" /fi "Imagename eq ' + $UsrCmd + '"' $aResult = WshPipe($Cmd) If UBound($aResult) < ($MaxSess + 1) ; 2 header lines, plus max 2 sessions Run $UsrCmd + ' ' + $CmdArg Else 'Sorry - already running ' $MaxSess ' instances of ' $UsrCmd '!' @CRLF EndIf Glenn |
||||||||
|
|
|||||||
Glenn, Thanks again for the major assist here, feels like we're close. Tried the last bit of code and it steps right through debug no probs, but after two exe's are running it doesn't hit the "ELSE" condition, it just launches a third. Full script code below (some comments removed, all code posted). The latest code is added at the bottom of the script. I did make some minor adjustments. Code: SetConsole("HIDE") ; Declaring variables ; ; PZB = defines what PZB path will be set within CER.ini ; CER = defines where our user's CER.ini file is saved too, and looked for ; PFX = defines the value for PFX for claimsview ; MENU = defines the value for Menu for Claimsview ; $pzb="\\root\Production$$" $cer="\\root\userconfigs$$\@userid\Production" $pfx="0001" $Menu="Y" ; ; $Message=" You are not allowed to run more than two copies of OurApp. " ; Checking for Cer.ini, copying it if it's not there. ; IF NOT Exist ("\\root\userconfigs$$\@userid") MD "\\root\userconfigs$$\@userid" EndIf ; IF NOT Exist ("$cer\cer.ini") MD "$cer" Copy "\\root\cer$$\cer.ini" "$cer" writeprofilestring ("$cer\cer.ini", "System", "PzbPath", "$pzb") EndIf ; writeprofilestring ("$cer\cer.ini", "CLAIMVIEW", "PFX", "$pfx") writeprofilestring ("$cer\cer.ini", "ClAIMVIEW", "MENU", "$menu") ; ; $MaxSess=2 $Tasklist='tasklist /FI "Username eq %USERDOMAIN%\%USERID%" /FI "Imagename eq App.exe"' $aResult=WshPipe($Tasklist) ; If UBound($aResult) < ($MaxSess +1) Run "C:\appname\app\app.exe $cer\cer.ini" Else MessageBox ("$Message"," ***You are not allowed to run more than two App sessions*** ",0,10) EndIf ; Exit TS |
||||||||
|
|
|||||||
Just realized that I hadn't filled in the variables of %userdomain% and %userid% in the code. /sigh not enough coffee yet. I updated that content, confirmed it worked as expected from command line, but same results from running the script, 3 app sessions are launched. TS |
||||||||
|
|
|||||||
hoookay...bout time I got more coffee. Just realized WSHpipe is another UDF script being referenced, which isn't being referenced within my script. I'm updating now, and will be trying again shortly. /sigh. |
||||||||
|
|
|||||||
Yeah, then have another sip and replace the %USERDOMAIN% and %USERID% vars - they should dynamically insert the environment values of the current domain and userid. Type "SET U" at a command prompt on the TS to confirm. Glenn |
||||||||
|
|
|||||||
PS - sorry about not giving the heads-up about the missing UDF... with 220+ UDFs in my library, using them is second-nature. I'm taking a closer look at your code over lunch.. Glenn |
||||||||
|
|
|||||||
Quote: Yeah, then have another sip and replace the %USERDOMAIN% and %USERID% vars - they should dynamically insert the environment values of the current domain and userid. \:\) derp. Should have realized that one, I instead opt'd to use the Kix variables of @domain, and @userid. No worries about the UDF, at least I know to be on the look out for them now ;). TS |
||||||||
|
|
|||||||
OK - try this one Code: SetConsole('HIDE') ; Declaring variables ; ## YEAH?? So really declare them!! ; Dim $PZB ; defines what PZB path will be set within CER.ini Dim $CER ; defines where our user's CER.ini file is saved too, and looked for Dim $PFX ; defines the value for PFX for claimsview Dim $MENU ; defines the value for Menu for Claimsview Dim $Rc ; Return code catcher Dim $Path ; path string Dim $Message ; message header Dim $MaxSess ; maximum number of allowed sessions Dim $TaskList ; command to return list of user tasks Dim $aResult ; array containing STDOUT and STDERR arrays ; ## Get the idea? Declare the rest of the variables, too! ; ## Be consistent with Var Names. I prefer MixedCase for local vars, CAPS for globals and @-Macros ; ## Consistent use of commands improves readability - "WriteProfileString" is easier to recognize than "writeprofilestring" ; ## Not required, but use single quotes for Kix strings so double quotes can easily be embedded. Many ; ## DOS commands require double quotes ; ## Embedding vars and macros in strings is bad - use the form 'text ' + $Var + ' more text ' + @MACRO instead $Rc = SetOption('NoVarsInString', 'On') $Rc = SetOption('NoMacrosInString', 'On') ; ## init all vars here $pzb = '\\root\Production$$' $cer = '\\root\userconfigs$$\@userid\Production' $pfx = '0001' $Menu = 'Y' $MaxSess = 2 ; ; $Message = ' You are not allowed to run more than two copies of OurApp. ' ; ## LOAD THE REQUIRED UDF (or paste it to the end of this file, which is more portable) ; ## if you embed the UDF, delete the next line or you will have a duplicate UDF error Call '.\WshPipe.udf' ; Checking for Cer.ini, copying it if it's not there. ; $Path = '\\root\userconfigs$\' + @USERID ; ## NO VARS/MACROS INSIDE STRINGS IF NOT Exist ($Path) MD $Path EndIf ; IF NOT Exist ($cer + '\cer.ini') MD $cer ; ## NO QUOTES AROUND VARS Copy '\\root\' + cer$ + '\cer.ini' $cer WriteProfileString($cer + '\cer.ini', 'System', 'PzbPath', $pzb) EndIf ; WriteProfileString($cer + '\cer.ini', 'CLAIMVIEW', 'PFX', $pfx) WriteProfileString($cer + '\cer.ini', 'ClAIMVIEW', 'MENU', 'menu') ; ; $Tasklist='tasklist /FI "Username eq %USERDOMAIN%\%USERID%" /FI "Imagename eq App.exe"' $aResult=WshPipe($Tasklist) ; If UBound($aResult[0]) < ($MaxSess +1) Run 'C:\appname\app\app.exe ' + $cer + '\cer.ini' Else MessageBox('$Message',' ***You are not allowed to run more than two App sessions*** ',0,10) EndIf ; ; ## Specify the exit code - leave nothing to chance Exit 0 Glenn |
||||||||
|
|
|||||||
derp? Either environment var or Kix Macro should work. Glenn |
||||||||
|
|
|||||||
IMHO something is not right with this line Code: IF UBound($aResult) < ($MaxSess + 1) You made the comment that: Quote: 2 header lines, plus max 2 sessions If $MaxSess = 2, and you +1, that's not quite the 4 lines involved with '2 header lines, plus 2 exe lines'. Or am I missing something with that? In just toying around with it (changing the $MaxSess value, changing the + value) I am unable to get the script to ever hit it's "ELSE" value. TS |
||||||||
|
|
|||||||
Glenn, You da man! Thanks again for all your assistance with this. Tweaking the UBound command worked. However, I seem unable to launch more than 1 application session now. BTW = I will look to amend my kix scripting ways and adjust as you've suggested. I've been a hack for so long it will be hard to 'correct' myself and write code the proper way . .but i'll try ;). TS PS - Derp. haven't watched Southpark have you? = http://www.urbandictionary.com/define.php?term=derp&defid=134579 |
||||||||
|
|
|||||||
Those are just suggestions from a programming class I teach.. good habits is all. They become more important as projects get larger and more complex. I'll take a look again to see why you're only getting one session. Glenn |
||||||||
|
|
|||||||
Found the problem.. Using this code to test on my PC Code: Break On Dim $MaxSess, $TaskList, $aResult, $Message, $Rc, $ $Message ='PROCSS LIMIT EXCEPTION' $MaxSess = 4 $Tasklist='tasklist /FI "Username eq %USERDOMAIN%\%USERNAME%" /FI "Imagename eq cmd.exe"' 'Running ' $Tasklist ? $aResult=WshPipe($Tasklist) UBound($aResult[0]) -1 ' processes' ? For $ = 0 to UBound($aResult[0]) $ ' ' $aResult[0][$] ? Next ; If UBound($aResult[0]) < ($MaxSess + 1) Run '%COMSPEC%' Else $Rc = MessageBox('You are not allowed to run more than two App sessions',$Message,0,10) EndIf Code: TECHSERV14 - K:\Misc>limit Running tasklist /FI "Username eq DOMAIN\userid" /FI "Imagename eq cmd.exe" 4 processes 0 1 Image Name PID Session Name Session# Mem Usage 2 ========================= ======== ================ =========== ============ 3 cmd.exe 2976 RDP-Tcp#0 2 4,376 K 4 cmd.exe 5976 RDP-Tcp#0 2 5,476 K 5 Code: If UBound($aResult[0]) < ($MaxSess + 3) Glenn |
||||||||
|
|
|||||||
Looks like that did it Glenn. Existing script now limits a user to only 2 app sessions, message pops up on third attempt. I'm going to re-write the whole thing in keeping with your suggestions. One further question with that however. You mention that Quote: Embedding vars and macros in strings is bad Wouldn't that mean that this particular VAR is 'bad' as well? Code: $cer = '\\root\userconfigs$$\@userid\production' I would presume that I should instead be stating this as Code: $cer = '\\root\userconfigs$$\' + @userid + '\production' Can't say thanks enough for all your help on this . .but I'll try thanks again, TS |
||||||||
|
|
|||||||
no problem! Yes, that's almost correct, but - if you've taken my example with the NoVarInStrings setting, then drop the double-$. That setting eliminates the need to double up on $ inside of quotes. Glenn |
||||||||
|
|
|||||||
Interesting, I've always understood it that Kix requires the double - $ to properly process hidden shares. But I guess that's just due to the way my statements were written. Re-working it now, will post it when done. TS |
||||||||
|
|
|||||||
If you don't disable variable expansion - SetOption('NoVarsInStrings', 'on') - you need to "escape" the $ by doubling it. Same with macros and the @. With variable expansion, Kix has to peek inside every string, look for "$" chars, and try to figure out if what follows it is a variable, and replace the variable with its value. When you want a "$", you use "$$", which tells Kix "no - really put a $ here!" When you disable variable expansion, you basically are saying "I promise not to put variables inside of quoted strings, so - Kix - take it easy and just output the strings without any extra effort!" Yes, this requires extra effort on our part to build the text strings properly, but it avoids many other issues with data manipulation. Imagine "The price is $$$Price" vs "The price is $" + $Price - where do you think more problems will occur? Glenn PS - take a look at the Sanity UDF. It calls out undeclared vars, duplicate declarations, finds mismatched quotes, parens, and even insures that paired commands (like If/EndIf) are properly matched up. KGen uses Sanity as a final step in assembling scripts, so it's a quick way to validate even simple scripts that don't use UDFs. |
||||||||
|
|
|||||||
Had to tinker with the syntax a tad to get the application to launch properly, but here's the final product: Code: SetConsole('HIDE') ; Declaring Global variables ; Dim $PZB ; defines what PZB path will be set within CER.ini Dim $Path ; User path string Dim $CER1 ; defines where the CER.ini base file is stored Dim $CER2 ; defines the environment folder Dim $CER3 ; defines the environment folder for the CER.ini Dim $CER4 ; defines where the CER.ini file is saved to, and looked for Dim $PFX ; defines the value for PFX for claimsview Dim $MENU ; defines the value for Menu for claimsview Dim $MaxSession ; Maximum number of allowed sessions Dim $App ; defines the EXE to be Run Dim $Message ; message header Dim $TaskList ; command to return list of user tasks Dim $aResult ; array containing STDOUT and STDERR arrays Dim $Rc ; Return code catcher ; Call 'c:\scripts\WshPipe.udf' ; Places a call to a script that helps identify running user EXE's. ; $Rc = SetOption('NoVarsInString', 'On') $Rc = SetOption('NoMacrosInString', 'On') ; $pzb = '\\root\Production$' $Path = '\\root\userconfigs$\' + @Userid $cer1 = '\\root\cer$' $cer2 = '\Production' $cer3 = $Path + $cer2 $cer4 = $cer3 + '\cer.ini' $pfx = '0001' $Menu = 'Y' $MaxSession = 2 $App = 'c:\appname\app\app.exe' $Message = ' You are not allowed to run more than two copies of OurApp. ' $Tasklist='tasklist /FI "Username eq %USERDOMAIN%\%USERNAME%" /FI "Imagename eq App.exe"' $aResult=WshPipe($Tasklist) ; ; Checking for Cer.ini, copy it if it's not there ; IF NOT Exist ($Path) MD $Path EndIf ; If NOT Exist ($cer4) MD $cer3 copy $cer1 + '\cer.ini' $cer3 WriteProfileString($cer4, 'System', 'PzbPath', $pzb) WriteProfileString($cer4, 'CLAIMVIEW', 'PFX', $pfx) writeProfileString($cer4, 'CLAIMVIEW', 'MENU', $Menu) EndIf ; ; If UBound($aResult[0]) < ($MaxSession +3) Run "$App $cer4" Else MessageBox ("$Message"," ***You are not allowed to run more than two App sessions*** ",0,10) EndIf ; ; Exit 0 I tried many different means of calling the App executable, but could not get it launch with the INI being specified until I finally put the two variables in quotes. Code: Run "$App $cer4" Seems like that's against syntax, but was the only way it would launch properly. Thanks 20 times over again for your work with me on this Glenn, I'll be rolling this to test servers soon, and hopefully prod next week. All looks good so far. TS |
||||||||
|
|
|||||||
You can't Run $App $Cer4 because those are variables, and kix will simply mash them together. The result is trying to run Code: c:\appname\app\app.exe\\root\userconfigs$\<UserID>\Production\cer.ini Code: $Cmd = $App + $Cer4 'Running: ' $Cmd @CRLF Run $Cmd Code: $Cmd = $App + ' ' + $Cer4 Simple concept is to not Run 'A complex command string with args...', but to build $Cmd, bit by bit if necessary, then display it during development before actually running it. You could then copy it off the screen and paste it back into the command prompt to see what it chokes on. After fixing it (adding the space between command and arg in this case) you can comment-out the debug message. In many of my scripts, I include the Msg() UDF library, which includes a Dbg() function. I define a Global var $DEBUG, and setting it to a non-zero value allows the debug messages to be displayed: Code: Dbg('About to run: ' + $Cmd) Glenn |
||||||||
|
|
|||||||
Hiya Glenn, You say "can't run", but it worked. I did feel a bit naughty putting the two Vars in quotes, but after playing with it for a bit I was just happy to get it launching properly. With adjusting the Run command to Code: Run $App + ' ' + $cer4 It works properly too, and I hope that's the better syntax. One last syntax question. As I look the final script over, it seems like there's more lines spent declaring variables, then there is doing the actual work. On the one hand, that makes it much much easier to mod this script for our different environments (qa, prod, dev, etc); two Vars get changed and it's good to go. On the other hand, I'm a bit confused between the need for 'DIM' in addition to '$' for variables. I always understood "$" to be defining a variable for the context of the script. What does "DIM" add to the statement of variables? I'm not sure I understand the distinction of a 'local variable' as the manual defines it. much thanks as always TS |
||||||||
|
|
|||||||
I say "can't run" because if you DON'T use quotes, Kix will mangle the command. Code: $Var = 4 Code: Dim $Var $Var = 4 Imagine that you use the variable "x" as a simple index counter inside a for/next loop. Inside that loop, you call a function that also uses "x" as a temporary variable. Without this restriction of scope, the function will modify the global value, affecting the loop in your main program. Using Dim to define a local var makes Kix treat the $X in the main program and $X in the function as totally different variables, even though they seem to have the same name. You'll see that the UDFs that I write take this one step further. Any variable used in my functions uses the form $_Name. Introducing the "_" char serves two purposes - if someone uses the function without declaring the Explicit option, conflicts in variable names would be unlikely. Further, it provides a visual distinction - my brain sees $_X and knows it's different from $X in the main program. I can appreciate your feelings for 10 Dim lines for 5 lines of code, so to speak. You can Code: Dim $X, $Y, $Z, $A, $B, $C Code: Dim $I, $J, $K ; index pointers Dim $aFiles, $File ; array of files & enumerator Dim $lblField1, $lblField2 ; KixForms field labels for form 1 I also use liberal amounts of comments. KGen allows you to strip off all of the comments from the finished script, so I can compare the commented and uncommented scripts.. this shows that my large projects have a 1.3:1 comment to code ratio. Yes, there are more comment than code characters! Hope that clears things up. Glenn |