syntax53
(Fresh Scripter)
2013-04-19 10:26 PM
Just Sharing - Adding Printers based on computer name via INI

I work at a school district and our users need to have network printers loaded onto computers based on the location of the computer. Teachers can move around the building (and even to other buildings) and need to have access to the closest printer. I posted this thread awhile ago to get an initial feeler for how to go about it. So the deployment was this week and I got the bugs ironed out. Below is the final script.

EDITED 5/1/2013 to include some extra options for the INI and moving the global declaration out of the function as suggested below.


First the UDF's I'm using:

 Code:
;FROM http://www.kixtart.org/forums/ubbthreads.php?ubb=showflat&Number=118766
function GetDefaultPrinter()
    $GetDefaultPrinter = join(split(readvalue("HKEY_CURRENT_USER\Software\Microsoft\Windows NT\CurrentVersion\Windows","Device"),',',1),'')
endfunction

;FROM http://www.kixtart.org/forums/ubbthreads.php?ubb=showflat&Number=202790#Post202790
Function IniArray($_fSrcFile, OPTIONAL $_aDataWrite)
 
  Dim $_					; temp var 
  Dim $_Fp					; file pointer 
  Dim $_I, $_J					; index pointers 
  Dim $_Sect					; Section Name 
  Dim $_aDat					; Data pair 
  Dim $_aSect[1,0], $_aData[1, 0]		; Section & Data Arrays 
 
 
  ; Obtain a File Handle for Read or Write operations 
  ; ============================================================ 
  $_Fp = FreeFileHandle				; locate an available file handle 
  If Not $_Fp
    Exit 1					; none available - exit 
  EndIf
 
 
  ; WRITE: verify that we have properly formatted data 
  ; ============================================================ 
  If VarType($_aDataWrite)  > 0			; Write the array to the INI file and exit 
    If VarType($_aDataWrite) < 8192		; data but not an array - exit! 
      Exit 87
    EndIf
 
    Del $_fSrcFile				; delete any pre-existing file 
    $_ = Open($_Fp, $_fSrcFile, 5)		; open the file for write/create 
    If @ERROR Exit @ERROR EndIf			; exit if error opening 
 
    ; Write the section data. If no data exists in the section, do not write anything! 
    For $_I = 0 to UBound($_aDataWrite, 2)
      $_Sect  = $_aDataWrite[0, $_I]		; Section name to write 
      $_aData = $_aDataWrite[1, $_I]		; Data array 
      If UBound($_aData, 2) > -1		; create Sect and write data if data is present 
        $_ = '[' + $_Sect + ']' + @CRLF		; section name 
        For $_J = 0 to UBound($_aData, 2)	; key/data pairs 
          If $_aData[1, $_J]			; only write keys that have non-null data 
            $_ = $_ + $_aData[0, $_J] + '=' + $_aData[1, $_J] + @CRLF
          EndIf
        Next
        $_ = WriteLine($_Fp, $_)		; write the output line 
        If @ERROR Exit @ERROR EndIf		; exit if error writing 
      EndIf
    Next
 
    $_ = Close($_Fp)				; close the file 
    ReDim $_aData[1, 0]				; clear array 
 
    ; exit 0 ; do not exit here to force a re-read of the freshly written data 
 
  EndIf
 
 
  ; READ: Load the ini file into an array 
  ; ============================================================ 
  $_I = -1  $_J = -1				; Initialize index pointers 
  
  $_ = Open($_Fp, $_fSrcFile, 2)		; open the file for read 
  If @ERROR Exit @ERROR EndIf			; exit if error opening 
 
  Do						; loop through the file contents 
    $_ = Trim(ReadLine($_Fp))			; remove leading/trailing spaces 
 
    If Left($_, 1) = '['			; found a section 
 
      If $_I >= 0				; process prior section data, if any 
        $_aSect[1, $_I] = $_aData
        ReDim $_aData[1, 0]
        $_J = -1
      EndIf
 
      $_I = $_I + 1				; increment the section index 
      ReDim Preserve $_aSect[1, $_I]
      $_aSect[0, $_I] = Split(SubStr($_, 2), ']')[0]
    Else
      If Not Left($_, 1) = ';' And Not Left($_, 1) = '#' And Len($_) > 2
        $_aDat = Split($_, '=')			; break into array 
        $_J = $_J + 1				; increment the data index 
        ReDim Preserve $_aData[1, $_J]
        $_aData[0, $_J] = $_aDat[0]		; Store the data 
        $_aData[1, $_J] = $_aDat[1]		; Store the data 
      EndIf
    EndIf
 
  Until @ERROR					; done with input data 
 
  $_ = Close($_Fp)				; close the file 
 
  ; process the last/only section 
  If $_I >= 0
    $_aSect[1, $_I] = $_aData
  EndIf
 
  $IniArray = $_aSect				; return the array 
 
  Exit 0					; exit success 
 
EndFunction
 
 
Function ReadIniArray($_aIniFile, OPTIONAL $_Section, OPTIONAL $_Key)
 
  ; exit immediately if the data format is invalid 
  If VarType($_aIniFile) < 8192			; data but not an array - exit! 
    Exit 87
  EndIf
 
  Dim $_aSectIdx[UBound($_aIniFile, 2)]		; Section Index Array 
  Dim $_I, $_J					; Index pointers 
  Dim $_aData					; Array of Key/Data pairs 
 
  ; Create a section index array 
  For $_I = 0 to UBound($_aIniFile, 2)
    $_aSectIdx[$_I] = $_aIniFile[0, $_I]
  Next
 
  ; If the Section is null, return a delimited string of Sections [same as ReadProfileString(file)] 
  If Not $_Section
    $ReadIniArray = Join($_aSectIdx, Chr(10))
    Exit 0
  EndIf
 
  ; Search the index for a section 
  $_I = aScan($_aSectIdx, $_Section)
  If $_I < 0 Exit 2 EndIf			; section not found - Exit 
 
  $_aData = $_aIniFile[1, $_I]			; Extract the key/value array 
 
  ; Create a Key index for the requested section 
  Dim $_aKeyIdx[UBound($_aData, 2)]
  For $_J = 0 to UBound($_aData, 2)
    $_aKeyIdx[$_J] = $_aData[0, $_J] 
  Next
 
  ; If the Key is null, return a delimited string of Keys [same as ReadProfileString(file, section)] 
  If Not $_Key
    $ReadIniArray = Join($_aKeyIdx, Chr(10))
    Exit 0
  EndIf
 
  ; Search the index for a Key 
  $_J = aScan($_aKeyIdx, $_Key)
  If $_J < 0 Exit 2 EndIf			; Key not found 
 
  $ReadIniArray = $_aData[1, $_J]
 
  Exit 0
 
EndFunction
 


;Modified FROM http://www.kixtart.org/forums/ubbthreads.php?ubb=showflat&Number=203126#Post203126
function INISections($array)
	$read = readiniarray($array)
	if LEN($read) > 0
		$INISections=split($read,chr(10))
	else
		$INISections = ""
	endif
endfunction

function INIKeys($array,$section)
	$read = readiniarray($array,$section)
	if LEN($read) > 0
		$INIKeys=split($read,chr(10))
	else
		$INIKeys = ""
	endif
endfunction


;FROM http://www.kixtart.org/forums/ubbthreads.php?ubb=showflat&Number=84020
function mid($midstring,$midstart,optional $midend)
  if $midend<0
    $midend=abs($midend)
  else 
    if $midend>len($midstring)-abs($Midstart) or $midend=""
      $midend=0
    endif
  endif
  select
    case $midstart<0
      $midstart=abs($midstart)
      If $midend=0 
        $mid=left(right($midstring,len($midstring)),(len($midstring)-$midstart)+1)
      else
        $mid=left(right($midstring,($midstart+$midend)-1),$midend)
      endif
    case $midstart>0
      if $midend=0
        $mid= right(left($midstring,len($midstring)),(len($midstring)-$midstart)+1)
      else
        $mid=right(left($midstring,($midstart+$midend)-1),$midend)
      endif
  endselect
endfunction


And here is my script:
 Code:
$debug = 0

IF NOT InStr(UCase(@PRODUCTTYPE), 'SERVER') AND NOT InStr(UCase(@PRODUCTTYPE), 'DOMAIN') ;DONT TOUCH SERVERS
	
	$userid = Ucase(@USERID)
	$workstation = Ucase(@WKSTA)
	$domain = Ucase(@DOMAIN)
	
	;IF $debug = 1 $workstation = "aj324-3" ENDIF
	
	$school=Left($workstation, 2)
	$netprint_ini = ""
	SELECT
		case $school = "AJ" OR $domain = "JUNIOR"
			$netprint_ini = "script_netprint_JUNIOR.ini"
		case $school = "AS" OR $domain = "SENIOR"
			$netprint_ini = "script_netprint_SENIOR.ini"
		case $school = "CB" OR $domain = "COPPER"
		case $school = "HI" OR $domain = "HIGHLAND"
		case $school = "MK" OR $domain = "MCKINLEY"
		case $school = "OV" OR $domain = "OVERLOOK"
		case $school = "RO" OR $domain = "ROSLYN"
		case $school = "RW" OR $school = "RY" OR $school = "RE" OR $domain = "RYDAL"
		case $school = "WH" OR $domain = "WILLOW_HILL"
		case $school = "AA" OR $domain = "ADMIN" ;must be last
			$netprint_ini = "script_netprint_ADMIN.ini"
			
	ENDSELECT
	
	IF NOT $netprint_ini = ""
		SELECT
			case EXIST(@ScriptDir + "\" + $netprint_ini)
				IF $debug = 1 ? @Time + ": NP: INI found in script dir" ENDIF
				$netprint_ini = @ScriptDir + "\" + $netprint_ini
			case EXIST(@LSERVER + "\NETLOGON\" +  $netprint_ini)
				IF $debug = 1 ? @Time + ": NP: INI found at logon server" ENDIF
				$netprint_ini = @LSERVER + "\NETLOGON\" +  $netprint_ini
			case EXIST(@LDRIVE + $netprint_ini)
				IF $debug = 1 ? @Time + ": NP: INI found at logon drive" ENDIF
				$netprint_ini = @LDRIVE + $netprint_ini
		ENDSELECT
		
		IF $debug = 1 ? @Time + ": NP: ini: " + $netprint_ini ENDIF

		$network_printers = IniArray($netprint_ini)
		IF NOT @ERROR = 0
			IF $debug = 1 ? "Error opening file: " + @ERROR + ": " + @SERROR ENDIF
		ELSE
			$current_default_printer = GetDefaultPrinter
			
			GLOBAL $prefix
			GLOBAL $roomnumber
			GLOBAL $beforedash
			GLOBAL $afterdash
			GLOBAL $pcnumber
			$prefix = ""
			$roomnumber = ""
			$beforedash = ""
			$afterdash = ""
			$pcnumber = ""
			
			$ret = DecipherComputerName($workstation)
			
			IF $debug = 1
				? @Time + ": NP: prefix: '" + $prefix + "'"
				? @Time + ": NP: room: '" + $roomnumber + "'"
				? @Time + ": NP: beforedash: '" + $beforedash + "'"
				? @Time + ": NP: afterdash: '" + $afterdash + "'"
				? @Time + ": NP: pcnum: '" + $pcnumber + "'"
			ENDIF
	
			GLOBAL $building_printers
			GLOBAL $new_default_printer
			$building_printers = 1
			$new_default_printer = ""
			
			$printers_added = 0
			
			;[COMPUTERNAME\USERNAME]
			IF $printers_added = 0
				$section = $workstation + '\' + $userid
				$printers_added = ProcessSection($network_printers, $section)
			ENDIF
			
			;[COMPUTERNAME-*\USERNAME]
			IF $printers_added = 0
				IF NOT $beforedash = ""
					$section = $beforedash
				ELSE
					$section = $prefix + CSTR($roomnumber)
				ENDIF
				$section = $section + '-*\' + $userid
				$printers_added = ProcessSection($network_printers, $section)
			ENDIF
			
			;[COMPUTERNAME]
			IF $printers_added = 0
				$section = $workstation
				$printers_added = ProcessSection($network_printers, $section)
			ENDIF
			
			;[COMPUTERNAME-*]
			IF $printers_added = 0
				IF NOT $beforedash = ""
					$section = $beforedash
				ELSE
					$section = $prefix + CSTR($roomnumber)
				ENDIF
				$section = $section + '-*'
				$printers_added = ProcessSection($network_printers, $section)
			ENDIF
			
			IF $printers_added = 0
				IF $debug = 1 ? @Time + ": NP: Iterating sections..." ENDIF
				$sections = INISections($network_printers)
				For Each $section in $sections
					IF $printers_added = 0
					
						;   [PREFIX_LOWRM#-LOWPC# through PREFIX_HIGHRM#-HIGHPC#]
						;OR [PREFIX-LOWPC# through PREFIX-HIGHPC#]
						$t = INSTR($section, " through ")
						IF $t > 0 AND NOT $pcnumber = "" AND $pcnumber = $afterdash AND (IsNumeric($roomnumber) = 1 OR $roomnumber = "")
							IF $debug = 1 ? @Time + ": NP: Processing section '" + $section + "' ..." ENDIF
							$section_prefix = ""
							$room_low = ""
							$room_high = ""
							$pc_low = ""
							$pc_high = ""
							$dash = ""
							For $i = 1 to $t
								$char = MID($section, $i, 1)
								SELECT
									CASE $char = "*"
										$pc_low = "0"
									CASE $char = "-"
										$dash = $char
									CASE $char = " "
										$i = 99999
									CASE IsNumeric($char) = 1
										IF $dash = ""
											$room_low = $room_low + $char
										ELSE
											$pc_low = $pc_low + $char
										ENDIF
									CASE 1
										IF $room_low = "" $section_prefix = $section_prefix + $char ENDIF
								ENDSELECT
							Next
							
							IF $debug = 1 ? @Time + ": NP: 1st prefix: " + $section_prefix ENDIF
							
							IF $section_prefix = $prefix AND NOT $pc_low = ""
								$dash = ""
								$section_prefix = ""
								For $i = ($t+LEN(" through ")) to LEN($section)
									$char = MID($section, $i, 1)
									SELECT
										CASE $char = "*"
											$pc_high = "32768"
										CASE $char = "-"
											$dash = $char
										CASE $char = " "
											$i = 99999
										CASE IsNumeric($char) = 1
											IF $dash = ""
												$room_high = $room_high + $char
											ELSE
												$pc_high = $pc_high + $char
											ENDIF
										CASE 1
											IF $room_high = "" $section_prefix = $section_prefix + $char ENDIF
									ENDSELECT
								Next
								
								IF $debug = 1 ? @Time + ": NP: 2nd prefix: " + $section_prefix ENDIF
								
								IF $section_prefix = $prefix AND NOT $pc_high = ""
									$room_low = VAL($room_low)
									$room_high = VAL($room_high)
									$pc_low = VAL($pc_low)
									$pc_high = VAL($pc_high)
									
									IF $debug = 1
										? @Time + ": NP: room low: " + $room_low
										? @Time + ": NP: room high: " + $room_high
										? @Time + ": NP: pc low: " + $pc_low
										? @Time + ": NP: pc high: " + $pc_high
									ENDIF
									
									IF VAL($pcnumber) >= $pc_low AND VAL($pcnumber) <= $pc_high
										IF ((VAL($roomnumber) >= $room_low AND VAL($roomnumber) <= $room_high) OR ($roomnumer = "" AND $room_low = 0 AND $room_high = 0))
											IF $debug = 1 ? @Time + ": NP: section matched." ENDIF
											$printers_added = ProcessSection($network_printers, $section)
										ELSE
											IF $debug = 1 ? @Time + ": NP: Machine out of room number range." ENDIF
										ENDIF
									ELSE
										IF $debug = 1 ? @Time + ": NP: Machine out of section's PC number range." ENDIF
									ENDIF
								ELSE
									IF $debug = 1 ? @Time + ": NP: Section prefix doesn't match OR pc_high not found." ENDIF
								ENDIF
							ELSE
								IF $debug = 1 ? @Time + ": NP: Section prefix doesn't match OR pc_low not found." ENDIF
							ENDIF
						ELSE
							;[BEFOREDASH-PC1,PC2,PC3]
							$c = INSTR($section, ",")
							$d = INSTR($section, "-")
							IF $d > 0 AND $c > $d
								IF $debug = 1 ? @Time + ": NP: Processing section '" + $section + "' ..." ENDIF
								$section_beforedash = MID($section, 1, $d-1)
								IF $debug = 1 ? @Time + ": NP: section beforedash: '" + $section_beforedash + "'" ENDIF
								IF $section_beforedash = $beforedash
									$postfixes = SPLIT(MID($section, $d+1), ",")
									For Each $section_afterdash in $postfixes
										IF $printers_added = 0
											IF $debug = 1 ? @Time + ": NP: section afterdash: '" + $section_afterdash + "'" ENDIF
											IF $section_afterdash = $afterdash
												IF $debug = 1 ? @Time + ": NP: section matched." ENDIF
												$printers_added = ProcessSection($network_printers, $section)
											ENDIF
										ENDIF
									Next
								ELSE
									IF $debug = 1 ? @Time + ": NP: Section before dash doesn't match." ENDIF
								ENDIF
							ENDIF
						ENDIF
					ENDIF
				Next
			ENDIF
			
			
			IF $printers_added = 1
				IF $building_printers = 1
					IF ContainsNumbers($userid) < 2 AND INGROUP("Students") = 0 ;NON-STUDENTS
						COLOR w/n
						? " Adding Printers, please wait..."
						IF NOT $debug = 1 COLOR n/n ENDIF
						$ret = ProcessSection($network_printers, "BUILDING_PRINTERS", 1)
					ENDIF
				ENDIF
				
				IF NOT $new_default_printer = ""
					$ret = SETDEFAULTPRINTER($new_default_printer)
					IF $debug = 1 ? @Time + ": NP: " + "SETDEFAULT( $new_default_printer ) -- RETURN: " + $ret ENDIF
				ELSE
					IF NOT $current_default_printer = ""
						$ret = SETDEFAULTPRINTER($current_default_printer)
						IF $debug = 1 ? @Time + ": NP: " + "SETDEFAULT( $current_default_printer ) -- RETURN: " + $ret ENDIF
					ENDIF
				ENDIF
			ENDIF
		ENDIF
	ENDIF
ENDIF

function ProcessSection($iniarray, $section, OPTIONAL $ignoredefault)
	$ProcessSection = 0
	IF $debug = 1 ? @Time + ": NP: Processing section '" + $section + "' ..." ENDIF
	
	$keylist = INIKeys($iniarray, $section)
	IF UBOUND($keylist) >= 0
		IF $debug = 1 ? @Time + ": NP: Section Found..." ENDIF
		$ProcessSection = 1
		For $i = 0 to UBOUND($keylist)
			$value = ReadIniArray($iniarray, $section, $keylist[$i])
			IF $keylist[$i] = "printer1" AND LEFT($value, 2) = "\\" AND NOT $ignoredefault = 1
				$new_default_printer = $value
			ENDIF
			IF $keylist[$i] = "building_printers" AND $value = "false"
				IF $debug = 1 ? @Time + ": NP: building_printers=false Found..." ENDIF
				$building_printers = 0
			ENDIF
			
			IF LEFT($keylist[$i], 7) = "printer" AND LEFT($value, 2) = "\\"
				$ret = ADDPRINTERCONNECTION($value) ;
				IF $debug = 1 ? @Time + ": NP: " + "ADDPRINTER( $value ) -- RETURN: " + $ret ENDIF
			ENDIF
		Next
	ENDIF
endfunction

;This takes a computer name like AS123-4 and breaks it down into variables
;$prefix is characters from the start of the string until the first number OR the first dash
;$roomnumber is the first sequence of numbers before the dash
;$beforedash is everything before the dash
;$afterdash is everything after the dash
;$pcnumber is the numbers after the dash unless there are letters in the mix
;$pcnumber would be blank on AS123-B4 and on AS123-4B5 $pcnumber would stop after the 4
function DecipherComputerName($pcname)
	$dash = ""
	
	For $i = 1 to LEN($pcname)
		$char = MID($pcname, $i, 1)
		
		SELECT
			CASE $char = "-"
				$dash = "-"
				
			CASE IsNumeric($char) = 1
				IF $dash = ""
					$roomnumber = $roomnumber + $char
				ELSE
					$pcnumber = $pcnumber + $char
				ENDIF
			
			CASE 1
				SELECT
					CASE $dash = "" AND $roomnumber = ""
						$prefix = $prefix + $char
					CASE $dash = "" AND NOT $roomnumber = ""
						$roomnumber = $roomnumber + $char
					CASE $dash = "-"
						;$i = LEN($pcname)+1
				ENDSELECT
		ENDSELECT
		
		IF $dash = ""
			$beforedash = $beforedash + $char
		ELSE
			IF NOT $char = "-"
				$afterdash = $afterdash + $char
			ENDIF
		ENDIF
	Next
endfunction

;this just counts the number of numbers in a string
;our student logons are all numbers so this is basically
;another way to detect a student logon
function ContainsNumbers($teststr)
    $ContainsNumbers = 0
    FOR $pos = 1 TO LEN($teststr)
        $char = SUBSTR($teststr, $pos, 1)
        SELECT
            case $char = "0"
		$ContainsNumbers = $ContainsNumbers+1
            case $char = "1"
		$ContainsNumbers = $ContainsNumbers+1
            case $char = "2"
		$ContainsNumbers = $ContainsNumbers+1
            case $char = "3"
		$ContainsNumbers = $ContainsNumbers+1
            case $char = "4"
		$ContainsNumbers = $ContainsNumbers+1
            case $char = "5"
		$ContainsNumbers = $ContainsNumbers+1
            case $char = "6"
		$ContainsNumbers = $ContainsNumbers+1
            case $char = "7"
		$ContainsNumbers = $ContainsNumbers+1
            case $char = "8"
		$ContainsNumbers = $ContainsNumbers+1
            case $char = "9"
		$ContainsNumbers = $ContainsNumbers+1
        ENDSELECT
    NEXT
endfunction


And here is a sample of the INI file I made up
 Code:
; ========================================================
;                       FORMAT OF FILE
; ========================================================
;
; [computername-1]
; printer1=\\server\printer name     ;printer1 = Default Printer.  If you don't want to set (or change) their default printer then start with printer2.
; printer2=\\server\printer name     ;OPTIONAL: Extra network printers to add for all users (students included).  Add more with printer3, printer4, etc.
; building_printers=false            ;OPTIONAL: With this flag it will not add any printers from the [BUILDING_PRINTERS] section (applies to non-students only)
;
; [computername-1\username]          ;User Name Override -- This user will only read this section and not the regular section for the computer
; printer1=\\server\printer name
;
; [computername-*]                   ;Printer Settings apply to all machines with the prefix (* must be preceeded by the first dash)
; printer1=\\server\mono printer 1   ;NOTE: You can override this for a single machine by specifying the actual computer name in another section.
; printer2=\\server\mono printer 2
; printer3=\\server\color printer
;
; [BEFOREDASH-PC1,PC2,PC3]           ;Match beforedash to after dash options.  e.g. AJGUID-A,B,D  or  AJLIB-CIRC,OFFICE1,OFFICE2
;
; [ABC100-1 through ABC120-1]        ;Range such as [AJ100-1 through AJ120-1] or [AJLIB-1 through AJLIB-15] or [AJP14-* through AJP18-*].
;
; The script checks for priority in the most specific to least:
; [computername\username] > [computername-*\username] > [computername] > [computername-*] > [ABC100-1 through ABC120-1]
;
; ========================================================

[BUILDING_PRINTERS]
;Printers that get added to all NON-Students unless building_printers=false is specified.
printer1=\\AJH2\Library HPM551 (Color)
printer2=\\AJH2\Library HPP3010#1 (Mono)
printer3=\\AJH2\Library HPP3010#2 (Mono)
printer4=\\AJH2\RICOH Copier Guidance
printer5=\\AJH2\RICOH Copier Library
printer6=\\AJH2\RICOH Copier Mail Rm
printer7=\\AJH2\RICOH Special Ed
printer8=\\AJH2\Rm 116 Office
printer9=\\AJH2\Rm 136 Learning Center
printer10=\\AJH2\Rm 215 Office
printer11=\\AJH2\Rm 224 DE5100 (Color)
printer12=\\AJH2\Rm 224 DE5300 (Mono)
printer13=\\AJH2\Rm 230 DE5130cdn (Color)
printer14=\\AJH2\Rm 230 DE5350dn (Mono)
printer15=\\AJH2\Rm 242 Soc Studies Office
printer16=\\AJH2\Rm 253 HP 4050 (Mono)
printer17=\\AJH2\Rm 310 Office
printer18=\\AJH2\Rm 324 DE5100 (Color)
printer19=\\AJH2\Rm 324 DE5200 (Mono)
printer20=\\AJH2\Rm 342 English Office
printer21=\\AJH2\Rm C58 Classroom
printer22=\\AJH2\Rm C58 DE3010cn (Color)
printer23=\\AJH2\Rm P18 DE5350dn (Mono)
printer24=\\AJH2\Rm S102B Science Office


;RANGES
;=================

[AJ108-1 through AJ126-1]
printer1=\\ajh2\Rm 116 Office

[AJ136-*]
printer1=\\ajh2\Rm 136 Learning Center

[AJ128-1 through AJ157-1]
printer1=\\ajh2\Rm 136 Learning Center

[AJ208-1 through AJ227-1]
printer1=\\ajh2\Rm 215 Office

[AJ215-*]
printer1=\\ajh2\Rm 215 Office

[AJ228-1 through AJ265-1]
printer1=\\ajh2\Rm 242 Soc Studies Office

[AJ310-*]
printer1=\\ajh2\Rm 310 Office

[AJ308-1 through AJ329-1]
printer1=\\ajh2\Rm 310 Office

[AJ324-*]
building_printers=false
printer1=\\ajh2\Rm 324 DE5200 (Mono)
printer2=\\AJH2\Rm 324 DE5100 (Color)

[AJ330-1 through AJ363-1]
printer1=\\ajh2\Rm 342 English Office

[AJ351-*]
printer1=\\ajh2\Rm 351 DE2330dn (Mono)

[AJC51-1 through AJC60-1]
printer1=\\ajh2\Rm C58 Classroom

[AJC58-*]
printer1=\\ajh2\Rm C58 Classroom
printer2=\\AJH2\Rm C58 DE3010cn (Color)

[AJGUID-1 through AJGUID-2]
building_printers=false
printer1=\\ajh2\RICOH Copier Guidance
printer2=\\asd11\AJ Guid Dell 2330dn
printer3=\\asd11\AJ Guid HP 1300n
printer4=\\AJH2\RICOH Copier Mail Rm

[AJGUID-C,B,J]
building_printers=false
printer1=\\asd11\AJ Guid HP 1300n
printer2=\\ajh2\RICOH Copier Guidance
printer3=\\asd11\AJ Guid Dell 2330dn
printer4=\\AJH2\RICOH Copier Mail Rm

[AJGUID-F,G,H]
building_printers=false
printer1=\\asd11\AJ Guid Dell 2330dn
printer2=\\ajh2\RICOH Copier Guidance
printer3=\\asd11\AJ Guid HP 1300n
printer4=\\AJH2\RICOH Copier Mail Rm

[AJHEALTH-*]
printer1=\\asd11\JR Health Suite Dell 2330dn

[AJLIB-1 through AJLIB-15]
building_printers=false
printer1=\\AJH2\Library HPP3010#1 (Mono)
printer2=\\AJH2\Library HPP3010#2 (Mono)
printer3=\\AJH2\Library HPM551 (Color)

[AJLIB-16 through AJLIB-29]
building_printers=false
printer1=\\AJH2\Library HPP3010#2 (Mono)
printer2=\\AJH2\Library HPP3010#1 (Mono)
printer3=\\AJH2\Library HPM551 (Color)

[AJLIB-30 through AJLIB-33]
;teacher computers in back
printer1=\\AJH2\Library HPP3010#2 (Mono)

[AJLIB-CIRC,OFFICE1,OFFICE2]
building_printers=false
printer1=\\AJH2\Library HPP3010#1 (Mono)
printer2=\\AJH2\Library HPP3010#2 (Mono)
printer3=\\AJH2\Library HPM551 (Color)
printer4=\\AJH2\Library Lab DE2330

[AJM2-1 through AJM5-1]
printer1=\\ajh2\Rm C58 Classroom

[AJP14-* through AJP18-*]
printer1=\\ajh2\Rm P18 DE5350dn (Mono)

[AJS6-1 through AJS106-1]
printer1=\\ajh2\Rm S102B Science Office


;SPECIFIC MACHINES
;=================

[AJ111-1]
;guidance (has local)
building_printers=true

[AJ114-1]
;guidance (has local)
building_printers=true

[AJ210-1]
;reading chair (has local)
building_printers=true

[AJ216-1]
;guidance (has local)
building_printers=true

[AJ253-1]
;special ed room with printer in back
printer1=\\ajh2\Rm 253 HP 4050 (Mono)

[AJ316-1]
;guidance (has local)
building_printers=true

[AJ359-2]
;second computer in speech room
printer1=\\ajh2\Rm 342 English Office

[AJATTENDANCE]
printer1=\\asd11\AJ Records Dell 2330dn

[AJC59-1]
;art room (has local printer)
building_printers=true

[AJC100-1]
printer1=\\ajh2\Rm 116 Office

[AJC100-6]
printer1=\\ajh2\Rm 116 Office

[AJC300-1]
printer1=\\ajh2\Rm 215 Office

[AJRECEP]
printer1=\\asd11\AJ Reception

[AJRECORDS]
printer1=\\asd11\AJ Records Dell 2330dn

[AJS3-1]
;ISS - has local
building_printers=true

[AJS102B-1]
printer1=\\AJH2\Rm S102B Science Office


LonkeroAdministrator
(KiX Master Guru)
2013-04-20 12:38 AM
Re: Just Sharing - Adding Printers based on computer name via INI

so, you are not actually giving them the closest printers, but the printers from their "home" building?

syntax53
(Fresh Scripter)
2013-04-20 03:24 AM
Re: Just Sharing - Adding Printers based on computer name via INI

Giving them the printer that is closest to the computer they are logging on to. As in "\\ash2\Rm 220B English Office" is the closest network printer to computer "AS220B-1".

Edited my original post to include 2 UDFs I forgot to include.

edit: The "building printers" section could be throwing you off-- every network printer in the building gets added to every teacher's computer so that they can print anywhere they want. The sections below that basically define the default printer (printer1) and any additional printers that students should get (as building printers aren't added for the students).


LonkeroAdministrator
(KiX Master Guru)
2013-04-20 03:28 AM
Re: Just Sharing - Adding Printers based on computer name via INI

ok... your ini-functions are rather complex for that purpose, given that simple few lines would do the logic in my opinion.

thanks for sharing anyways... there might very well be something of use even for me in the future.


Glenn BarnasAdministrator
(KiX Supporter)
2013-04-20 03:47 AM
Re: Just Sharing - Adding Printers based on computer name via INI

If you're using my IniArray UDF, then the IniSections and IniKeys UDFs are redundant - you already have all of the data in the master array that was loaded. By using these extra UDFs, you're losing efficiency by loading the INI file into the array, then enumerating the INI file to locate data that's in your array.
 Code:
$aData = IniArray('.\file.ini')
; List of sections
For $S = 0 to UBound($aData, 2)
 'Section: ' $aData[0, $S] ?
 ; list of keys in the current section
 $aKeyData = $aData[1, $S]        ; put keys array into local var
 For $K = 0 to UBound($aKeyData, 2]
  'Key / Data: ' $aKeyData[0, $K] ' / ' $aKeyData[1, $K]
 Next
Next
This keeps all references in the array. This UDF might be overkill for a simple INI lookup, though.

This UDF was updated just today, so you should pull the latest copy after 10pm EST tonight when it gets uploaded to our web site. The update improves performance for larger files and resoves a recently discovered bug that will appear when a section is defined in the file without any keys associated with it.

You shouldn't have to do all the "UCase" commands as text comparisons are case insensitive unless you use a SetOption call to enable case sensitivity.

It's generally good practice to leave the headers in the UDFs as they contain important reference material.

You should never declare a GLOBAL var inside a function. Doing so will cause the var to be declared each time the function is called causing a Duplicate Definition error.

I haven't reviewed the code in depth, but first blush it seems more complicated than necessary. If your computer name identifies the school and classroom along with a unique PC identifier, then a single config file could be used with one section per location and one key per classroom. If a classroom has multiple computers, then reverse the logic - Printer\share=room instead of room=Printer\Share. Search the values for DATA matching the room and the KEY will represent the printer share. You can then determine which preter was preferred.

Glenn


syntax53
(Fresh Scripter)
2013-04-20 04:35 AM
Re: Just Sharing - Adding Printers based on computer name via INI

 Originally Posted By: Lonkero
ok... your ini-functions are rather complex for that purpose, given that simple few lines would do the logic in my opinion.

There are over 400 computers in each school that the ini files takes are of, without having to specify 400 sections for 400 machines in each ini. The goal was to have as few sections as possible in the INI. The part of the ini I pasted was only a sample. Here are the two main INIs in their entirety, for the junior and senior high--

junior high...
 Code:
; ========================================================
;                       FORMAT OF FILE
; ========================================================
;
; [computername-1]
; printer1=\\server\printer name     ;printer1 = Default Printer.  If you don't want to set (or change) their default printer then start with printer2.
; printer2=\\server\printer name     ;OPTIONAL: Extra network printers to add for all users (students included).  Add more with printer3, printer4, etc.
; building_printers=false            ;OPTIONAL: With this flag it will not add any printers from the [BUILDING_PRINTERS] section (applies to non-students only)
;
; [computername-1\username]          ;User Name Override -- This user will only read this section and not the regular section for the computer
; printer1=\\server\printer name
;
; [computername-*]                   ;Printer Settings apply to all machines with the prefix (* must be preceeded by the first dash)
; printer1=\\server\mono printer 1   ;NOTE: You can override this for a single machine by specifying the actual computer name in another section.
; printer2=\\server\mono printer 2
; printer3=\\server\color printer
;
; [ABC100-1 through ABC120-1]        ;Range such as [AJ100-1 through AJ120-1] or [AJLIB-1 through AJLIB-15] or [AJP14-* through AJP18-*].
;
; The script checks for priority in the most specific to least:
; [computername\username] > [computername-*\username] > [computername] > [computername-*] > [ABC100-1 through ABC120-1]
;
; ========================================================

[BUILDING_PRINTERS]
;Printers that get added to all NON-Students unless building_printers=false is specified.
printer1=\\AJH2\Library HPM551 (Color)
printer2=\\AJH2\Library HPP3010#1 (Mono)
printer3=\\AJH2\Library HPP3010#2 (Mono)
printer4=\\AJH2\RICOH Copier Guidance
printer5=\\AJH2\RICOH Copier Library
printer6=\\AJH2\RICOH Copier Mail Rm
printer7=\\AJH2\RICOH Special Ed
printer8=\\AJH2\Rm 116 Office
printer9=\\AJH2\Rm 136 Learning Center
printer10=\\AJH2\Rm 215 Office
printer11=\\AJH2\Rm 224 DE5100 (Color)
printer12=\\AJH2\Rm 224 DE5300 (Mono)
printer13=\\AJH2\Rm 230 DE5130cdn (Color)
printer14=\\AJH2\Rm 230 DE5350dn (Mono)
printer15=\\AJH2\Rm 242 Soc Studies Office
printer16=\\AJH2\Rm 253 HP 4050 (Mono)
printer17=\\AJH2\Rm 310 Office
printer18=\\AJH2\Rm 324 DE5100 (Color)
printer19=\\AJH2\Rm 324 DE5200 (Mono)
printer20=\\AJH2\Rm 342 English Office
printer21=\\AJH2\Rm C58 Classroom
printer22=\\AJH2\Rm C58 DE3010cn (Color)
printer23=\\AJH2\Rm P18 DE5350dn (Mono)
printer24=\\AJH2\Rm S102B Science Office


;RANGES
;=================

[AJ108-1 through AJ125-1]
printer1=\\ajh2\Rm 116 Office

[AJ136-*]
printer1=\\ajh2\Rm 136 Learning Center

[AJ128-1 through AJ157-1]
printer1=\\ajh2\Rm 136 Learning Center

[AJ208-1 through AJ225-1]
printer1=\\ajh2\Rm 215 Office

[AJ215-*]
printer1=\\ajh2\Rm 215 Office

[AJ234-1 through AJ265-1]
printer1=\\ajh2\Rm 242 Soc Studies Office

[AJ310-*]
printer1=\\ajh2\Rm 310 Office

[AJ308-1 through AJ325-1]
printer1=\\ajh2\Rm 310 Office

[AJ324-*]
building_printers=false
printer1=\\ajh2\Rm 324 DE5200 (Mono)
printer2=\\AJH2\Rm 324 DE5100 (Color)

[AJ330-1 through AJ363-1]
printer1=\\ajh2\Rm 342 English Office

[AJ351-*]
printer1=\\ajh2\Rm 351 DE2330dn (Mono)

[AJC51-1 through AJC60-1]
printer1=\\ajh2\Rm C58 Classroom

[AJC58-*]
printer1=\\ajh2\Rm C58 Classroom
printer2=\\AJH2\Rm C58 DE3010cn (Color)

[AJHEALTH-*]
printer1=\\asd11\JR Health Suite Dell 2330dn

[AJLIB-1 through AJLIB-15]
building_printers=false
printer1=\\AJH2\Library HPP3010#1 (Mono)
printer2=\\AJH2\Library HPP3010#2 (Mono)
printer3=\\AJH2\Library HPM551 (Color)

[AJLIB-16 through AJLIB-29]
building_printers=false
printer1=\\AJH2\Library HPP3010#2 (Mono)
printer2=\\AJH2\Library HPP3010#1 (Mono)
printer3=\\AJH2\Library HPM551 (Color)

[AJLIB-30 through AJLIB-33]
;teacher computers in back
printer1=\\AJH2\Library HPP3010#2 (Mono)

[AJM2-1 through AJM5-1]
printer1=\\ajh2\Rm C58 Classroom

[AJP14-* through AJP18-*]
printer1=\\ajh2\Rm P18 DE5350dn (Mono)

[AJS6-1 through AJS106-1]
printer1=\\ajh2\Rm S102B Science Office


;SPECIFIC MACHINES
;=================

[AJ111-1]
;guidance
building_printers=true

[AJ114-1]
;guidance
building_printers=true

[AJ210-1]
;reading chair
building_printers=true

[AJ216-1]
;guidance
building_printers=true

[AJ253-1]
;special ed room with printer in back
printer1=\\ajh2\Rm 253 HP 4050 (Mono)

[AJ316-1]
;guidance
building_printers=true

[AJ359-2]
;second computer in speech room
printer1=\\ajh2\Rm 342 English Office

[AJATTENDANCE]
printer1=\\asd11\AJ Records Dell 2330dn

[AJC59-1]
;art room (has local printer)
building_printers=true

[AJC100-1]
printer1=\\ajh2\Rm 116 Office

[AJC100-6]
printer1=\\ajh2\Rm 116 Office

[AJGUID-1]
;has local printer
building_printers=false
printer2=\\ajh2\RICOH Copier Guidance
printer3=\\asd11\AJ Guid HP 1300n
printer4=\\asd11\AJ Guid Dell 2330dn
printer5=\\AJH2\RICOH Copier Mail Rm

[AJGUID-2]
building_printers=false
printer1=\\ajh2\RICOH Copier Guidance
printer2=\\asd11\AJ Guid HP 1300n
printer3=\\asd11\AJ Guid Dell 2330dn
printer4=\\AJH2\RICOH Copier Mail Rm

[AJGUID-B]
building_printers=false
printer1=\\asd11\AJ Guid HP 1300n
printer2=\\ajh2\RICOH Copier Guidance
printer3=\\asd11\AJ Guid Dell 2330dn
printer4=\\AJH2\RICOH Copier Mail Rm

[AJGUID-C]
building_printers=false
printer1=\\ajh2\RICOH Copier Guidance
printer2=\\asd11\AJ Guid HP 1300n
printer3=\\asd11\AJ Guid Dell 2330dn
printer4=\\AJH2\RICOH Copier Mail Rm

[AJGUID-F]
building_printers=false
printer1=\\asd11\AJ Guid Dell 2330dn
printer2=\\ajh2\RICOH Copier Guidance
printer3=\\asd11\AJ Guid HP 1300n
printer4=\\AJH2\RICOH Copier Mail Rm

[AJGUID-G]
building_printers=false
printer1=\\asd11\AJ Guid Dell 2330dn
printer2=\\ajh2\RICOH Copier Guidance
printer3=\\asd11\AJ Guid HP 1300n
printer4=\\AJH2\RICOH Copier Mail Rm

[AJGUID-H]
building_printers=false
printer1=\\asd11\AJ Guid Dell 2330dn
printer2=\\ajh2\RICOH Copier Guidance
printer3=\\asd11\AJ Guid HP 1300n
printer4=\\AJH2\RICOH Copier Mail Rm

[AJGUID-J]
building_printers=false
printer1=\\asd11\AJ Guid HP 1300n
printer2=\\ajh2\RICOH Copier Guidance
printer3=\\asd11\AJ Guid Dell 2330dn
printer4=\\AJH2\RICOH Copier Mail Rm

[AJC300-1]
printer1=\\ajh2\Rm 215 Office

[AJLIB-CIRC]
building_printers=false
printer1=\\AJH2\Library HPP3010#1 (Mono)
printer2=\\AJH2\Library HPP3010#2 (Mono)
printer3=\\AJH2\Library HPM551 (Color)
printer4=\\AJH2\Library Lab DE2330

[AJLIB-OFFICE1]
building_printers=false
printer1=\\AJH2\Library HPP3010#1 (Mono)
printer2=\\AJH2\Library HPP3010#2 (Mono)
printer3=\\AJH2\Library HPM551 (Color)
printer4=\\AJH2\Library Lab DE2330

[AJLIB-OFFICE2]
building_printers=false
printer1=\\AJH2\Library HPP3010#1 (Mono)
printer2=\\AJH2\Library HPP3010#2 (Mono)
printer3=\\AJH2\Library HPM551 (Color)
printer4=\\AJH2\Library Lab DE2330

[AJRECEP]
printer1=\\asd11\AJ Reception

[AJRECORDS]
printer1=\\asd11\AJ Records Dell 2330dn

[AJS3-1]
;ISS - has local
building_printers=true


senior high...
 Code:
; ========================================================
;                       FORMAT OF FILE
; ========================================================
;
; [computername-1]
; printer1=\\server\printer name     ;printer1 = Default Printer.  If you don't want to set (or change) their default printer then start with printer2.
; printer2=\\server\printer name     ;OPTIONAL: Extra network printers to add for all users (students included).  Add more with printer3, printer4, etc.
; building_printers=false            ;OPTIONAL: With this flag it will not add any printers from the [BUILDING_PRINTERS] section (applies to non-students only)
;
; [computername-1\username]          ;User Name Override -- This user will only read this section and not the regular section for the computer
; printer1=\\server\printer name
;
; [computername-*]                   ;Printer Settings apply to all machines with the prefix (* must be preceeded by the first dash)
; printer1=\\server\mono printer 1   ;NOTE: You can override this for a single machine by specifying the actual computer name in another section.
; printer2=\\server\mono printer 2
; printer3=\\server\color printer
;
; [ABC100-1 through ABC120-1]        ;Range such as [AJ100-1 through AJ120-1] or [AJLIB-1 through AJLIB-15] or [AJP14-* through AJP18-*].
;
; The script checks for priority in the most specific to least:
; [computername\username] > [computername-*\username] > [computername] > [computername-*] > [ABC100-1 through ABC120-1]
;
; ========================================================

[BUILDING_PRINTERS]
;Printers that get added to all NON-Students unless building_printers=false is specified.
printer1=\\ASH2\Library#1 DE5310 (Mono)
printer2=\\ASH2\Library#2 DE5310 (Mono)
printer3=\\ASH2\Library DE5100 (Color)
printer5=\\ASH2\Ricoh Copier Faculity Rm
printer6=\\ASH2\Ricoh Copier Mail Rm
printer7=\\ASH2\Rm 110 DE5100 (Color)
printer8=\\ASH2\Rm 110 HP4200 (Mono)
printer9=\\ASH2\Rm 121 HP4600 (Color)
printer10=\\ASH2\Rm 129C Science Office
printer11=\\ASH2\Rm 158 Classroom
printer12=\\ASH2\Rm 159 DE3000
printer13=\\ASH2\Rm 209 Math Office
printer14=\\ASH2\Rm 220 DE5100 (Color)
printer15=\\ASH2\Rm 220 DE5300 (Mono)
printer16=\\ASH2\Rm 220B English Office
printer18=\\ASH2\Rm 222#1 DE5300 (Mono)
printer19=\\ASH2\Rm 222#2 DE5300 (Mono)
printer20=\\ASH2\Rm 224 DE5310 (Mono)
printer21=\\ASH2\Rm 235 DE3000
printer22=\\ASH2\Rm 235 Special Ed Office
printer23=\\ASH2\Rm 256 World Lang Office
printer24=\\ASH2\Rm L4 DE5300 (Mono)
printer25=\\ASH2\Rm M106 Music
printer26=\\ASH2\Rm P215 Health
printer27=\\ASH2\Rm S10 Classroom
printer28=\\ASH2\Rm S101A Classroom
printer29=\\ASH2\Rm S106 DE3000


;RANGES
;=================

[AS113-1 through AS118-1]
printer1=\\ash2\Library#1 DE5310 (Mono)

[AS124-1 through AS140-1]
printer1=\\ash2\Rm 129C Science Office

[AS129C-*]
printer1=\\ash2\Rm 129C Science Office

[AS150-1 through AS165-1]
printer1=\\ash2\Rm 158 Classroom

[AS159-*]
printer1=\\ash2\Rm 159 DE3000

[AS201-1 through AS213-1]
printer1=\\ash2\Rm 209 Math Office

[AS219-1 through AS230-1]
printer1=\\ash2\Rm 220B English Office

[AS235-1 through AS239-1]
printer1=\\ash2\Rm 235 Special Ed Office

[AS243-1 through AS256-1]
printer1=\\ash2\Rm 256 World Lang Office

[ASA112-*]
printer1=\\asd11\AS Lower Guid DE5300

[ASA115-*]
printer1=\\asd11\AS Lower Guid Dell 2330dn

[ASL4-*]
building_printers=false
printer1=\\ash2\Rm L4 DE5300 (Mono)
printer2=\\ASH2\Library#1 DE5310 (Mono)
printer3=\\ASH2\Library#2 DE5310 (Mono)
printer4=\\ASH2\Library DE5100 (Color)

[ASLIB-1 through ASLIB-25]
building_printers=false
printer1=\\ash2\Library#1 DE5310 (Mono)
printer2=\\ASH2\Library#2 DE5310 (Mono)
printer3=\\ASH2\Library DE5100 (Color)

[ASLIB-26 through ASLIB-49]
building_printers=false
printer1=\\ASH2\Library#2 DE5310 (Mono)
printer2=\\ash2\Library#1 DE5310 (Mono)
printer3=\\ASH2\Library DE5100 (Color)

[ASM100-1 through ASM106-1]
printer1=\\ash2\Rm M106 Music

[ASP212-1 through ASP217-1]
printer1=\\ash2\Rm P215 Health

[ASP215-*]
printer1=\\ash2\Rm P215 Health

[ASS3-1 through ASS10-1]
printer1=\\ash2\Rm S10 Classroom

[ASS101-1 through ASS113-1]
printer1=\\ash2\Rm S101A Classroom

[ASS101A-*]
printer1=\\ash2\Rm S101A Classroom


;SPECIFIC MACHINES
;=================

[AS120-1]
printer1=\\AS119-1\Dell Open Print Driver (PCL XL)

[AS121-1]
printer1=\\ash2\Rm 121 HP4600 (Color)

[AS209-2]
printer1=\\ash2\Rm 209 Math Office

[AS220B-1]
printer1=\\ash2\Rm 220B English Office

[AS222C-1]
printer1=\\ash2\Rm 222#2 DE5300 (Mono)

[AS233A-1]
printer1=\\ASH2\Rm 235 Special Ed Office

[AS233B-1]
printer1=\\ASH2\Rm 235 Special Ed Office

[ASA103-1]
printer1=\\asd11\AS Upper Guid DE5300

[ASA118-1]
printer1=\\ash2\Rm M106 Music

[ASLIB-CIRC1]
building_printers=false
printer1=\\ash2\Library#1 DE5310 (Mono)
printer2=\\ash2\Rm L4 DE5300 (Mono)
printer3=\\ASH2\Library#2 DE5310 (Mono)
printer4=\\ASH2\Library DE5100 (Color)

[ASLIB-CIRC2]
building_printers=false
printer1=\\ash2\Library#1 DE5310 (Mono)
printer2=\\ash2\Rm L4 DE5300 (Mono)
printer3=\\ASH2\Library#2 DE5310 (Mono)
printer4=\\ASH2\Library DE5100 (Color)

[ASLIB-CIRC3]
building_printers=false
printer1=\\ash2\Library#1 DE5310 (Mono)
printer2=\\ash2\Rm L4 DE5300 (Mono)
printer3=\\ASH2\Library#2 DE5310 (Mono)
printer4=\\ASH2\Library DE5100 (Color)

[ASLIB-OFFICE1]
building_printers=false
printer1=\\ash2\Library#1 DE5310 (Mono)
printer2=\\ash2\Rm L4 DE5300 (Mono)
printer3=\\ASH2\Library#2 DE5310 (Mono)
printer4=\\ASH2\Library DE5100 (Color)

[ASLIB-OFFICE2]
building_printers=false
printer1=\\ash2\Library#1 DE5310 (Mono)
printer2=\\ash2\Rm L4 DE5300 (Mono)
printer3=\\ASH2\Library#2 DE5310 (Mono)
printer4=\\ASH2\Library DE5100 (Color)

[ASRECORDS-1]
printer1=\\asd11\AS Records Hp 4200

[ASRECORDS-2]
printer1=\\asd11\AS Upper Guid DE5300

[ASS4A-1]
printer1=\\ash2\Rm S10 Classroom

[ASS4B-1]
printer1=\\ash2\Rm S10 Classroom

[ASS10C-1]
printer1=\\ash2\Rm S10 Classroom

[ASS10-C]
printer1=\\ash2\Rm S10 Classroom

[ASS101A-1]
building_printers=true

[ASSEC-1]
printer1=\\asd11\Sr Prin Dell 2330dn

[AS107-1]
building_printers=false
printer2=\\ASH2\Ricoh Copier Mail Rm

[ASA109-1]
building_printers=true


syntax53
(Fresh Scripter)
2013-04-20 04:49 AM
Re: Just Sharing - Adding Printers based on computer name via INI

 Originally Posted By: Glenn Barnas
If you're using my IniArray UDF, then the IniSections and IniKeys UDFs are redundant - you already have all of the data in the master array that was loaded. By using these extra UDFs, you're losing efficiency by loading the INI file into the array, then enumerating the INI file to locate data that's in your array.

I'm not sure what you mean here. I'm only reading the file once. Those two functions just take the default delimited string return from readiniarray function and puts it back into an array.


 Quote:
This UDF was updated just today, so you should pull the latest copy after 10pm EST tonight when it gets uploaded to our web site. The update improves performance for larger files and resoves a recently discovered bug that will appear when a section is defined in the file without any keys associated with it.

I put a link in my post the the UDF I'm using. It says it hasn't been updated since 8-2011.


 Quote:
You shouldn't have to do all the "UCase" commands as text comparisons are case insensitive unless you use a SetOption call to enable case sensitivity.

Yea, there are a lot of little things I do that are unnecessary, I know. Sometimes it's just for readability, but often it's because I write code primarily in PHP where it does matter a lot and it's just habit.


 Quote:
It's generally good practice to leave the headers in the UDFs as they contain important reference material.

I left links to all the UDFs in my post. The headers are quite long and I was already making a pretty lengthy post \:\)


 Quote:
You should never declare a GLOBAL var inside a function. Doing so will cause the var to be declared each time the function is called causing a Duplicate Definition error.

Good to know, I'll move it out.


 Quote:
I haven't reviewed the code in depth, but first blush it seems more complicated than necessary. If your computer name identifies the school and classroom along with a unique PC identifier, then a single config file could be used with one section per location and one key per classroom.

See my my reply to Lonkero above. I had only pasted a snipit of my full INI. The goal was to have as few sections in the INI as possible.


Glenn BarnasAdministrator
(KiX Supporter)
2013-04-20 03:49 PM
Re: Just Sharing - Adding Printers based on computer name via INI

OK - I misread the source of those two functions.. they're fine.

Most of my UDF posts reference this and many of the older farts around here know, but my web site always hosts the most current copy of the UDFs we use in production coding. I rewrote the PostPrep utility to run from a command line, and it has the ability to convert an entire folder's worth of UDFs to HTML format. Our web management server runs a task at 10pm nightly that converts all of our UDFs to HTM, so any updates will appear nightly. I do give a heads-up on KORG for some of the more popular UDFs and even update some of them here, but it's a good idea to always check our site for the most current versions.. Click the IT Resources menu, then Kixtart. The UDF Library link is in the middle of that page. There are 141 items on that link, including 4 "libraries" - collections of related UDFs for database access, WSUS management, task scheduler control, an MS Excel manipulation. The version string for IniArray() is
 Code:
;;VERSION        1.0 / 2011-08-02
;;		 1.1 / 2013-04-17 - improve file load on large files, bugfix

My reference to UCase didn't indicate it was wrong or bad form, just that it was unnecessary in Kix.

Before you just move the global declaration out of your UDF, take a look at the tcLib UDF Library, specifically the tcInit() function for an alternate method. This function declares global vars only if they don't already exist, allowing the first call to tcInit to initialize the environment and preload the arrays, following calls only preload the arrays.

I still think your method is complex. Tell me how your computer naming scheme works and I might be able to offer some ways to simplify. Right off the bat, with computer/user definitions, you might be able to do something like
 Code:
; configuration for a specific computer, with user-specific default printers. Unlisted users default to Printer1
[computer 1]
fred=Printer2
mary=Printer3
; printers map for all users, printer1 is set default unless a user-specific override is defined
Printer1=printer UNC
Printer2=printer UNC
Printer3=printer UNC
; disable mapping of all printers - default is to map
Building_Printers=No
to handle both generic and user-specific defaults with one record. Look at the Bool() UDF so your setting values can be Yes/No, True/False, On/Off, or 1/0.

Think about the data before the code.. realize that an INI value can hold many data items:
 Code:
[SECTION]
Value=D1,d2,"last, first",d5
Use the CSV() UDF to read a CSV data set:
 Code:
$aRecordData=CSV(ReadIniArray($Array, $Section, $Value))
This will create an array of the related data from just one read of the INI file or INI_Array. Packing data like this might simplify your code significantly. You might also consider a two-layer approach.. create a section for the school that defines the classrooms, this would point to sections that define the specific mappings. A slight increase in data complexity can result in sigificant reduction in code complexity. This is important with scripts like this that touch your entire user population. Simplicity = reliability.

Tell us more about the layout of the district and we'll be able to offer some specific ideas to make this nearly bullet-proof for you.

Glenn


AllenAdministrator
(KiX Supporter)
2013-04-20 04:19 PM
Re: Just Sharing - Adding Printers based on computer name via INI

I was about to ask how the INISections and INIKeys UDFs were inefficient (certainly they are redundant) but it seems that it got sorted out.

I think it's a matter of scripting preference regarding the UDFs. Personally I don't like dealing with multidimension arrays. Using the functions allows you to "See and Say" what is happening with all those values. Here is Glenn's example using the UDFs, done on the fly, untested.

 Code:
$aData = IniArray('.\file.ini')
For each $S in INISections($aData)
 'Section: ' + $S ?
 For each $K in INIKeys($aData,$S)
  'Key / Data: ' + $K + ' / ' +  ReadINIArray($aData,$S, $K) ?
 Next
Next






ShaneEP
(MM club member)
2013-04-20 05:21 PM
Re: Just Sharing - Adding Printers based on computer name via INI

Wow has it already been almost 2 years since you wrote those INIArray functions?

Glenn BarnasAdministrator
(KiX Supporter)
2013-04-20 06:05 PM
Re: Just Sharing - Adding Printers based on computer name via INI

Allen - yeah, it was a crazy day and I thought I saw ReadProfileString in those functions, which would have performed duplicate INI reads.. my mistake.

Shane - time flies, whether you're having fun or not!

Note the recent code update on this UDF.. I'm getting slightly better performance loading the array with LARGE files and ironically slightly worse performance with tiny (1-2K) files, but we're talking 32-48ms worst on small files, but 100-500+ ms gains on larger files. Also, the worst-case is an estimate because the resolution of Kix @MSECS macro I used for timing is 16ms.

More importantly, the new version corrects a bug that can occur when your INI file contains a section with no keys. Technically, not valid, but possible with hand-written INI files. The old version loaded the section with no keys and threw an error when you asked for a key. The new code ignores empty sections on read.

I just updated the Universal Login Script to use IniArray and shaved 600ms off of a typical config, bringing production execution time for the script to under 2 seconds! Script overhead is just 750ms - the time it takes to load and process an empty config.

Glenn


ChristopheM
(Hey THIS is FUN)
2013-04-21 08:02 PM
Re: Just Sharing - Adding Printers based on computer name via INI

Just a remark about performance with ini file :
personnally, i am still using ReadProfileString but if i know the file to read is on a network share (like netlogon), i copy the file in a temporary local file and all following actions are done on the local file.

Why i do like that ?
the windows api is quite efficient but it is a monolithic operation. The ini file is read in memory, list of section, list of keyword or values are read and then, the file is closed and memory freed.

when this is done on a local file, the first time, the file is loaded in the local disk cache and next actions are done with the copy in the cache. This is very fast.
When the same thing is done on a network file, there is no cache. for each action, the all file is read through the network. Even if the network is very fast, this is slower than a local read.

Suppose you have to read 10 sections with 10 keys in a 10 kb file.
- locally, you read 10kb one time on disk
- from the network, you read 10 x 10 x 10kb (1 Mb). This is much more slower !!!

initially, in my login script, i read network file. it was really slow specially for WAN workstation. When i changed the script to create a temporay local file and, performances increased significantly.


Glenn BarnasAdministrator
(KiX Supporter)
2013-04-21 11:01 PM
Re: Just Sharing - Adding Printers based on computer name via INI

Christophe,

That's part of the point of the IniArray UDF suite. It uses standard File I/O to load the entire INI file into an array once rather than using multiple Read/WriteProfileString functions. The other purpose of this UDF is that it removes the 32/64K size limit of INI files. My earlier reference of a 600ms performance improvement was moving from ReadProfileString on a locally cached file to loading the same local file into IniArray and using ReadIniArray calls instead.

On a WAN, local caching of a 12K file before loading into IniArray will save between 0.4 and 1.3 seconds, depending on WAN speed. Directly reading the INI on the server with ReadProfileString is much worse and local caching has been known to knock off 15-20 seconds! This is why our Universal Login Script has been caching the INI file on local disk since 2006. The IniArray UDFs just take it to the next level, managing all INI functions in RAM.

Glenn


Glenn BarnasAdministrator
(KiX Supporter)
2013-04-21 11:06 PM
Re: Just Sharing - Adding Printers based on computer name via INI

FYI - Allen reported a bug in the IniArray package that occurs when you delete a key two or more times. This is an unusual situation (likely due to some wacky stuff Allen's doing \:D ) That bug's been fixed, but the problem brough to light some other issues with potentially invalid situations (empty section in INI, for example). I've run some significant tests on the current version and should have an update tonight or tomorrow. I'm waiting for Allen to test in his large INI app.

Bottom line, if you use IniArray and the related functions, please be on the lookout for the newest version (1.2) in the next 48 hours or so.

Glenn


AllenAdministrator
(KiX Supporter)
2013-04-22 12:04 AM
Re: Just Sharing - Adding Printers based on computer name via INI

I only found the bug in INIArray because I had a bug in my code. Nothing wacky going on.

Glenn BarnasAdministrator
(KiX Supporter)
2013-04-22 04:41 PM
Re: Just Sharing - Adding Printers based on computer name via INI

Aren't bugs wacky? \:D

ChristopheM
(Hey THIS is FUN)
2013-04-23 12:14 AM
Re: Just Sharing - Adding Printers based on computer name via INI

Glenn,

i agree that it is a very bad idea to use readprofilestring with a network file. This is why I first copy network file to a local temp file and use the local file.

i have made a test of performance with your functions and with ReadProfileString on local files of several size. Here is my code :
 Code:
break on

dim $inifile, $tmpfile, $ini, $arrSections, $arrKeys, $section, $key, $d1, $d2

$arrInifiles =
  @scriptdir+"\test\file000.ini",
  @scriptdir+"\test\file001.ini",
  @scriptdir+"\test\file002.ini",
  @scriptdir+"\test\file003.ini",
  @scriptdir+"\test\file004.ini",
  @scriptdir+"\test\file005.ini",
  @scriptdir+"\test\file006.ini",
  ""

right( "        size",8 )
right( "        time1",8 )
right( "        time2",8 )
"  filename"
?
for each $inifile in $arrInifiles
  if $inifile
    $tmpfile = @scriptdir+"\"+@ScriptName+".tmp"
    COPY $inifile $tmpfile

    $size = GetFileSize($tmpfile)

    ;-- read with ini functions --
    $start = @TICKS
    $ini = IniArray( $tmpfile )
    $arrSections = IniSections($ini)
    for each $section in $arrSections
      $arrKeys = IniKeys( $ini, $section )
      for each $key in $arrKeys
        $value = ReadIniArray( $ini, $section, $key )
      next
    next
    $d1 = @TICKS - $start

    ;-- read with ReadProfileString --
    $start = @TICKS
    $arrSections = ReadProfileString( $tmpfile, "", "" )
    $arrSections = split($arrSections, chr(10) )
    for each $section in $arrSections
      if $section
        $arrKeys = ReadProfileString( $tmpfile, $section, "" )
        $arrKeys = split($arrKeys, chr(10) )
        for each $key in $arrKeys
          if $key
            $value = ReadProfileString( $tmpfile, $section, $key )
          endif
        next
      endif
    next
    $d2 = @TICKS - $start

    right( "        "+$size,8 )
    right( "        "+$d1,8 )
    right( "        "+$d2,8 )
    "  "
    $inifile
    ?

    DEL $tmpfile
  endif
next

;-- insert your ini functions here

I have been very surprised because on my workstation, code with ReadProfileString is always faster (Windows XP SP3 and never constated problem of file size) !!!
 Code:
    size   time1   time2  filename
      91       0       0  .\test\file000.ini
    2019       0       0  .\test\file001.ini
    7553      47      15  .\test\file002.ini
   34527     485      47  .\test\file003.ini
   69058    1078     140  .\test\file004.ini
  103587    1844     219  .\test\file005.ini
  138116    2735     344  .\test\file006.ini

First, i copy the ini file in a temp file. so, ini file is the local cache disk.
I think the difference of speed is due to many calls to functions with array that can be large. As variables are passed by value, each call duplicates the array in memory. With large ini file this can be much slower than call to ReadProfileString.

One of the first steps of my login script is to do a robocopy from \\netlogon to a local temp directory. Next, all scripts are executed from local directory.

We have the same login script for about 6000 users. At the first connection, the netlogon is copied locally. At the following connection, a new robocopy copies only changed files (not often). This is fast and efficient, specially for WAN workstations.

I will make other tests tomorrow at the office on several types of workstations to see if results are reproductible...

Now, it's time to sleep in France ;\)


LonkeroAdministrator
(KiX Master Guru)
2013-04-23 03:16 PM
Re: Just Sharing - Adding Printers based on computer name via INI

the difference most likely comes from the fact that the first read is done from the disk and AV is possibly interfering with it too.
you should do a simple:
 Code:
$trash=open(1,$tmpfile) do $trash=readline() until @error $trash=close(1)


to make sure the file is indeed cached before timing the difference.


ChristopheM
(Hey THIS is FUN)
2013-04-23 07:12 PM
Re: Just Sharing - Adding Printers based on computer name via INI

may be AV is impacting the result but i start by a COPY to tmpfile so this file should already be in the cache and should have been scanned by the AV.

i tried to add your line in my code. the result is
 Code:
    size   time1   time2  filename
      91       0       0  .\test\file000.ini
    2019       0       0  .\test\file001.ini
    7553      47      16  .\test\file002.ini
   34527     484      47  .\test\file003.ini
   69058    1094     141  .\test\file004.ini
  103587    1859     250  .\test\file005.ini
  138116    2796     375  .\test\file006.ini

This is very near from the previous results !!!

if i have free time, i'll try to test a new version of ini functions based on a "ini handle" in spite of array of strings. The idea is that IniArray loads data in a global internal array and returns a handle. Next calls to ReadIniArray, IniSections or IniKeys just used this handle. I hope duplicating of an integer is much more faster than duplicating an array.

Theorically, impact is null on functions but navigation in the inifile with access to the 3D array becomes impossible (this should not be used).
This solution also needs to add an IniClose function to free memory array and handle.


Glenn BarnasAdministrator
(KiX Supporter)
2013-04-23 09:13 PM
Re: Just Sharing - Adding Printers based on computer name via INI

I have a version that does indirect references to global arrays..

I had several people using this version so wanted to address some of the issues they had before updating/releasing the other version. The interface is the same, except you pass the name of the array, not the array itself.

Glenn


ChristopheM
(Hey THIS is FUN)
2013-04-23 10:17 PM
Re: Just Sharing - Adding Printers based on computer name via INI

happy to see that we have similar idea
;\)


Glenn BarnasAdministrator
(KiX Supporter)
2013-04-24 01:11 AM
Re: Just Sharing - Adding Printers based on computer name via INI

\:D I put a lot of effort into the quality and performance of our login script - one of the reasons it's used at so many global companies and educational facilities. In a recent deployment at a financial company, we brought the Citrix logon script time down from nearly 30 seconds to under 4.5, and the less complex desktop login script to under 2 seconds.

While many/most of the UDFs it uses are published on our web site, I don't always put those that provide us with a strong competetive edge into the public domain right away! ;\)

Glenn


LonkeroAdministrator
(KiX Master Guru)
2013-04-24 03:51 AM
Re: Just Sharing - Adding Printers based on computer name via INI

so... what you are saying is, that you knew this one was actually slow and maybe even slower than readprofilestring() but did not say it that way, as it gave you competitive edge?

I do have to say that sounds... wacky...


Glenn BarnasAdministrator
(KiX Supporter)
2013-04-24 06:43 PM
Re: Just Sharing - Adding Printers based on computer name via INI

The purpose of the IniArray UDF was not an alternative to Read/WriteProfileString, but specifically to overcome the size limitations of INI files. I wrote this for Shane some time ago and Allen's been using it as well.

In my testing, the moderately sized INI files used by my login script saw performance gains over repetetive ReadProfileString calls. I developed another version of this that uses Global arrays that is quite a bit faster, but I haven't published it because - while it works for my login script, it isn't very generic. And - since my login script is a closed-source commercial product, of course I don't publish every one of the UDFs it uses!

I am working on a generic version that uses global arrays, but that will alter the syntax of the read/write calls (eliminating the data source name) and will require careful management of the INI array if multiple data sources are needed. The published version does not have the limitation of a single, globally declared data source.

Glenn


Glenn BarnasAdministrator
(KiX Supporter)
2013-04-24 09:18 PM
Re: Just Sharing - Adding Printers based on computer name via INI

OK - I'm starting to wonder about the performance of arrays in Kix...

I modified the IniArray library to use a single, global array. No more passing arrays to/from the functions. While I can determine that this is slightly faster than passing the arrays, it is (surprisingly) nowhere near the direct read performance of ReadProfileString.

Taking your test code and my new function with Global arrays, I get the following results. The one difference in my code is that I separated out the array load time (LTime) from the ReadIniArray enumerations.
 Code:
    size   LTime   time1   time2  filename
    1199      16       0      16  .\test1.ini
    7131      31     141      62  .\test2.ini
   19989      47     687     188  .\test3.ini
   63231     141    4375     890  .\test4.ini
   91229     219   12062    1703  .\test5.ini
This is on an older P4 3.2GHz w/ Win-8. On my Win-7 test system, I get
 Code:
    size   LTime   time1   time2  filename
    1199       0      16       0  .\test1.ini
    7131      16     141      78  .\test2.ini
   19989      16     391     187  .\test3.ini
   63231      78    2484    1032  .\test4.ini
   91229     109    7125    2109  .\test5.ini
All testing was done on local storage - 72K RPM SATA. Interestingly, running over the network showed better performance using IniArray.
Win-8 PC on Network drive (100Mbps)
 Code:
    size   LTime   time1   time2  filename
    1199       0      16      47  .\test1.ini
    7131      31     140     438  .\test2.ini
   19989      62     688    2156  .\test3.ini
   63231     156    4360   16625  .\test4.ini
   91229     250   12109   34672  .\test5.ini
Win-7 Test PC on Network drive(Gig-E)
 Code:
    size   LTime   time1   time2  filename
    1199      16       0      47  .\test1.ini
    7131      15     141     328  .\test2.ini
   19989      47     562    1000  .\test3.ini
   63231     140    2704    4875  .\test4.ini
   91229     203    7047    8985  .\test5.ini
Just thought this was interesting.

I think I'll drop the Global Array model for now since the performance benefit is slight. The IniArray library has benefit for network based I/O and for larger files.

Glenn

Here's the modified test code and global array version of IniArray:
 Code:
Break On

Dim $

$ = SetOption('Explicit', 'On')
$ = SetOption('WrapAtEOL', 'On')
$ = SetOption('NoVarsInStrings', 'On')

If Not Exist('.\Test1.ini')
  'Generating test files' 
  GenIni('.\Test1.ini', 10, 5)
  '!'
  GenIni('.\Test2.ini', 50, 8)
  '!'
  GenIni('.\Test3.ini', 100, 15)
  '!'
  GenIni('.\Test4.ini', 250, 18)
  '!'
  GenIni('.\Test5.ini', 500, 12)
  'Done!' ? ?
EndIf
  
'Press a key to continue ' Get $ ?

dim $arrInifiles, $inifile, $tmpfile, $ini, $arrSections, $arrKeys, $section, $key, $d0, $d1, $d2, $Size, $Start, $Value

$arrInifiles =
  ".\test1.ini",
  ".\test2.ini",
  ".\test3.ini",
  ".\test4.ini",
  ".\test5.ini"

right( "        size",8 )
right( "        LTime",8 )
right( "        time1",8 )
right( "        time2",8 )
"  filename"
?
for each $inifile in $arrInifiles
  if $inifile
    $tmpfile = @scriptdir+"\"+@ScriptName+".tmp"
    COPY $inifile $tmpfile

    $size = GetFileSize($tmpfile)

    ;-- read with ini functions --
    $start = @TICKS
    $ini = IniArray($tmpfile)
    $d0 = @TICKS - $Start
    $start = @TICKS
    $arrSections = Split(ReadIniArray(''), Chr(10))
    for each $section in $arrSections
      $arrKeys = Split(ReadIniArray($section), Chr(10))
      for each $key in $arrKeys
        $value = ReadIniArray($section, $key )
      next
    next
    $d1 = @TICKS - $start

    ;-- read with ReadProfileString --
    $start = @TICKS
    $arrSections = ReadProfileString( $tmpfile, "", "" )
    $arrSections = split($arrSections, chr(10) )
    for each $section in $arrSections
      if $section
        $arrKeys = ReadProfileString( $tmpfile, $section, "" )
        $arrKeys = split($arrKeys, chr(10) )
        for each $key in $arrKeys
          if $key
            $value = ReadProfileString( $tmpfile, $section, $key )
          endif
        next
      endif
    next
    $d2 = @TICKS - $start

    right( "        "+$size,8 )
    right( "        "+$d0,8 )
    right( "        "+$d1,8 )
    right( "        "+$d2,8 )
    "  "
    $inifile
    ?

    DEL $tmpfile
  endif
next


Function GenIni($_File, $_S, $_MKey)

  Dim $_, $_X, $_Y
  Dim $_Section, $_Key, $_Data

  Del $_File

  SRnd(@MSECS)
  For $_X = 1 to $_S				; Sections
    For $_Y = 1 to 1 + Rnd($_MKey)		; Keys
      $_Section = 'SECTION_' + Right('0000' + $_X, 4)
      $_Key = 'Key_' + Right('00' + $_Y, 2)
      $_Data = Left('The quick brown fox jumped over the lazy dog.', Rnd(30))
      $_ = WriteProfileString($_File, $_Section, $_Key, $_Data)
    Next
    If $_X Mod 50 = 0 '.' EndIf
  Next

  Exit 0

EndFunction






;;
;;======================================================================
;;
;;FUNCTION       IniArray()
;;		  ReadIniArray()	- subfunction for reading array
;;		  WriteIniArray()	- subfunction for writing array
;;
;;ACTION         Reads INI file into an array;
;;		 Writes array to INI format file & reloads the array with fresh data
;;		  ReadIniArray() reads a Section:Key:Data set from the array
;;		  WriteIniArray() writes a Section:Key:Data set to the array
;;
;;AUTHOR         Glenn Barnas
;;
;;VERSION        1.0 / 2011-08-02
;;		 1.1 / 2013-04-17 - improve file load on large files
;;		 1.2 / 2013-04-20 - bugfixes:
;;				    Read empty section or file, write empty section, 
;;				WriteIniArray() - fix error if duplicate write of null data
;;				    Added Options to IniArray for Write to add blank
;;				    lines between sections
;;
;;SYNTAX         IniArray(filename, Action [,options])
;;
;;PARAMETERS     filename - REQUIRED - the name of the INI file to read/write
;;
;;		 action	  - OPTIONAL - "R" or "W" for Read or Write - Defaults to Read
;;
;;		 options  - OPTIONAL - bitwise settings to control output formatting
;;		      Value	Mode	Description
;;			1	Write	Adds blank lines between Sections when true
;;			2	Write	Suppress the array reload on write, useful when
;;					no further INI manipulation is planned.
;;
;;REMARKS        Reads an entire INI file into an array for processing. There is no limit
;;		 on the size of the INI file, other than any Kix limits on general file sizes.
;;		 On read, blank lines and comments (lines that begin with ";" or "#") are
;;		 ignored. On write, only sections that contain data/value pairs are written,
;;		 mimicing the action of WriteProfileString(). Similarly, only Keys that
;;		 have non-null values are written.
;;
;;		 The global array called $a_INIDATA_ is used for operations and will be declared
;;		 by the first call to IniArray if it was not previously defined.
;;		 
;;		 The secondary functions ReadIniArray() and WriteIniArray() make using the 
;;		 IniArray() UDF as easy to use as ReadProfileString() and WriteProfileString(),
;;		 simply requiring calls to IniArray to load and then save the INI file. The
;;		 Read and Write sub-functions use the same syntax as the ProfileString
;;		 functions but reference the array instead of the INI file. 
;;
;;		 NOTE: During array manipulation, deleted records are set to null and not reused.
;;			Similarly, when a new key/data pair is added, deleted array items are
;;			not reused. When the array is written to disk, the null records are skipped. 
;;		 	When the file is again read, the array will only contain valid data with no
;;			empty records.
;;			WRITING an array causes a RE-READ operation, cleaning the empty records.
;;
;;		 NOTE: IMPORTANT - When using WriteIniArray() to create a new Ini-array, you MUST
;;		       first declare the array via Dim $a_INIARRAY_[1, 0]. This is not necessary
;;		       if you read an INI file first with IniArray(filename).
;; 
;;RETURNS        IniArray: Returns 1 on success or 0 on failure
;;		 Populates a two-dimensional array of two-dimensional arrays. The first element 
;;		 is the section name, the second element is a two-dimensional array consisting of
;;		 the key/data pairs.
;;
;;		 ReadIniArray: returns the data specified by the section/value pair
;;		 WriteIniArray: Returns 1 on success or 0 on failure
;;		 Updates the global array with the section/value/data provided
;;
;;		 Consider the following simple INI file:
;;		  [COLORS]
;;		  Apple=Red
;;		  Lime=Green
;;		  [TASTES]
;;		  Apple=sweet
;;		  Lime=sour
;;
;;		 The call $Rc = IniArray('inifile.ini') would populate the $a_INIDATA_ array
;;		 that contained the following values:
;;		  $a_INIDATA_[0,0] contains "COLORS"
;;		  $a_INIDATA_[1,0] contains an array of data/value pairs from this section
;;		  - extract with $aData = $a_INIDATA_[1,0]
;;		   $aData[0,0] contains "Apple"; $aData[1,0] contains "Red"
;;		   $aData[0,1] contains "Lime"; $aData[1,1] contains "Green"
;;
;;		  $a_INIDATA_[0,1] contains "TASTES"
;;		  $a_INIDATA_[1,1] contains Array of data/value pairs from this section
;;		  - extract with $aData = $a_INIDATA_[1,1]
;;		   $aData[0,0] contains "Apple"; $aData[1,0] contains "Sweet"
;;		   $aData[0,1] contains "Lime"; $aData[1,1] contains "Sour"
;;
;;		  ; the following would return "Sweet", just like ReadProfileString
;;		  $Taste = ReadIniArray('TASTES', 'Apple')
;;
;;
;;DEPENDENCIES   none
;;
;;TESTED WITH    W2K, WXP, W2K3, W2K8, W7
;;
;;EXAMPLES       INI FILE READ:
;;		   $Rc = IniArray('testfile.ini')
;;		   'File contains ' UBound($a_INIDATA_) + 1 ' sections' ?
;;
;;		 ENUMERATE:
;;		   For $I = 0 to UBound($a_INIDATA_, 2)
;;		     $aData = $a_INIDATA_[1, $I]
;;		  
;;		     'Section: ' $a_INIDATA_[0, $I] ' contains ' UBound($aData, 2) + 1 ' Data/Value elements' ?
;;		     ' Press a key: ' get $ ?
;;		   
;;		     For $J = 0 to UBound($aData, 2)
;;		       $J ': ' $aData[0, $J] ' = ' $aData[1, $J] ?
;;		     Next
;;		   Next
;;
;;		  ELEMENT READ:
;;		   ; Return a specific value
;;		   $Value = ReadIniArray('SectionName', 'keyname')
;;		   ; Return an array of all section names
;;		   $aSections = Split(ReadIniArray(''), Chr(10))
;;		   ; Return an array of Keys in a specific section
;;		   $aKeys = Split(ReadIniArray('SectionName'), Chr(10))
;;
;;		  ELEMENT WRITE/UPDATE:
;;		   ; Write a key/value pair in the named section
;;		   $Rc = WriteIniArray('SectionName', 'keyname', 'Data')
;;
;;		  ELEMENT DELETE:
;;		   ; Remove the named key from the defined section
;;		   $Rc = WriteIniArray('SectionName', 'keyname')
;;
;;		  SECTION DELETE:
;;		   ; Remove all key/value pairs from the section, deleting the section
;;		   $Rc = WriteIniArray('SectionName')
;;
;;		  INI FILE WRITE:
;;		   ; Flush the array to the file and then reload the array
;;		   $Rc = IniArray('testfile.ini', "W")
;
Function IniArray($_fSrcFile, OPTIONAL $_DataWrite, OPTIONAL $_Options)

  Dim $_					; temp var
  Dim $_Fp					; file pointer
  Dim $_C, $_I, $_J				; index pointers
  Dim $_AddLine, $_NoRead			; Option Vars
  Dim $_Sect					; Section Name
  Dim $_aDat					; Data pair array
  Dim $_aData[1, 499]				; Section & Data Arrays

  $_NoRead = 0					; perform read after write

  If $_Options
    If $_Options & 1
      $_AddLine = @CRLF
    EndIf
    If $_Options & 2
      $_NoRead = 1				; write & exit w/o re-reading
    EndIf
  EndIf


  ; Obtain a File Handle for Read or Write operations
  ; ============================================================
  $_Fp = FreeFileHandle				; locate an available file handle
  If Not $_Fp
    Exit 1					; none available - exit
  EndIf

  ; WRITE: verify that we have properly formatted data
  ; ============================================================
  If $_DataWrite = 'W'				; Write operation
    If VarType($a_INIDATA_) < 8192		; Not an array - exit!
      Exit 87
    EndIf

    Del $_fSrcFile				; delete any pre-existing file
    $_ = Open($_Fp, $_fSrcFile, 5)		; open the file for write/create
    If @ERROR
      ReDim $a_INIDATA_[1, 0]			; create empty array
      Exit @ERROR				; exit if error opening
    EndIf

    ; Write the section data. If no data exists in the section, do not write anything!
    For $_I = 0 to UBound($a_INIDATA_, 2)
      $_Sect  = $a_INIDATA_[0, $_I]		; Section name to write
      $_aData = $a_INIDATA_[1, $_I]		; Data array
      If UBound($_aData, 2) > -1		; create Sect and write data if data is present
        $_ = '[' + $_Sect + ']' + @CRLF		; section name
        $_C = 0
        For $_J = 0 to UBound($_aData, 2)	; key/data pairs
          If $_aData[1, $_J]			; only write keys that have non-null data
            $_C = 1
            $_ = $_ + $_aData[0, $_J] + '=' + $_aData[1, $_J] + @CRLF
          EndIf
        Next
        If $_C
          $_ = WriteLine($_Fp, $_ + $_AddLine)	; write the output line
	EndIf
        If @ERROR
	  $_ = Close($_Fp)			; close the file
	  Exit @ERROR
	EndIf		; exit if error writing
      EndIf
    Next

    $_ = Close($_Fp)				; close the file
    ReDim $_aData[1, 0]				; clear array

    If $_NoRead
      exit 0					; exit here without a re-read of the freshly written data
    EndIf

  EndIf

  ; declare the INI array if undefined
  If Not IsDeclared($a_INIDATA_)
    Global $a_INIDATA_[1,0]
  EndIf

  ; READ: Load the ini file into an array
  ; ============================================================
  $_I = -1  $_J = -1				; Initialize index pointers
  
  $_ = Open($_Fp, $_fSrcFile, 2)		; open the file for read
  If @ERROR
    ReDim $a_INIDATA_[1, 0]			; return an empty array
    Exit @ERROR					; exit if error opening
  EndIf

  ReDim $a_INIDATA_[1, 499], $_aData[1, 499]	; Prep Section & Data Arrays for Read

  $_ = Trim(ReadLine($_Fp))			; read INI, removing leading/trailing spaces

  While Not @ERROR				; loop through the file contents
    If Left($_, 1) = '['			; found a section

      If $_I >= 0				; process prior section data, if any
        If $_J >= 0
          ReDim Preserve $_aData[1, $_J]	; trim data array to size
          $a_INIDATA_[1, $_I] = $_aData
          ReDim $_aData[1, 499]			; create the data array for the new section
          $_J = -1
        Else
          $_I = $_I - 1				; ignore empty sections
        EndIf
      EndIf

      $_I = $_I + 1				; increment the section index
      If $_I Mod 499 = 0 And $_I > 0
        ReDim Preserve $a_INIDATA_[1, $_I + 500] ; increase the section input buffer
      EndIf
      $a_INIDATA_[0, $_I] = Split(SubStr($_, 2), ']')[0]
    Else
      If Not InStr(';#', Left($_, 1)) And Len($_) > 2
        $_aDat = Split($_, '=')			; break into array
        $_J = $_J + 1				; increment the data index
        If $_J Mod 499 = 0 And $_J > 0
          ReDim Preserve $_aData[1, $_J + 500]	; increase the key input buffer
        EndIf
        $_aData[0, $_J] = $_aDat[0]		; Store the value
        $_aData[1, $_J] = $_aDat[1]		; Store the data
      EndIf
    EndIf

    $_ = Trim(ReadLine($_Fp))			; remove leading/trailing spaces
  Loop						; done with input data

  $_ = Close($_Fp)				; close the file
  $_ = 2					; prep for Not Found exit

  ; process the last/only section
  If $_I >= 0
    If $_J >= 0
      ReDim Preserve $_aData[1, $_J]		; trim data array to size
      $a_INIDATA_[1, $_I] = $_aData
    Else
      ReDim Preserve $_aData[1, 0]
      $a_INIDATA_[1, $_I] = $_aData
    EndIf
    ReDim Preserve $a_INIDATA_[1, $_I]		; trim section array to size
    $_ = 0
  EndIf

  Exit $_					; exit success

EndFunction


Function ReadIniArray(OPTIONAL $_Section, OPTIONAL $_Key)

  Dim $_I, $_J					; Index pointers
  Dim $_aData, $_aSrc				; Array of Key/Data pairs
  Dim $_Cmd
  Dim $_S
  Dim $_

  ; exit immediately if the data format is invalid
  If VarType($a_INIDATA_) < 8192					; data but not an array - exit!
    Exit 87
  EndIf

  ; Get the array size
  $_S = UBound($a_INIDATA_, 2)
  If $_S <= 0
    Exit 87
  EndIf

  Dim $_aSectIdx[$_S]				; Section Index Array

  ; Create a section index array
  For $_I = 0 to $_S
    $_aSectIdx[$_I] = $a_INIDATA_[0, $_I]
  Next

  ; If the Section is null, return a delimited string of Sections [same as ReadProfileString(file)]
  If Not $_Section
    $ReadIniArray = Join($_aSectIdx, Chr(10))
    Exit 0
  EndIf

  ; Search the index for a section
  $_I = aScan($_aSectIdx, $_Section)
  If $_I < 0 Exit 2 EndIf			; section not found - Exit

  $_aData = $a_INIDATA_[1, $_I]			; Extract the key/value array

  ; Create a Key index for the requested section
  Dim $_aKeyIdx[UBound($_aData, 2)]
  For $_J = 0 to UBound($_aData, 2)
    $_aKeyIdx[$_J] = $_aData[0, $_J] 
  Next

  ; If the Key is null, return a delimited string of Keys [same as ReadProfileString(file, section)]
  If Not $_Key
    $ReadIniArray = Join($_aKeyIdx, Chr(10))
    Exit 0
  EndIf

  ; Search the index for a Key
  $_J = aScan($_aKeyIdx, $_Key)
  If $_J < 0 Exit 2 EndIf			; Key not found

  $ReadIniArray = $_aData[1, $_J]

  Exit 0

EndFunction


Function WriteIniArray($_Section, OPTIONAL $_Key, OPTIONAL $_Data)

  ; exit immediately if the data format is invalid
  If VarType($a_INIDATA_) < 8192		; data but not an array - exit!
    Exit 87
  EndIf

  Dim $_aSectIdx[UBound($a_INIDATA_, 2)]	; Section Index Array
  Dim $_I, $_J					; Index pointers
  Dim $_aData					; Key/Data array


  ; Create a section index array
  For $_I = 0 to UBound($a_INIDATA_, 2)
    $_aSectIdx[$_I] = $a_INIDATA_[0, $_I]
  Next

  ; Search the index for a section
  $_I = aScan($_aSectIdx, $_Section)

  ; If the section does not exist and Keydata is present - add the section and key/value pair (new Sect:Key:Data)
  If $_I < 0					; section not found - Add if Data is present
    If $_Key And $_Data
      $_I = 0					; default to empty array
      If $a_INIDATA_[0, 0]
        $_I = UBound($a_INIDATA_, 2) + 1		; find next section index
      EndIf
      ReDim Preserve $a_INIDATA_[1, $_I]		; Add new section
      ReDim $_aData[1, 0]			; create data array
      $_aData[0,0] = $_Key			; key
      $_aData[1,0] = $_Data			; data
      $a_INIDATA_[0, $_I] = $_Section		; section name
      $a_INIDATA_[1, $_I] = $_aData		; section data
    Else
      Exit 0					; nothing to do
    EndIf
  Else						; the section does exist, locate the Key

    ; If the Key is null, delete the section (delete Section)
    If Not $_Key
      ReDim $_aData[1, 0]			; create empty keys array
      $a_INIDATA_[1, $_I] = $_aData		; write to section
      $a_INIDATA_[0, $_I] = ''			; write null section name
    Else
      $_aData = $a_INIDATA_[1, $_I]		; Extract the key/value array
      ; Create a Key index for the requested section
      Dim $_aKeyIdx[UBound($_aData, 2)]
      For $_J = 0 to UBound($_aData, 2)
        $_aKeyIdx[$_J] = $_aData[0, $_J]	; create the index
      Next
      $_J = aScan($_aKeyIdx, $_Key)		; find the key

      ; If the key does not exist, add the key/data (new Key/data)
      If $_J < 0
        If $_Data
          $_aData = $a_INIDATA_[1, $_I]		; array of key/data pairs
          $_J = UBound($_aData, 2) + 1		; find next key index
          ReDim Preserve $_aData[1, $_J]	; create data array
          $_aData[0, $_J] = $_Key		; key
          $_aData[1, $_J] = $_Data		; data
          $a_INIDATA_[1, $_I] = $_aData		; section data
        Else
          Exit 0				; nothing to do (no data)
        EndIf

      Else					; the key exists - either Update or Delete
    
        ; if the data is not null, write the new data (update key)
        If $_Data
          $_aData[1, $_J] = $_Data

        ; If the data is null, write empty key and data values (delete key)
        Else
          $_aData[0, $_J] = ''			; delete key
          $_aData[1, $_J] = ''			; clear data value
        EndIf
        $a_INIDATA_[1, $_I] = $_aData		; update the array
      EndIf

    EndIf

  EndIf

  Exit 0

EndFunction


LonkeroAdministrator
(KiX Master Guru)
2013-04-25 02:15 AM
Re: Just Sharing - Adding Printers based on computer name via INI

that is indeed weird.
was thinking about checking the readiniarray() syntax for obvious time hogs, but the UDF is not in our library.


LonkeroAdministrator
(KiX Master Guru)
2013-04-25 02:24 AM
Re: Just Sharing - Adding Printers based on computer name via INI

found it.

and now found it in your script too \:D


LonkeroAdministrator
(KiX Master Guru)
2013-04-25 04:28 AM
Re: Just Sharing - Adding Printers based on computer name via INI

hmm... I will try a different design if I don't fall asleep too fast.

LonkeroAdministrator
(KiX Master Guru)
2013-04-25 06:18 AM
Re: Just Sharing - Adding Printers based on computer name via INI

did try couple different ones... it's still just as slow.
I eliminated the multidim array, in hopes of some improvement. did a separate index array for the keys... still not improving. I am starting to think it might not actually have anything to do with arrays but UDF processing itself.

here is my tweaked one...
 Code:
Break On

Dim $

$ = SetOption('Explicit', 'On')
$ = SetOption('WrapAtEOL', 'On')
$ = SetOption('NoVarsInStrings', 'On')

If Not Exist('.\Test1.ini')
  'Generating test files' 
  GenIni('.\Test1.ini', 10, 5)
  '!'
  GenIni('.\Test2.ini', 50, 8)
  '!'
  GenIni('.\Test3.ini', 100, 15)
  '!'
  GenIni('.\Test4.ini', 250, 18)
  '!'
  GenIni('.\Test5.ini', 500, 12)
  'Done!' ? ?
EndIf
  
'Press a key to continue ' Get $ ?

dim $arrInifiles, $inifile, $tmpfile, $ini, $arrSections, $arrKeys, $section, $key, $d0, $d1, $d2, $Size, $Start, $Value

$arrInifiles =
  ".\test1.ini",
  ".\test2.ini",
  ".\test3.ini",
  ".\test4.ini",
  ".\test5.ini"

right( "        size",8 )
right( "        LTime",8 )
right( "        time1",8 )
right( "        time2",8 )
"  filename"
?
for each $inifile in $arrInifiles
  if $inifile
    $tmpfile = @scriptdir+"\"+@ScriptName+".tmp"
    COPY $inifile $tmpfile

    $size = GetFileSize($tmpfile)


    ;-- read with ini functions --
    $start = @TICKS
    $ini = IniArray($tmpfile)
    $d0 = @TICKS - $Start
    $start = @TICKS
    $arrSections = Split(ReadIniArray(''), Chr(10))
    for each $section in $arrSections
      $arrKeys = Split(ReadIniArray($section), Chr(10))
      for each $key in $arrKeys
        $value = ReadIniArray($section, $key )
      next
    next
    $d1 = @TICKS - $start

    ;-- read with ReadProfileString --
    $start = @TICKS
    $arrSections = ReadProfileString( $tmpfile, "", "" )
    $arrSections = split($arrSections, chr(10) )
    for each $section in $arrSections
      if $section
        $arrKeys = ReadProfileString( $tmpfile, $section, "" )
        $arrKeys = split($arrKeys, chr(10) )
        for each $key in $arrKeys
          if $key
            $value = ReadProfileString( $tmpfile, $section, $key )
          endif
        next
      endif
    next
    $d2 = @TICKS - $start

    right( "        "+$size,8 )
    right( "        "+$d0,8 )
    right( "        "+$d1,8 )
    right( "        "+$d2,8 )
    "  "
    $inifile
    ?

    DEL $tmpfile
  endif
next

? "finished" ?

get $

Function GenIni($_File, $_S, $_MKey)

  Dim $_, $_X, $_Y
  Dim $_Section, $_Key, $_Data

  Del $_File

  SRnd(@MSECS)
  For $_X = 1 to $_S				; Sections
    For $_Y = 1 to 1 + Rnd($_MKey)		; Keys
      $_Section = 'SECTION_' + Right('0000' + $_X, 4)
      $_Key = 'Key_' + Right('00' + $_Y, 2)
      $_Data = Left('The quick brown fox jumped over the lazy dog.', Rnd(30))
      $_ = WriteProfileString($_File, $_Section, $_Key, $_Data)
    Next
    If $_X Mod 50 = 0 '.' EndIf
  Next

  Exit 0

EndFunction






;;
;;======================================================================
;;
;;FUNCTION       IniArray()
;;		  ReadIniArray()	- subfunction for reading array
;;		  WriteIniArray()	- subfunction for writing array
;;
;;ACTION         Reads INI file into an array;
;;		 Writes array to INI format file & reloads the array with fresh data
;;		  ReadIniArray() reads a Section:Key:Data set from the array
;;		  WriteIniArray() writes a Section:Key:Data set to the array
;;
;;AUTHOR         Glenn Barnas
;;
;;VERSION        1.0 / 2011-08-02
;;		 1.1 / 2013-04-17 - improve file load on large files
;;		 1.2 / 2013-04-20 - bugfixes:
;;				    Read empty section or file, write empty section, 
;;				WriteIniArray() - fix error if duplicate write of null data
;;				    Added Options to IniArray for Write to add blank
;;				    lines between sections
;;
;;SYNTAX         IniArray(filename, Action [,options])
;;
;;PARAMETERS     filename - REQUIRED - the name of the INI file to read/write
;;
;;		 action	  - OPTIONAL - "R" or "W" for Read or Write - Defaults to Read
;;
;;		 options  - OPTIONAL - bitwise settings to control output formatting
;;		      Value	Mode	Description
;;			1	Write	Adds blank lines between Sections when true
;;			2	Write	Suppress the array reload on write, useful when
;;					no further INI manipulation is planned.
;;
;;REMARKS        Reads an entire INI file into an array for processing. There is no limit
;;		 on the size of the INI file, other than any Kix limits on general file sizes.
;;		 On read, blank lines and comments (lines that begin with ";" or "#") are
;;		 ignored. On write, only sections that contain data/value pairs are written,
;;		 mimicing the action of WriteProfileString(). Similarly, only Keys that
;;		 have non-null values are written.
;;
;;		 The global array called $a_INIDATA_ is used for operations and will be declared
;;		 by the first call to IniArray if it was not previously defined.
;;		 
;;		 The secondary functions ReadIniArray() and WriteIniArray() make using the 
;;		 IniArray() UDF as easy to use as ReadProfileString() and WriteProfileString(),
;;		 simply requiring calls to IniArray to load and then save the INI file. The
;;		 Read and Write sub-functions use the same syntax as the ProfileString
;;		 functions but reference the array instead of the INI file. 
;;
;;		 NOTE: During array manipulation, deleted records are set to null and not reused.
;;			Similarly, when a new key/data pair is added, deleted array items are
;;			not reused. When the array is written to disk, the null records are skipped. 
;;		 	When the file is again read, the array will only contain valid data with no
;;			empty records.
;;			WRITING an array causes a RE-READ operation, cleaning the empty records.
;;
;;		 NOTE: IMPORTANT - When using WriteIniArray() to create a new Ini-array, you MUST
;;		       first declare the array via Dim $a_INIARRAY_[1, 0]. This is not necessary
;;		       if you read an INI file first with IniArray(filename).
;; 
;;RETURNS        IniArray: Returns 1 on success or 0 on failure
;;		 Populates a two-dimensional array of two-dimensional arrays. The first element 
;;		 is the section name, the second element is a two-dimensional array consisting of
;;		 the key/data pairs.
;;
;;		 ReadIniArray: returns the data specified by the section/value pair
;;		 WriteIniArray: Returns 1 on success or 0 on failure
;;		 Updates the global array with the section/value/data provided
;;
;;		 Consider the following simple INI file:
;;		  [COLORS]
;;		  Apple=Red
;;		  Lime=Green
;;		  [TASTES]
;;		  Apple=sweet
;;		  Lime=sour
;;
;;		 The call $Rc = IniArray('inifile.ini') would populate the $a_INIDATA_ array
;;		 that contained the following values:
;;		  $a_INIDATA_[0,0] contains "COLORS"
;;		  $a_INIDATA_[1,0] contains an array of data/value pairs from this section
;;		  - extract with $aData = $a_INIDATA_[1,0]
;;		   $aData[0,0] contains "Apple"; $aData[1,0] contains "Red"
;;		   $aData[0,1] contains "Lime"; $aData[1,1] contains "Green"
;;
;;		  $a_INIDATA_[0,1] contains "TASTES"
;;		  $a_INIDATA_[1,1] contains Array of data/value pairs from this section
;;		  - extract with $aData = $a_INIDATA_[1,1]
;;		   $aData[0,0] contains "Apple"; $aData[1,0] contains "Sweet"
;;		   $aData[0,1] contains "Lime"; $aData[1,1] contains "Sour"
;;
;;		  ; the following would return "Sweet", just like ReadProfileString
;;		  $Taste = ReadIniArray('TASTES', 'Apple')
;;
;;
;;DEPENDENCIES   none
;;
;;TESTED WITH    W2K, WXP, W2K3, W2K8, W7
;;
;;EXAMPLES       INI FILE READ:
;;		   $Rc = IniArray('testfile.ini')
;;		   'File contains ' UBound($a_INIDATA_) + 1 ' sections' ?
;;
;;		 ENUMERATE:
;;		   For $I = 0 to UBound($a_INIDATA_, 2)
;;		     $aData = $a_INIDATA_[1, $I]
;;		  
;;		     'Section: ' $a_INIDATA_[0, $I] ' contains ' UBound($aData, 2) + 1 ' Data/Value elements' ?
;;		     ' Press a key: ' get $ ?
;;		   
;;		     For $J = 0 to UBound($aData, 2)
;;		       $J ': ' $aData[0, $J] ' = ' $aData[1, $J] ?
;;		     Next
;;		   Next
;;
;;		  ELEMENT READ:
;;		   ; Return a specific value
;;		   $Value = ReadIniArray('SectionName', 'keyname')
;;		   ; Return an array of all section names
;;		   $aSections = Split(ReadIniArray(''), Chr(10))
;;		   ; Return an array of Keys in a specific section
;;		   $aKeys = Split(ReadIniArray('SectionName'), Chr(10))
;;
;;		  ELEMENT WRITE/UPDATE:
;;		   ; Write a key/value pair in the named section
;;		   $Rc = WriteIniArray('SectionName', 'keyname', 'Data')
;;
;;		  ELEMENT DELETE:
;;		   ; Remove the named key from the defined section
;;		   $Rc = WriteIniArray('SectionName', 'keyname')
;;
;;		  SECTION DELETE:
;;		   ; Remove all key/value pairs from the section, deleting the section
;;		   $Rc = WriteIniArray('SectionName')
;;
;;		  INI FILE WRITE:
;;		   ; Flush the array to the file and then reload the array
;;		   $Rc = IniArray('testfile.ini', "W")
;
Function IniArray($_fSrcFile, OPTIONAL $_DataWrite, OPTIONAL $_Options)

  Dim $_					; temp var
  Dim $_Fp					; file pointer
  Dim $_C, $_I, $_J				; index pointers
  Dim $_AddLine, $_NoRead			; Option Vars
  Dim $_Sect					; Section Name
  Dim $_aDat					; Data pair array
  Dim $_aData[1, 49]				; Section & Data Arrays

  $_NoRead = 0					; perform read after write

  If $_Options
    If $_Options & 1
      $_AddLine = @CRLF
    EndIf
    If $_Options & 2
      $_NoRead = 1				; write & exit w/o re-reading
    EndIf
  EndIf


  ; Obtain a File Handle for Read or Write operations
  ; ============================================================
  $_Fp = FreeFileHandle				; locate an available file handle
  If Not $_Fp
    Exit 1					; none available - exit
  EndIf

  ; WRITE: verify that we have properly formatted data
  ; ============================================================
  If $_DataWrite = 'W'				; Write operation
    If VarType($a_INIDATA_) < 8192		; Not an array - exit!
      Exit 87
    EndIf

    Del $_fSrcFile				; delete any pre-existing file
    $_ = Open($_Fp, $_fSrcFile, 5)		; open the file for write/create
    If @ERROR
      ReDim $a_INIDATA_[1, 0]			; create empty array
      Exit @ERROR				; exit if error opening
    EndIf

    ; Write the section data. If no data exists in the section, do not write anything!
    For $_I = 0 to UBound($a_INIDATA_, 2)
      $_Sect  = $a_INIDATA_[0, $_I]		; Section name to write
      $_aData = $a_INIDATA_[1, $_I]		; Data array
      If UBound($_aData, 2) > -1		; create Sect and write data if data is present
        $_ = '[' + $_Sect + ']' + @CRLF		; section name
        $_C = 0
        For $_J = 0 to UBound($_aData, 2)	; key/data pairs
          If $_aData[1, $_J]			; only write keys that have non-null data
            $_C = 1
            $_ = $_ + $_aData[0, $_J] + '=' + $_aData[1, $_J] + @CRLF
          EndIf
        Next
        If $_C
          $_ = WriteLine($_Fp, $_ + $_AddLine)	; write the output line
	EndIf
        If @ERROR
	  $_ = Close($_Fp)			; close the file
	  Exit @ERROR
	EndIf		; exit if error writing
      EndIf
    Next

    $_ = Close($_Fp)				; close the file
    ReDim $_aData[1, 0]				; clear array

    If $_NoRead
      exit 0					; exit here without a re-read of the freshly written data
    EndIf

  EndIf

  ; declare the INI array if undefined
  If Not IsDeclared($a_INIDATA_)
    Global $a_INIDATA_[1,0]
  EndIf

  ; READ: Load the ini file into an array
  ; ============================================================
  $_I = -1  $_J = -1				; Initialize index pointers
  
  $_ = Open($_Fp, $_fSrcFile, 2)		; open the file for read
  If @ERROR
    ReDim $a_INIDATA_[0]			; return an empty array
    Exit @ERROR					; exit if error opening
  EndIf

  ReDim $a_INIDATA_[49], $_aKey[49], $_aData[49]	; Prep Section & Data Arrays for Read

  $_ = Trim(ReadLine($_Fp))			; read INI, removing leading/trailing spaces

  While Not @ERROR				; loop through the file contents
    If Left($_, 1) = '['			; found a section

      If $_I >= 0				; process prior section data, if any
        If $_J >= 0
          ReDim Preserve $_aData[$_J]	; trim data array to size
          ReDim Preserve $_aKey[$_J]	; trim data array to size
dim $sps[1]
$sps[0]=$_aKey
$sps[1]=$_aData
          $a_INIDATA_[$_I][1] = $sps
          ReDim $_aKey[49]			; create the data array for the new section
          ReDim $_aData[49]			; create the data array for the new section
          $_J = -1
        Else
          $_I = $_I - 1				; ignore empty sections
        EndIf
      EndIf

      $_I = $_I + 1				; increment the section index
      If $_I Mod 49 = 0 And $_I > 0
        ReDim Preserve $a_INIDATA_[$_I + 50] ; increase the section input buffer
      EndIf
dim $indx[1]
$indx[0] = Split(SubStr($_, 2), ']')[0]
      $a_INIDATA_[$_I] = $indx
    Else
      If Not InStr(';#', Left($_, 1)) And Len($_) > 2
        $_aDat = Split($_, '=')			; break into array
        $_J = $_J + 1				; increment the data index
        If $_J Mod 49 = 0 And $_J > 0
          ReDim Preserve $_aData[$_J + 50]	; increase the key input buffer
        EndIf
        $_aKey[$_J] = $_aDat[0]		; Store the value
        $_aData[$_J] = $_aDat[1]		; Store the value
      EndIf
    EndIf

    $_ = Trim(ReadLine($_Fp))			; remove leading/trailing spaces
  Loop						; done with input data

  $_ = Close($_Fp)				; close the file
  $_ = 2					; prep for Not Found exit

  ; process the last/only section
  If $_I >= 0
    If $_J >= 0
      ReDim Preserve $_aKey[$_J]		; trim data array to size
      ReDim Preserve $_aData[$_J]		; trim data array to size
dim $sps[1]
$sps[0]=$_aKey
$sps[1]=$_aData
      $a_INIDATA_[$_I][1] = $sps
    Else
      ReDim Preserve $_aData[0]
      ReDim Preserve $_aKey[0]		; trim data array to size
dim $sps[1]
$sps[0]=$_aKey
$sps[1]=$_aData
      $a_INIDATA_[$_I][1] = $sps
    EndIf
    ReDim Preserve $a_INIDATA_[$_I]		; trim section array to size
    $_ = 0
  EndIf

  Exit $_					; exit success

EndFunction


Function ReadIniArray(OPTIONAL $_Section, OPTIONAL $_Key)

  Dim $_I, $_J					; Index pointers
  Dim $_aData, $_aSrc				; Array of Key/Data pairs
  Dim $_Cmd
  Dim $_S
  Dim $_

  ; exit immediately if the data format is invalid
  If VarType($a_INIDATA_) < 8192					; data but not an array - exit!
    Exit 87
  EndIf

  ; Get the array size
  $_S = UBound($a_INIDATA_)
  If $_S <= 0
    Exit 87
  EndIf

  Dim $_aSectIdx[$_S]			; Section Index Array

  ; Create a section index array
  For $_I = 0 to $_S
    $_aSectIdx[$_I] = $a_INIDATA_[$_I][0]
  Next

  ; If the Section is null, return a delimited string of Sections [same as ReadProfileString(file)]
  If Not $_Section
    $ReadIniArray = Join($_aSectIdx, Chr(10))
    Exit 0
  EndIf

  ; Search the index for a section
  $_I = aScan($_aSectIdx, $_Section)
  If $_I < 0 Exit 2 EndIf			; section not found - Exit

  $_aData = $a_INIDATA_[$_I][1]			; Extract the key/value array

  ; Create a Key index for the requested section
   If Not $_Key
    $readiniarray=join($_aData[0],chr(10))
    exit 0
   endif
   if 0>ascan($_aData[0],$_Key)
    exit 2
   endif
   $ReadIniArray = $_aData[1][ascan($_aData[0],$_Key)]


EndFunction


Function WriteIniArray($_Section, OPTIONAL $_Key, OPTIONAL $_Data)

  ; exit immediately if the data format is invalid
  If VarType($a_INIDATA_) < 8192		; data but not an array - exit!
    Exit 87
  EndIf

  Dim $_aSectIdx[UBound($a_INIDATA_, 2)]	; Section Index Array
  Dim $_I, $_J					; Index pointers
  Dim $_aData					; Key/Data array


  ; Create a section index array
  For $_I = 0 to UBound($a_INIDATA_, 2)
    $_aSectIdx[$_I] = $a_INIDATA_[0, $_I]
  Next

  ; Search the index for a section
  $_I = aScan($_aSectIdx, $_Section)

  ; If the section does not exist and Keydata is present - add the section and key/value pair (new Sect:Key:Data)
  If $_I < 0					; section not found - Add if Data is present
    If $_Key And $_Data
      $_I = 0					; default to empty array
      If $a_INIDATA_[0, 0]
        $_I = UBound($a_INIDATA_, 2) + 1		; find next section index
      EndIf
      ReDim Preserve $a_INIDATA_[1, $_I]		; Add new section
      ReDim $_aData[1]			; create data array
      $_aData[0] = $_Key			; key
      $_aData[1] = $_Data			; data
      $a_INIDATA_[0, $_I] = $_Section		; section name
      $a_INIDATA_[1, $_I] = $_aData		; section data
    Else
      Exit 0					; nothing to do
    EndIf
  Else						; the section does exist, locate the Key

    ; If the Key is null, delete the section (delete Section)
    If Not $_Key
      ReDim $_aData[1]			; create empty keys array
      $a_INIDATA_[1, $_I] = $_aData		; write to section
      $a_INIDATA_[0, $_I] = ''			; write null section name
    Else
      $_aData = $a_INIDATA_[1, $_I]		; Extract the key/value array
      ; Create a Key index for the requested section
      Dim $_aKeyIdx[UBound($_aData)]
      For $_J = 0 to UBound($_aData)
        $_aKeyIdx[$_J] = $_aData[$_J][0]	; create the index
      Next
      $_J = aScan($_aKeyIdx, $_Key)		; find the key

      ; If the key does not exist, add the key/data (new Key/data)
      If $_J < 0
        If $_Data
          $_aData = $a_INIDATA_[1, $_I]		; array of key/data pairs
          $_J = UBound($_aData) + 1		; find next key index
          ReDim Preserve $_aData[$_J]	; create data array
dim $dats[1]
$dats[0] = $_Key		; key
          $dats[1] = $_Data		; data
          $_aData[$_J] = $dats
          $a_INIDATA_[1, $_I] = $_aData		; section data
        Else
          Exit 0				; nothing to do (no data)
        EndIf

      Else					; the key exists - either Update or Delete
    
        ; if the data is not null, write the new data (update key)
        If $_Data
          $_aData[$_J][1] = $_Data

        ; If the data is null, write empty key and data values (delete key)
        Else
dim $dats[1]
          $_aData[$_J] = $dats			; delete key
        EndIf
        $a_INIDATA_[1, $_I] = $_aData		; update the array
      EndIf

    EndIf

  EndIf

  Exit 0

EndFunction


LonkeroAdministrator
(KiX Master Guru)
2013-04-25 06:26 AM
Re: Just Sharing - Adding Printers based on computer name via INI

modified my readiniarray to just be a stub:
 Code:

Function ReadIniArray($file, OPTIONAL $_Section, OPTIONAL $_Key)
 $ReadIniArray = readprofilestring($file,$_Section,$_Key)
EndFunction


and still the processing time hikes in the last one:
 Code:
   91539     312    5523    2074  .\test5.ini



ChristopheM
(Hey THIS is FUN)
2013-04-25 11:22 AM
Re: Just Sharing - Adding Printers based on computer name via INI

Hello,

done a new version of functions with a handle instead of an array (and prefix all functions with CM). the script tests files with 3 methods :
- time1 uses ReadProfileString
- time2 uses my own functions CMIniArray, CMReadIniArray, etc...
- time3 uses IniArray, ReadIniArray, etc...

here is the complete code for test (just create a subdir called "test" with inifiles or change the content of $arrInifiles) :
 Code:
break on

$=setoption( "explicit", "on" )

dim $arrInifiles, $inifile, $tmpfile, $size, $ini, $start, $d1, $d2, $d3
dim $arrSections, $arrKeys, $section, $key, $value

$arrInifiles =
    @scriptdir+"\test\file010.ini",
    @scriptdir+"\test\file020.ini",
    @scriptdir+"\test\file030.ini",
    @scriptdir+"\test\file040.ini",
    @scriptdir+"\test\file050.ini",
    @scriptdir+"\test\file060.ini",
    @scriptdir+"\test\file070.ini",
    @scriptdir+"\test\file080.ini",
    @scriptdir+"\test\file090.ini",
    @scriptdir+"\test\file100.ini",
    ""

right( "        size", 8 )
right( "        time1",8 )
right( "        time2",8 )
right( "        time3",8 )
"  filename"
?
for each $inifile in $arrInifiles
    if $inifile
        $tmpfile = @scriptdir+"\"+@ScriptName+".tmp"

        $size = GetFileSize($inifile)

        ;-- read with ReadProfileString --
        $start = @TICKS
        COPY $inifile $tmpfile
        $arrSections = ReadProfileString( $tmpfile, "", "" )
        $arrSections = split($arrSections, chr(10) )
        for each $section in $arrSections
            if $section
                $arrKeys = ReadProfileString( $tmpfile, $section, "" )
                $arrKeys = split($arrKeys, chr(10) )
                for each $key in $arrKeys
                    if $key
                        $value = ReadProfileString( $tmpfile, $section, $key )
                    endif
                next
            endif
        next
        $d1 = @TICKS - $start

        ;-- read with ini functions (version Christophe) --
        $start = @TICKS
        $ini = CMIniArray( $inifile )
        if not @error
            $arrSections = CMINISections($ini)
            for each $section in $arrSections
                $arrKeys = CMINIKeys( $ini, $section )
                for each $key in $arrKeys
                    $value = CMReadIniArray( $ini, $section, $key )
                next
            next
        endif
        $ = CMCloseIniArrayHandle( $ini )
        $d2 = @TICKS - $start

        $ = CMCloseIniArrayHandle( $ini )

        ;-- read with ini functions (version Glenn) --
        $start = @TICKS
        $ini = IniArray( $inifile )
        if not @error
            $arrSections = INISections($ini)
            for each $section in $arrSections
                $arrKeys = INIKeys( $ini, $section )
                for each $key in $arrKeys
                    $value = ReadIniArray( $ini, $section, $key )
                next
            next
        endif
        $d3 = @TICKS - $start

        right( "        "+$size,8 )
        right( "        "+$d1,  8 )
        right( "        "+$d2,  8 )
        right( "        "+$d3,  8 )
        "  "
        $inifile
        ?

        DEL $tmpfile
    endif
next

;-------------------------------------------------------------------------------
; set of function to read in a ini file (version Christophe)
;-------------------------------------------------------------------------------
; internal function
; returned handle is the index of the entry in a global array
; each item in the array is an array of 4 values :
;   entry 0 is a boolean (handle used or not)
;   entry 1 is the name of the ini file
;   entry 2 is the number of sections found in the ini file
;   entry 3 is an array of sections structures
;
; each section structure is an array of 3 values :
;   entry 0 is the name of the section
;   entry 1 is the number of keys in the section
;   entry 2 is an array of keys structures
;
; each key structure is an array of 2 values :
;   entry 0 is the name of the key
;   entry 1 is the value
;-------------------------------------------------------------------------------
function CMGetIniArrayHandle( $inifilename )
    if not IsDeclared( $_IniArrayGlobalHandle )
        ;-- initialize global internal variables --
        global $_IniArrayGlobalHandleNb            $_IniArrayGlobalHandleNb = -1
        global $_IniArrayGlobalHandleInc        $_IniArrayGlobalHandleInc = 32
        global $_IniArrayGlobalHandle[$_IniArrayGlobalHandleInc]
    endif

    dim $i

    for $i = 0 to $_IniArrayGlobalHandleNb
        if ($_IniArrayGlobalHandle[$i][0]=1) and ($_IniArrayGlobalHandle[$i][1]=$inifilename)
            ;-- inifile already loaded : reuse the same handle --
            $CMGetIniArrayHandle = $i
            exit 0
        endif

        if ($_IniArrayGlobalHandle[$i][0]=0)
            ;-- use a free existing handle --
            $_IniArrayGlobalHandle[$i] = 1, $inifilename, 0, ""
            $CMGetIniArrayHandle = $i
            exit 0
        endif
    next

    ;-- use a new handle --
    $_IniArrayGlobalHandleNb = $_IniArrayGlobalHandleNb + 1
    if $_IniArrayGlobalHandleNb > UBound($_IniArrayGlobalHandle)
        redim preserve $_IniArrayGlobalHandle[ UBound($_IniArrayGlobalHandle)+$_IniArrayGlobalHandleInc ]
    endif
    $_IniArrayGlobalHandle[$_IniArrayGlobalHandleNb] = 1, $inifilename, 0, ""

    $CMGetIniArrayHandle = $_IniArrayGlobalHandleNb
endfunction


;-------------------------------------------------------------------------------
; external function
;-------------------------------------------------------------------------------
function CMCloseIniArrayHandle( $inihandle )
    if $inihandle < 0                           exit    endif
    if $inihandle > $_IniArrayGlobalHandleNb    exit    endif

    if $_IniArrayGlobalHandle[$inihandle][0]=1
        if $_IniArrayGlobalHandle[$inihandle][2]>0
            dim $i
            for $i = 0 to UBound( $_IniArrayGlobalHandle[$inihandle][3] )
                $_IniArrayGlobalHandle[$inihandle][3][$i][1] = 0
                $_IniArrayGlobalHandle[$inihandle][3][$i][2] = ""
            next
        endif
        $_IniArrayGlobalHandle[$inihandle] = 0, "", 0, ""
    endif
endfunction


function CMIniArray( $inifilename )
    dim $fHandle                                         ; file pointer

    $CMIniArray = -1
    $fHandle = FreeFileHandle()                          ; locate an available file handle
    if not $fHandle
        exit 1                                           ; none available - exit
    endif

    ;-- try to open file for read into a memory structure --
    if Open($fHandle, $inifilename, 2)<>0                ; open the file for read
        exit 2                                           ; error opening - exit
    endif

    dim $inihandle
    $inihandle = CMGetIniArrayHandle( $inifilename )

    dim $inc        $inc = 128
    dim $arrSections[$inc], $nbSection, $section
    dim $arrKeys[$inc], $nbKey
    dim $_, $ch, $i

    $nbSection    = -1
    do                                                   ; loop through the file contents
        $_ = Trim(ReadLine($fHandle))                    ; remove leading/trailing spaces
        if $_
            $ch = Left($_, 1)
            select
                case $ch="["
                    $ch = Right($_, 1)
                    if $ch="]"                           ; found a section
                        If $nbSection > -1               ; process prior section data, if any
                            gosub _CMIniArray_SavePreviousSection
                        endif

                        $nbKey = -1
                        $nbSection = $nbSection + 1      ; increment the section index
                        if $nbSection > UBound($arrSections)
                            redim Preserve $arrSections[ UBound($arrSections)+$inc ]
                        endif

                        $section = SubStr($_, 2, len($_)-2 )
                    endif

                case $ch=";"                             ; comment line
                case $ch="#"                             ; comment line

                case Len($_) > 2
                    if $nbSection > -1
                        $nbKey = $nbKey + 1
                        if $nbKey > UBound($arrKeys)
                            redim preserve $arrKeys[ UBound($arrKeys)+$inc ]
                        endif

                        $i = instr( $_, "=" )
                        if $i
                            $arrKeys[ $nbKey ] = substr( $_, 1, $i-1), substr( $_, $i+1 )
                        else
                            $arrKeys[ $nbKey ] = $_, ""
                        endif
                    endif

                case 1
            endselect
        endif
    Until @ERROR                                         ; done with input data
    $_ = Close($fHandle)                                 ; close the file

    ;-- process the last/only section --
    If $nbSection > -1
        gosub _CMIniArray_SavePreviousSection

        redim preserve $arrSections[$nbSection]
        $_IniArrayGlobalHandle[$inihandle][2] = $nbsection + 1
        $_IniArrayGlobalHandle[$inihandle][3] = $arrSections
    else
        $_IniArrayGlobalHandle[$inihandle][2] = 0
        $_IniArrayGlobalHandle[$inihandle][3] = ""
    endif

    $CMIniArray = $inihandle                             ; return the array

    Exit 0                                               ; exit success

:_CMIniArray_SavePreviousSection
    if $nbKey=-1
        $arrSections[$nbSection] = $section, 0, ""
    else
        redim preserve $arrKeys[ $nbKey ]
        $arrSections[$nbSection] = $section, ($nbKey+1), $arrKeys
    endif

    return
endfunction


function CMReadIniArray( $inihandle, OPTIONAL $Section, OPTIONAL $Key )
    ;-- in the code --
    ;   $_IniArrayGlobalHandle[$inihandle][2]                     number of sections
    ;   $_IniArrayGlobalHandle[$inihandle][3]                     array of sections
    ;   $_IniArrayGlobalHandle[$inihandle][3][$sectionindex][1]   number of keys in a section
    ;   $_IniArrayGlobalHandle[$inihandle][3][$sectionindex][2]   array of (key, value) in a section
    ;
    ; If the Section is null, return a delimited string of Sections [same as ReadProfileString(file)]
    $CMReadIniArray = ""
    If Not $Section
        if $_IniArrayGlobalHandle[$inihandle][2] > 0
            ;-- enum section name in the inifile --
            for each $section in $_IniArrayGlobalHandle[$inihandle][3]
                $CMReadIniArray = $CMReadIniArray + $section[0] + chr(10)
            next
        endif
        Exit 0
    endif

    ; Search the index for a section
    dim $sectionindex, $i, $item
    $sectionindex = -1
    if $_IniArrayGlobalHandle[$inihandle][2] > 0
        for $i = 0 to UBound($_IniArrayGlobalHandle[$inihandle][3])
            if $_IniArrayGlobalHandle[$inihandle][3][$i][0]=$section
                $sectionindex = $i
            endif
        next
    endif
    If $sectionindex < 0 Exit 2 endif                    ; section not found - Exit

    If Not $Key
        ;-- enum key name in the section --
        if $_IniArrayGlobalHandle[$inihandle][3][$sectionindex][1]<>0
            for each $item in $_IniArrayGlobalHandle[$inihandle][3][$sectionindex][2]
                $CMReadIniArray = $CMReadIniArray + $item[0] + chr(10)
            next
        endif

        Exit 0
    endif

    for each $item in $_IniArrayGlobalHandle[$inihandle][3][$sectionindex][2]
        if $item[0]=$key
            $CMReadIniArray = $item[1]
            exit 0                                       ; key found
        endif
    next

    exit 2                                               ; key not found
endfunction


function CMINISections( $inihandle )
    $CMINISections = CMReadIniArray( $inihandle )
    if Len($CMINISections) > 0
        $CMINISections=split($CMINISections,chr(10))
    endif
endfunction


function CMINIKeys( $inihandle, $section )
    $CMINIKeys = CMReadIniArray( $inihandle, $section )
    if Len($CMINIKeys) > 0
        $CMINIKeys=split($CMINIKeys,chr(10))
    endif
endfunction

;-------------------------------------------------------------------------------
; set of function to read in a ini file (version Glenn)
;-------------------------------------------------------------------------------
;FROM http://www.kixtart.org/forums/ubbthreads.php?ubb=showflat&Number=202790#Post202790
Function IniArray($_fSrcFile, OPTIONAL $_aDataWrite)
    Dim $_                                    ; temp var
    Dim $_Fp                                ; file pointer
    Dim $_I, $_J                            ; index pointers
    Dim $_Sect                                ; Section Name
    Dim $_aDat                                ; Data pair
    Dim $_aSect[1,0], $_aData[1, 0]            ; Section & Data Arrays


    ; Obtain a File Handle for Read or Write operations
    ; ============================================================
    $_Fp = FreeFileHandle                                ; locate an available file handle
    If Not $_Fp
        Exit 1                                           ; none available - exit
    EndIf


    ; WRITE: verify that we have properly formatted data
    ; ============================================================
    If VarType($_aDataWrite)  > 0                        ; Write the array to the INI file and exit
        If VarType($_aDataWrite) < 8192                  ; data but not an array - exit!
            Exit 87
        EndIf

        Del $_fSrcFile                                   ; delete any pre-existing file
        $_ = Open($_Fp, $_fSrcFile, 5)                   ; open the file for write/create
        If @ERROR Exit @ERROR EndIf                      ; exit if error opening

        ; Write the section data. If no data exists in the section, do not write anything!
        For $_I = 0 to UBound($_aDataWrite, 2)
            $_Sect  = $_aDataWrite[0, $_I]               ; Section name to write
            $_aData = $_aDataWrite[1, $_I]               ; Data array
            If UBound($_aData, 2) > -1                   ; create Sect and write data if data is present
                $_ = '[' + $_Sect + ']' + @CRLF          ; section name
                For $_J = 0 to UBound($_aData, 2)        ; key/data pairs
                    If $_aData[1, $_J]                   ; only write keys that have non-null data
                        $_ = $_ + $_aData[0, $_J] + '=' + $_aData[1, $_J] + @CRLF
                    EndIf
                Next
                $_ = WriteLine($_Fp, $_)                 ; write the output line
                If @ERROR Exit @ERROR EndIf              ; exit if error writing
            EndIf
        Next

        $_ = Close($_Fp)                                 ; close the file
        ReDim $_aData[1, 0]                              ; clear array

        ; exit 0 ; do not exit here to force a re-read of the freshly written data
    EndIf


    ; READ: Load the ini file into an array
    ; ============================================================
    $_I = -1  $_J = -1                                   ; Initialize index pointers

    $_ = Open($_Fp, $_fSrcFile, 2)                       ; open the file for read
    If @ERROR Exit @ERROR EndIf                          ; exit if error opening

    Do                        ; loop through the file contents
        $_ = Trim(ReadLine($_Fp))                        ; remove leading/trailing spaces

        If Left($_, 1) = '['                             ; found a section

            If $_I >= 0                                  ; process prior section data, if any
                $_aSect[1, $_I] = $_aData
                ReDim $_aData[1, 0]
                $_J = -1
            EndIf

            $_I = $_I + 1                                ; increment the section index
            ReDim Preserve $_aSect[1, $_I]
            $_aSect[0, $_I] = Split(SubStr($_, 2), ']')[0]
        Else
            If Not Left($_, 1) = ';' And Not Left($_, 1) = '#' And Len($_) > 2
                $_aDat = Split($_, '=')                  ; break into array
                $_J = $_J + 1                            ; increment the data index
                ReDim Preserve $_aData[1, $_J]
                $_aData[0, $_J] = $_aDat[0]              ; Store the data
                $_aData[1, $_J] = $_aDat[1]              ; Store the data
            EndIf
        EndIf
    Until @ERROR                                         ; done with input data
    $_ = Close($_Fp)                                     ; close the file

    ; process the last/only section
    If $_I >= 0
        $_aSect[1, $_I] = $_aData
    EndIf

    $IniArray = $_aSect                                  ; return the array

    Exit 0                                               ; exit success
EndFunction


Function ReadIniArray($_aIniFile, OPTIONAL $_Section, OPTIONAL $_Key)
    ; exit immediately if the data format is invalid
    If VarType($_aIniFile) < 8192                        ; data but not an array - exit!
        Exit 87
    EndIf

    Dim $_aSectIdx[UBound($_aIniFile, 2)]                ; Section Index Array
    Dim $_I, $_J                                         ; Index pointers
    Dim $_aData                                          ; Array of Key/Data pairs

    ; Create a section index array
    For $_I = 0 to UBound($_aIniFile, 2)
        $_aSectIdx[$_I] = $_aIniFile[0, $_I]
    Next

    ; If the Section is null, return a delimited string of Sections [same as ReadProfileString(file)]
    If Not $_Section
        $ReadIniArray = Join($_aSectIdx, Chr(10))
        Exit 0
    EndIf

    ; Search the index for a section
    $_I = aScan($_aSectIdx, $_Section)
    If $_I < 0 Exit 2 EndIf                              ; section not found - Exit

    $_aData = $_aIniFile[1, $_I]                         ; Extract the key/value array

    ; Create a Key index for the requested section
    Dim $_aKeyIdx[UBound($_aData, 2)]
    For $_J = 0 to UBound($_aData, 2)
        $_aKeyIdx[$_J] = $_aData[0, $_J]
    Next

    ; If the Key is null, return a delimited string of Keys [same as ReadProfileString(file, section)]
    If Not $_Key
        $ReadIniArray = Join($_aKeyIdx, Chr(10))
        Exit 0
    EndIf

    ; Search the index for a Key
    $_J = aScan($_aKeyIdx, $_Key)
    If $_J < 0 Exit 2 EndIf                              ; Key not found

    $ReadIniArray = $_aData[1, $_J]

    Exit 0
EndFunction



;Modified FROM http://www.kixtart.org/forums/ubbthreads.php?ubb=showflat&Number=203126#Post203126
function INISections($array)
    $INISections = readiniarray($array)
    if LEN($INISections) > 0
        $INISections=split($INISections,chr(10))
    endif
endfunction

function INIKeys($array,$section)
    $INIKeys = readiniarray($array,$section)
    if LEN($INIKeys) > 0
        $INIKeys=split($INIKeys,chr(10))
    endif
endfunction
.
On a Intel E5200 (Dual-Core 2.5Ghz) with 2Gb, the results are :
 Code:
    size   time1   time2   time3  filename
      91      15       0       0  .\test\file010.ini
    2019       0      16       0  .\test\file020.ini
    7553      16      47      46  .\test\file030.ini
   15173      94    1110    2484  .\test\file040.ini
   28149     187    1704    2296  .\test\file050.ini
   34527      47     250     500  .\test\file060.ini
   43179      94    1031    1938  .\test\file070.ini
   69058     125     515    1125  .\test\file080.ini
  103587     235     797    1922  .\test\file090.ini
  138116     375    1062    2969  .\test\file100.ini

On a Intel Pentium 4 (Mono-Core 2.4Ghz) with 1Gb, the results are :
 Code:
    size   time1   time2   time3  filename
      91       0       0       0  .\test\file010.ini
    2019      15      16      16  .\test\file020.ini
    7553      31      94      93  .\test\file030.ini
   15173     203    2563    6094  .\test\file040.ini
   28149     468    3625    6250  .\test\file050.ini
   34527     125     610    1328  .\test\file060.ini
   43179     218    2532    5015  .\test\file070.ini
   69058     360    1281    3281  .\test\file080.ini
  103587     781    1938    5969  .\test\file090.ini
  138116    1328    2562    9641  .\test\file100.ini

ReadProfileString is always the faster with local file !!!

I use a global array to allocate handle (with associated data). redim is automatic if many differents ini files are used.
I have not tested all cases for CMReadIniArray and I suppress the optional parameter for write in CMIniArray.
Code is not very easy to read because of use of array of array of array ... but it seems to be fast.

Not sure i'll use it in my login script because all procedures are already written to copy inifile locally before using ReadProfileString and delete temp file when finished.
But it was a good exercize for fun ;\)


Glenn BarnasAdministrator
(KiX Supporter)
2013-04-25 01:13 PM
Re: Just Sharing - Adding Printers based on computer name via INI

From the testing, there seems to be a threshold where performance drops off dramatically. My login.ini is just under 12K. When I enabled process timestamps, the script completed 600ms faster using IniArray. Most config files are in that 8-12K size, but when I added a few records to the test config, I lost the 600ms and another 2 seconds! I've rolled back to the cached INI file model so performance is consistent.

Thanks for your ideas!

Glenn