The following code works, except for the first time around
-
Friday, March 01, 2013 8:18 PM
I hope this is an easy question, so as to not cause too much of stir.
I need to modify an INI file on remote workstations and I am building this script in sections. First I get one piece to work, then I move on to the next.
The following code works, except for the first time around and I’ve gone through this code several times and can't see whey it wouldn't work.
Right now I'm just trying to echo out the workstation name, profile path (Windows XP or 7), the Application Data location and user name. As you can see from the output, the first time around it doesn’t add the 'Application Data' string.
I need another pair of eyes to look at this, PLEASE.
\\WORKSTATION1\C$\Documents and Settings\USER1APPSRV.INI
\\WORKSTATION2\C$\Documents and Settings\USER2\Application Data\ICAClient\APPSRV.INI
\\WORKSTATION3\C$\Users\USER3\AppData\Roaming\ICAClient\APPSRV.INI
\\WORKSTATION4\C$\Documents and Settings\USER4\Application Data\ICAClient\APPSRV.INIDim objDictionary Set objDictionary = CreateObject("Scripting.Dictionary") Set objFSO = CreateObject("Scripting.FileSystemObject") Set objLog = objFSO.CreateTextFile("C:\Scripts\Log.txt") Const ADS_SCOPE_SUBTREE = 2 Set objConnection = CreateObject("ADODB.Connection") Set objCommand = CreateObject("ADODB.Command") objConnection.Provider = "ADsDSOObject" objConnection.Open "Active Directory Provider" Set objCOmmand.ActiveConnection = objConnection objCommand.CommandText = _ "Select Name, Location from 'LDAP://OU=Workstation,OU=Finance,DC=fabrikam,DC=com'" _ & "Where objectClass='computer'" objCommand.Properties("Page Size") = 1000 objCommand.Properties("Searchscope") = ADS_SCOPE_SUBTREE Set objRecordSet = objCommand.Execute objRecordSet.MoveFirst i=1 Do Until objRecordSet.EOF objDictionary.Add i,(objRecordSet.Fields("Name").Value) i=i+1 objRecordSet.MoveNext Loop colComputers = objDictionary.Items For Each strComputer in colComputers If IsAlive(strComputer) Then GetWorkstationOS(strComputer) GetUserProfiles(strComputer,strAppDataPath) Else WScript.Echo strComputer & " * Was not on the network *" End If Next '----------------------------- Ping Workstation ----------------------------- Function IsAlive(strHost) Const OpenAsASCII = 0 Const FailIfNotExist = 0 Const ForReading = 1 Dim objShell, objFSO, sTempFile, fFile Set objShell = CreateObject("WScript.Shell") Set objFSO = CreateObject("Scripting.FileSystemObject") sTempFile = objFSO.GetSpecialFolder(2).ShortPath & "\" & objFSO.GetTempName objShell.Run "%comspec% /c ping.exe -n 2 -w 500 " & strHost & ">" & sTempFile, 0 , True Set fFile = objFSO.OpenTextFile(sTempFile, ForReading, FailIfNotExist, OpenAsASCII) Select Case InStr(fFile.ReadAll, "TTL=") Case 0 IsAlive = False Case Else IsAlive = True End Select fFile.Close objFSO.DeleteFile(sTempFile) Set objFSO = Nothing Set objShell = Nothing End Function '----------------------------- Get Workstation OS --------------------------- Function GetWorkstationOS(strComputer) Set objWMIService = GetObject("winmgmts:\\" & strComputer & "\root\cimv2") Set colItems = objWMIService.ExecQuery("Select * from Win32_OperatingSystem",,48) For Each objItem in colItems Select Case Trim(objItem.Caption) Case "Microsoft Windows XP Professional" strAppDataPath = "\Application Data\ICAClient\" 'WScript.Echo strComputer & " running Windows XP " & objItem.CSDVersion Case "Microsoft Windows 7 Professional" strAppDataPath = "\AppData\Roaming\ICAClient\" 'WScript.Echo strComputer & " running Windows 7 " & objItem.CSDVersion Case Else WScript.Echo strComputer & "Something else" End Select Next End Function '----------------------------- Get User Profiles ---------------------------- Function GetUserProfiles(strComputer,strAppDataPath) On Error Resume Next Const HKEY_LOCAL_MACHINE = &H80000002 Set objRegistry=GetObject("winmgmts:\\" & _ strComputer & "\root\default:StdRegProv") strKeyPath = "SOFTWARE\Microsoft\Windows NT\CurrentVersion\ProfileList" objRegistry.EnumKey HKEY_LOCAL_MACHINE, strKeyPath, arrSubkeys For Each objSubkey In arrSubkeys strValueName = "ProfileImagePath" strSubPath = strKeyPath & "\" & objSubkey objRegistry.GetExpandedStringValue HKEY_LOCAL_MACHINE,strSubPath,strValueName,strValue strPath = strValue arrPath = Split(strPath, "\") strUserID = arrPath(UBound(arrPath)) If Len(strUserID) = 7 Then If UserExists(strUserID) Then strAppSrvPath = "\\" & strComputer & "\" & Replace(strPath, ":", "$") & _ strAppDataPath & "APPSRV.INI" 'If objFSO.FileExists(strAppDataFolder & "APPSRV.INI") Then WScript.Echo strAppSrvPath End If End If Next End Function '----------------------------- Check User Exists ---------------------------- Function UserExists(strUserName) dtStart = TimeValue(Now()) Set objConnection = CreateObject("ADODB.Connection") objConnection.Open "Provider=ADsDSOObject;" Set objCommand = CreateObject("ADODB.Command") objCommand.ActiveConnection = objConnection objCommand.CommandText = _ "<LDAP://DC=fabrikam,DC=com>;(&(objectCategory=User)" & _ "(samAccountName=" & strUserName & "));samAccountName;subtree" Set objRecordSet = objCommand.Execute If objRecordset.RecordCount = 0 Then UserExists = False Else UserExists = True End If objConnection.Close End Function
"Fate rarely calls upon us at a moment of our choosing…"
All Replies
-
Friday, March 01, 2013 8:53 PM
It's probably the GetExpandedStringValue that fails.
You can try to do a Result = objRegistry.GetExpandedStringValue... and check the value of the result, 0 would be success, everything else is a failure.
What about getting the SystemDrive of the remote machine and then just appending "Users\" & username & "\AppData\Roaming"
Of cause that would only be true for Vista and up, however you could do some logic to determine if the remote computer were XP or below.
You can retrieve the Remote Computers SystemDrive, by getting the SystemRoot value from HKLM\Software\Microsoft\Windows NT\CurrentVersion\ and doing a substring to get the first 3 characters.
Best Regards
Claus Codam
Consultant, Developer
Coretech - Blog -
Friday, March 01, 2013 9:06 PM
strAppDataPath is defined in GetWorkstationOS, but only for two specific OSes. I suspect that the caption property returned by Win32_OperatingSystem for that first computer is not exactly equal to "Microsoft Windows XP Professional" or "Microsoft Windows 7 Professional". try changing this:
Case Else WScript.Echo strComputer & "Something else"
to this:
Case Else
strAppDataPath = "\\" & objItem.Caption & "\\"
WScript.Echo strComputer & "Something else"to see what the actual caption is.
If I am correct, this could impact on the results given for other computers, this one was just the most obvious because there was no previous valid data.
Al Dunbar -- remember to 'mark or propose as answer' or 'vote as helpful' as appropriate.
-
Monday, March 04, 2013 1:49 PM
Claus Codam Al Dunbar, thanks for both of your responses. Al, the objItem.Caption doesn’t show anything and un-commenting out the echo for Windows XP and 7 shows this first computer is running Windows XP Service Pack 3, just like the next one.
"Fate rarely calls upon us at a moment of our choosing…"
-
Monday, March 04, 2013 6:14 PM
re this part of your script:
For Each objItem in colItems Select Case Trim(objItem.Caption) Case "Microsoft Windows XP Professional" strAppDataPath = "\Application Data\ICAClient\" 'WScript.Echo strComputer & " running Windows XP " & objItem.CSDVersion Case "Microsoft Windows 7 Professional" strAppDataPath = "\AppData\Roaming\ICAClient\" 'WScript.Echo strComputer & " running Windows 7 " & objItem.CSDVersion Case Else WScript.Echo strComputer & "Something else" End Select Next
If objItem.Caption is null or blank, then the ELSE case will be executed, and that piece of code does not give variable strAppDataPath a value. Given that, I do not see how uncommenting the echo statements in the other two cases will show that the first computer is running xpsp3, because that is deduced from the objItem.Caption value, which you say is blank, empty, or null.
I think you are either reading the output incorrectly, or the script you are running is not exactly the same as the one you quoted here.
Have you tried the suggestion I gave previously, and, if so, what was the result? If not, please try this, and re-post the code in that For Each loop.
Al Dunbar -- remember to 'mark or propose as answer' or 'vote as helpful' as appropriate.
-
Monday, March 04, 2013 7:58 PM
Al, yes I did try your suggestion and I also uncommected out the 'WScript.Echo strComputer & " running Windows XP " & objItem.CSDVersion' to see if it was picking up the workstation OS. As you can see from the output, WORKSTATION1 is running Windows XP Service Pack 3 and should have set strAppDataPath to "\Application Data\ICAClient\", but it didn't set the variable. I've also added a 'WScript.Echo "strAppDataPath: " & strAppDataPath' to the GetUserProfile function and as you can see the first time around that variable is blank.
Microsoft (R) Windows Script Host Version 5.8
Copyright (C) Microsoft Corporation. All rights reserved.WORKSTATION1 running Windows XP Service Pack 3
strAppDataPath:
\\WORKSTATION1\C$\Documents and Settings\USER1APPSRV.INI
WORKSTATION2 running Windows XP Service Pack 3
strAppDataPath: \Application Data\ICAClient\
\\WORKSTATION2\C$\Documents and Settings\USER2\Application Data\ICAClient\APPSRV.INI
WORKSTATION3 running Windows XP Service Pack 3
strAppDataPath: \Application Data\ICAClient\
\\WORKSTATION3\C$\Documents and Settings\USER3\Application Data\ICAClient\APPSRV.INI***** script completed - exit code: 259 *****
The only thing different in the script that I posted and the script that I am running, I've change the domain, workstation and user names. The rest of the script is the same.
"Fate rarely calls upon us at a moment of our choosing…"
-
Monday, March 04, 2013 8:26 PMModerator
I think at this point you need to take a step back and re-evaluate the code you're trying to use. One good way to do this is to start writing a new script that contains only the absolute minimum amount of code needed to reproduce the problem you're having. This is an excellent way of troubleshooting when something is going wrong, and it also help you to understand the code better.
Bill
-
Monday, March 04, 2013 9:37 PM
Agreed. He obviously did not add the code I suggested, as it would not have caused the output of the "strAppDataPath" variable in the format indicated in his sample output. Had he made the change I suggested, the output would have looked more like this:
\\WORKSTATION1\C$\Documents and Settings\\\\USER1APPSRV.INI \\WORKSTATION2\C$\Documents and Settings\USER2\Application Data\ICAClient\APPSRV.INI \\WORKSTATION3\C$\Documents and Settings\USER3\Application Data\ICAClient\APPSRV.INI
whatever the value of that variable was would have been bracketted between two pairs of backslashes. The first line above indicates that this was a null or empty value. Had the actual operating system been, say, Windows NT, that first line would have appeared as:
\\WORKSTATION1\C$\Documents and Settings\\Windows NT\\USER1APPSRV.INI
Al Dunbar -- remember to 'mark or propose as answer' or 'vote as helpful' as appropriate.
-
Monday, March 04, 2013 10:07 PM
Al, thank you for your response, although I DID follow your suggestion. Which would have resulted in the double backslashes ONLY if the OS didn't match "Microsoft Windows XP Professional" or "Microsoft Windows 7 Professional" and it is finding "Microsoft Windows XP Professional", so it is not defaulting to "Case Else". It's just not passing the value of "strAppDataPath" the first time around and works with all of the other computer accounts in that one OU.
"Fate rarely calls upon us at a moment of our choosing…"
- Marked As Answer by os_car Monday, March 04, 2013 10:07 PM
- Unmarked As Answer by Bill_StewartMicrosoft Community Contributor, Moderator Sunday, March 10, 2013 2:57 PM
-
Monday, March 04, 2013 11:06 PM
I am glad you feel you have what is, to you, a suitable answer.
But if you are correct in what you say, namely that some of the code in a CASE block is mysteriously not being executed, then we have the following possibilities:
- vbscript itself is broken, but just in the case of this one script of yours; or:
- you have inadvertently not posted either the exact actual code run and/or results output, or have made some other error of interpretation.
- your script includes some self-modifying code.
This is nothing personal, however, my money is on #2.
If you want to get to the bottom of this, please post an exact copy of the current version of the GetWorkstationOS function and an exact copy of the corresponding output and we will try again. If you'd rather just get to the results you are looking for, I'd suggest you follow Bill's suggestion and start from scratch.
As an aside, I do not mean my comments to be insulting to you. I have scripted for quite a while and still remember one time having an intractable problem from which I concluded that the laws of mathematics must no longer be functioning properly. When you reach that stage it is a good idea to reflect on a saying by Spock and by Sherlock Holmes: "If you eliminate the impossible, whatever remains, however improbable, must be the truth", and consider the corollary: "when you have determined something extremely improbable to be the truth, there still remains a possibility that you have not yet eliminated all of the impossibles".
Al Dunbar -- remember to 'mark or propose as answer' or 'vote as helpful' as appropriate.
-
Tuesday, March 05, 2013 3:06 PM
Al, based on your three possibilities:
- Vbscript is not broken, because all of my other scripts work fine.
- The code I posted is the code I'm using. As previously stated, I have NOT left out any code and the only thing that I've changed is the domain information, based on a requirement from our audit department. The output I posted is also the output from the script, with only the workstation and user names changed, to protect the innocent.
- The script I posted is the script I’m using, no self-modifying code concealed.
I accepted my own answer, because I don't see that there will be an answer. The code works. Based on Bill Stewart's response, I have started out with absolute minimum code and build on that. I started out with just enough code to list all the computers in a particular OU. Then added the "Ping Workstation" function and then added the 'Get Workstation OS" function, which returned an OS of either "Microsoft Windows XP Professional" or "Microsoft Windows 7 Professional" for each and every workstation on that particular OU. I then added the "Get User Profiles" and "Check User Exists" functions. Those two I added together because once I found a profile on a computer I didn't want to waste my time checking for the INI file is the user was no longer in AD. But, in trouble shooting I have removed the "Check User Exists" function and I still get the same results from the "Get User Profiles" function where the variable from "Get Workstation OS" function is not passed to "Get User Profiles" function the first time the script is run.
"Fate rarely calls upon us at a moment of our choosing…"
-
Tuesday, March 05, 2013 6:13 PMModerator
If you're still having problems with unexpected variable values, please post a minimalist example of code that doesn't work (no need to include any WMI calls or anything like that) -- just a quick and short using some sample string values -- and the problem will probably become apparent.
Bill
-
Tuesday, March 05, 2013 8:29 PM
My sincerest apology, I've had to restate my problem several times that my wording may have changed from post to post. This script works except for the first computer it finds, earlier I said the first time I run the script. It's the strAppDataPath variable that is not being pasted to the next function ONLY on the first computer, but works fine for all other computers.
I've reposted the GetWorkstationOS and GetUserProfiles functions. As you can see, I have uncommended out the echoes when the GetWorkstationOS function finds the workstation's OS and it displays it for all the workstations in that OU.
I've added an echo to the GetUserProfiles function at the beginning to verify that the strAppDataPath variable is being pasted and from the output you can see that strAppDataPath does not contain any data for the first computer, although it did have a value in the previous function.
Microsoft (R) Windows Script Host Version 5.8
Copyright (C) Microsoft Corporation. All rights reserved.WORKSTAION1 running Windows XP Service Pack 3
strAppDataPath:
\\ WORKSTAION1\C$\Documents and Settings\USER1APPSRV.INI
WORKSTAION2 running Windows XP Service Pack 3
strAppDataPath: \Application Data\ICAClient\
\\ WORKSTAION2\C$\Documents and Settings\ USER 2\Application Data\ICAClient\APPSRV.INI
WORKSTAION3 running Windows XP Service Pack 3
strAppDataPath: \Application Data\ICAClient\
\\ WORKSTAION3\C$\Documents and Settings\ USER 3\Application Data\ICAClient\APPSRV.INI***** script completed - exit code: 259 *****
The exit code: 259 is because I stopped the script.
'----------------------------- Get Workstation OS ----------------------------------- Function GetWorkstationOS(strComputer) Set objWMIService = GetObject("winmgmts:\\" & strComputer & "\root\cimv2") Set colItems = objWMIService.ExecQuery("Select * from Win32_OperatingSystem",,48) For Each objItem in colItems Select Case Trim(objItem.Caption) Case "Microsoft Windows XP Professional" strAppDataPath = "\Application Data\ICAClient\" WScript.Echo strComputer & " running Windows XP " & objItem.CSDVersion Case "Microsoft Windows 7 Professional" strAppDataPath = "\AppData\Roaming\ICAClient\" WScript.Echo strComputer & " running Windows 7 " & objItem.CSDVersion Case Else WScript.Echo strComputer & "Something else" End Select Next End Function '----------------------------- Get User Profiles ----------------------------------- Function GetUserProfiles(strComputer,strAppDataPath) On Error Resume Next WScript.Echo "strAppDataPath: " & strAppDataPath Const HKEY_LOCAL_MACHINE = &H80000002 Set objRegistry=GetObject("winmgmts:\\" & _ strComputer & "\root\default:StdRegProv") strKeyPath = "SOFTWARE\Microsoft\Windows NT\CurrentVersion\ProfileList" objRegistry.EnumKey HKEY_LOCAL_MACHINE, strKeyPath, arrSubkeys For Each objSubkey In arrSubkeys strValueName = "ProfileImagePath" strSubPath = strKeyPath & "\" & objSubkey objRegistry.GetExpandedStringValue HKEY_LOCAL_MACHINE,strSubPath,strValueName,strValue strPath = strValue arrPath = Split(strPath, "\") strUserID = arrPath(UBound(arrPath)) If Len(strUserID) = 7 Then If UserExists(strUserID) Then strAppSrvPath = "\\" & strComputer & "\" & Replace(strPath, ":", "$") & _ strAppDataPath & "APPSRV.INI" WScript.Echo strAppSrvPath End If End If Next End Function"Fate rarely calls upon us at a moment of our choosing…"
-
Friday, March 08, 2013 5:20 AM
It is difficult to pinpoint precisely where your script is going wrong because of its complexity. But, to be clear, "going wrong" does not meant that it is producing erroneous results, only that it does not correspond to what you think it is supposed to do.
I believe that the cause of your problem here is one of variable scope. If so, then how these functions are called, and in what order, may be the cause. Consider the following demo script:
function sub1(x) select case x case 1 strAppDataPath = "one" wscript.echo "SUB1 " & x & " = " & strAppDataPath case 2 strAppDataPath = "two" wscript.echo "SUB1 " & x & " = " & strAppDataPath case else wscript.echo "whatever" end select end function function sub2(x,strAppDataPath) WScript.Echo "strAppDataPath: " & strAppDataPath end function for i = 1 to 3 sub1 i sub2 i,strAppDataPath next
and its output:
C:\Users\Al>cscript z.vbs Microsoft (R) Windows Script Host Version 5.8 Copyright (C) Microsoft Corporation. All rights reserved. SUB1 1 = one strAppDataPath: SUB1 2 = two strAppDataPath: two whatever strAppDataPath: two
C:\Users\Al>
This script exhibits a similar anomaly to your more complex one regarding what may appear as erroneous results on the first set of data processed. What is happening in this case is that on its first call, sub1 creates a local variable called strAppDataPath, because the global variable by the same name has not yet been created in the main loop. That global variable is created when it is passed to sub2 in the second statement. The next time through the loop there is such a variable, and that is assigned a value in sub1, which is then available to be passed to sub1 for its use.
In addition to this error I think you are inviting trouble or at least confusion when you pass a variable of global scope to a function as a parameter of the same name, but that is another issue.
Al Dunbar -- remember to 'mark or propose as answer' or 'vote as helpful' as appropriate.
- Proposed As Answer by Al Dunbar Sunday, March 10, 2013 6:19 AM
- Marked As Answer by Bill_StewartMicrosoft Community Contributor, Moderator Sunday, March 10, 2013 2:57 PM
-
Friday, March 08, 2013 5:40 AM
Further to my above analysis, I think a "quick fix" for your script might only require that you add this statement:
dim strAppDataPath
to the top of the main program.
That is not optimal, though, as it only seems to propagate the likelihood of making a similar mistake at some point. I think a better solution would be to script in such a way that the code does not rely on the incidental creation of global variables. And one good way to do that is to avoid the use of global variables altogether.
As an aside, I am very rusty at vbscript, and surprised myself by figuring this out after having re-read Bruce Payette's excellent book, "Windows Powershell in Action", specifically the section on variable scope.
Al Dunbar -- remember to 'mark or propose as answer' or 'vote as helpful' as appropriate.
-
Friday, March 08, 2013 5:45 AMOne further aside: my initial ideas about the nature of this problem were completely wrong, in that I assumed that the ELSE clause was being executed because that first machine had a slightly different version of windows. In a sense, I made a mistake similar to yours, in that I saw the code and simply assumed it was correctly doing what it appeared your intention was for it to do.
Al Dunbar -- remember to 'mark or propose as answer' or 'vote as helpful' as appropriate.
-
Sunday, March 10, 2013 6:26 AMAlthough I dislike the practice I have proposed my own post as answer because I believe it gives an accurate analysis and explanation of the problem posed by the OP. Unfortunately, he marked one of his own posts as answer because, as he said: "because I don't see that there will be an answer". As a result, this seems to have prevented the OP and other contributors from continuing the discussion.
Al Dunbar -- remember to 'mark or propose as answer' or 'vote as helpful' as appropriate.
-
Sunday, March 10, 2013 2:56 PMModerator
Hi Al,
You did precisely what I previously asked the OP to do: Write short example code containing only variable assignments and no other cruft (such as WMI calls, etc.). This is exactly and precisely what is needed for the OP to understand what is wrong with his code. If the OP will take the time to read and understand this thread and follow the advice, I have no doubt he will be able to solve his own problem. I think you have gone above and beyond in your efforts to facilitate this understanding so I am going to mark your post as the answer.
Bill
-
Sunday, March 10, 2013 9:53 PM
Thanks, Bill. But more important to me than having my contribution marked as answer is that the OP actually read and understand.
The problem here is an implicit assumption he has made that is so subtle he did not realize he made it. As such, this assumption (i.e. that one never needs to concern oneself with issues of scope) will likely create more difficulties for him in the future with code that seems to look OK, but really isn't.
Al Dunbar -- remember to 'mark or propose as answer' or 'vote as helpful' as appropriate.
-
Sunday, March 10, 2013 10:01 PM
Earlier Bill gave this excellent advice, suggesting that the OP write "a new script that contains only the absolute minimum amount of code needed to reproduce the problem you're having".
Unfortunately it can be difficult for an inexperienced scripter to realize which parts of his code are associated with the problem, and which are not. I was unable to generate that demo script myself until I actually had that eureka moment in which I realized the probable cause of the problem.
Al Dunbar -- remember to 'mark or propose as answer' or 'vote as helpful' as appropriate.

