Showing posts with label x64. Show all posts
Showing posts with label x64. Show all posts

Monday, September 17, 2012

Windows Scripting Host Sucks on 64-bit Windows

Here's an example of something I've known about for years, but somehow forgot, and at the most inconvenient time: Windows Scripting Host SUCKS on 64-bit Windows.  I'm almost ready to dump VBScript for good and move on with PowerShell after this.  There is a workaround for this, but it's stupid.  It's beyond stupid.  It's pathetic.

The code below looks for the Uninstall key value named "DisplayName" for 7-zip 9.20 on a Windows 7 64-bit computer.  The first key path returns "null".  The second key path returns the appropriate version "9.20.00.0"


[CODE]

Set objShell = CreateObject("Wscript.Shell")

Const HKEY_LOCAL_MACHINE = &H80000002
Const appGUID = "{23170F69-40C1-2701-0920-000001000000}"
Const vName = "DisplayName"

kPath1 = "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall"
kPath2 = "SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall"

wscript.echo RegValue(kPath1 & "\" & appGUID, vName)
wscript.echo RegValue(kPath2 & "\" & appGUID, vName)
 
Function RegValue(key, v)
  Dim objRegistry, strValue
  Set objRegistry = GetObject("winmgmts:{impersonationLevel=impersonate}" & _
    "!\\.\root\default:StdRegProv")
  objRegistry.GetStringValue HKEY_LOCAL_MACHINE, key, v, strValue
  RegValue = strValue
End Function

[/CODE]

The screen capture below shows the Registry key and its values.  Note the path shown in the Status Bar along the bottom.  This is the default location under HKLM\Software\Microsoft.


When you install most 64-bit applications, this is where they will record their Uninstall information.  If you install a 32-bit application however, it puts the information under HKLM\Software\WOW6432Node\Microsoft\...


Note that the Uninstall GUID for 7-Zip 9.20 doesn't exist under the Wow6432Node tree.  It only resides under the default tree.  This can get really messy when you start installing 32-bit applications on 64-bit Windows 7.  And before you think that's easy to avoid, think again.

The problem is that CScript under the C:\Windows\SysWOW64 path only looks under the Wow6432Node tree for anything.  Even when you use the ExpandEnvironmentStrings method of the Shell object, it will expand the variable using what it finds here.

For example, if you execute the following VBScript code on a 64-bit machine...


[CODE]

Set objShell = CreateObject("Wscript.Shell")
wscript.echo objShell.ExpandEnvironmentStrings("%programfiles%")
wscript.echo objShell.ExpandEnvironmentStrings("%programfiles(x86)%")

wscript.echo objShell.RegRead("HKLM\SOFTWARE\WOW6432Node\" & _
  "Microsoft\Windows\CurrentVersion\CommonFilesDir")
wscript.echo objShell.RegRead("HKLM\SOFTWARE\Microsoft\" & _
  "Windows\CurrentVersion\CommonFilesDir")

[/CODE]

Notice the path of CScript.exe I invoke in the output capture below.  Note the impact each has on the output also...
Compare the two Value collections under each Registry key path.  The path is shown in the Status Bar along the bottom of each window...
One more example, and this is where it shows it's 2:00 AM beer goggles ugliness...
Consider the following two Registry keys:
HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\Dave
HKLM\SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\Dave
Both keys have a Value named "DisplayName", but key1 is assigned "Dave123", while the same Value under key2 is assigned "Dave456" (both are type REG_SZ).
[CODE]

Set objShell = CreateObject("Wscript.Shell")
key1 = "HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\Dave"
key2 = "HKLM\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall\Dave"
wscript.echo objShell.RegRead(key1 & "\DisplayName")
wscript.echo objShell.RegRead(key2 & "\DisplayName")

[/CODE]

If you execute the above code using CScript.exe from both locations on a typical Windows 7 64-bit computer, this is what you'd see...
Conclusion
I promised you a "workaround" at the beginning of this article, and here it is:  On 64-bit Windows 7 systems, to ensure you get accurate results, you absolutely HAVE to invoke the CScript.exe from C:\Windows\System32.  However, if you are are looking for installed Applications you need to invoke BOTH of them.  That's right, both of them.  Why? Because you can't get a complete picture without poking into both "sides" of the Registry of a 64-bit Windows 7 computer.
And I haven't even mentioned crawling through HKCU to find installed apps.  Some of you may be making a funny face right now, thinking "there's no application uninstall keys under HKCU" and then you check and realize that there are.  Most ClickOnce application installations, and pretty much any "per-user" installations for that matter, will hide their Registry stuff under HKCU.  So to really see what's "installed" on a 64-bit client, you need to look under the following places...

  • HKLM\Software\Microsoft\Windows\CurrentVersion\Uninstall
  • HKLM\Software\Wow6432Node\Microsoft\ Windows\CurrentVersion\Uninstall
  • HKEY_USERS (crawl every SID tree beneath it)
  • probably other locations that I'm just too lazy to dig up right now

Is this retarded?  Yep.  Could Microsoft remedy this 64-bit issue with a patch for WSH and Wscript.exe/CScript.exe?  Yep.  Will they?  Don't bet on it.  
Note:  Wrapped code lines in the above examples are for formatting only.  The actual script code used for the examples does not have the lines wrapped.

Saturday, February 19, 2011

The %ProgramFiles% Bug, Part 2 / Distrust & Uncertainty

So I already mentioned the bug with VBscript reading the "ProgramFiles" environment variable from Windows Vista and Windows 7 (and corresponding server platforms as well).  Well, I've done some more tinkering and found that it is due to WSH reading environment properties from a different place than CMD, PowerShell and KiXtart read from.  This is still pertaining to 64-bit operating system versions, not 32-bit.  This is very odd and very concerning for anyone still working with VBscript.  After all: If this is looking in the wrong place, what else is?

Examples?

In PowerShell, try both of the following and observe the output…

> dir env:"ProgramFiles"

> dir env:"ProgramFiles(x86)"

In a CMD console, try both and observe the output…

> echo %programfiles%

> echo %programfiles(x86)%

Then create a .VBS script file and drop the following code in and run it…

Set objShell = CreateObject("Wscript.Shell")
wscript.echo objShell.ExpandEnvironmentStrings("%programfiles(x86)%")
wscript.echo objShell.ExpandEnvironmentStrings("%programfiles%")

Yep - this is f***ed up.  Microsoft should release a patch to correct this, but I doubt they will.  Asking everyone to stop what they're doing and convert all of their legacy scripts to PowerShell is not only a bong-smoking delusion, it's also like drinking the bong water and then eating the bong!  I can see this causing a silent havoc on lots of systems around the world that use that environent variable in path resolution and path concatenation operations.  As I said earlier: something this esoteric and fundamental belies a worrisome distrust for VBscript in general.  How could you trust flying in an aircraft if you learn one of the key instruments has a known malfunction but is never going to be fixed?

Given that PowerShell is still not suitable for login scripting, and is still TOO DAMN SLOW to initialize, I will probably start shifting back towards KiXtart for the time being until this is either resolved (not likely) or until Microsoft beats PowerShell into a leaner, meaner execution machine.  (as an aside: my guess is that due to the significant .NET stack that has to be awoken each time the shell is initialized, their "solution" will be to autorun it in the background like so many bloatware vendors do already).

The results of this unchecked bug could be very bad.

uncertainty

Monday, January 31, 2011

Bugs that May Never be Fixed

I'm guessing that you are most likely familiar with Windows Environment variables, since most of the readers here are pretty savvy about Windows stuff.  So, you should be pretty familiar with how 64-bit Windows variables are spread out too…

%programfiles%

%programfiles(x86)%

…and so on.

Well, guess what?  VBscript (WSH), has a little bug regarding how it handles the [programfiles] variable on 64-bit Windows 7.  To demonstrate, try this…

  • Open a CMD console
  • Type SET and press Enter
  • Observe the values for [ProgramFiles], and [ProgramFiles(x86)]
  • Type echo %programfiles% and press Enter.  Observe the output.
  • Now run the following VBscript code and observe the output
Set objShell = CreateObject("Wscript.Shell")
wscript.echo objShell.ExpandEnvironmentStrings("%ProgramFiles%")



Notice anything odd?  While the echo statement returns "C:\Program Files", the script result returns "C:\Program Files (x86)", as will "%programfiles(x86)%".  So how do you get the 64-bit native app install path of "C:\Program Files"?  Well, using the ExpandEnvironmentStrings method - you don't.



And you might expect that you could rely upon the Environment property of the WshShell object, like this…




Set WshSysEnv = objShell.Environment("PROCESS")
wscript.Echo WshSysEnv("ProgramFiles")



But, sadly, it too returns "C:\Program Files (x86)"



And just when you thought that maybe something like KiXtart could ride up on a stallion wearing a shiney suit of armor and save the day with …




Break ON
? ExpandEnvironmentVars("%ProgramFiles%")


Well, think again.

There is one workaround, but it's not very "elegant" in terms of methodology (but it works, hey):




Set objShell = CreateObject("Wscript.Shell")
sysdrv = objShell.ExpandEnvironmentStrings("%SystemDrive%")
progfiles = sysdrv & "\Program Files"




The same approach works for KiXtart and others as well.

Conclusion



Given that Microsoft left VBscript at the wedding altar, sobbing and destitute, and ran off in a stolen convertible to Vegas with PowerShell, I wouldn't expect any changes to this behavior - ever.