Google

2013-09-02

PowerShell try catch gotcha

I check out StackOverflow's PowerShell section time to time, and one issue seems to trip people a lot: Try/Catch block.

Here is how Try/Catch is block is constructed:


Try {
## Some code here which is likely to fail
}
Catch {
## What to do if try block has a "terminating" error
}

Here is an example:


try { get-content c:\a.txt;"file exists" }catch{"that file does not exist"}

If you run the code above; it will throw an error on the Get-Content if 'c:\a.txt' file does not exist. And in theory we should be able to catch that but that is not exactly what happens:


PS Z:\> try { get-content c:\a.txt;"file exists" }catch{"that file does not exist"}
get-content : Cannot find path 'C:\a.txt' because it does not exist.
At line:1 char:7
+ try { get-content c:\a.txt;"file exists" }catch{"that file does not exist"}
+       ~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : ObjectNotFound: (C:\a.txt:String) [Get-Content], ItemNotFoundException
    + FullyQualifiedErrorId : PathNotFound,Microsoft.PowerShell.Commands.GetContentCommand

file exist
PS Z:\>

Did you notice that "file exist" line being printed? Umm, what happened?

Well, remember I said "terminating" errors are caught. the first line is an error but not a terminating error. And therefore, the next line is executed.

Anything we can do to stop this? Yes, use -ErrorAction after the command or $ErrorActionPreference.


PS Z:\> try { get-content c:\a.txt -ErrorAction stop; "file exists" }catch{"that file does not exist"}
that file does not exist
PS Z:\>

With ErrorAction set to STOP, the error on the first line becomes a terminating error and hence get caught in the CATCH {} block.

Similarly, we can set the $ErrorActionPreference variable before the line top "STOP"; and tell our code to treat any error as terminating one.


PS Z:\> $ErrorActionPreference="STOP"; try { get-content c:\a.txt; "file exists" }catch{"that file does not exist"}
that file does not exist
PS Z:\>

If not specified, $ErrorActionPreference is set to "Continue".


PS Z:\> $ErrorActionPreference
Continue
PS Z:\>

Note that it is not "SilentlyContinue", which would execute the next statement without complaining about the error.


PS Z:\> try { get-content c:\a.txt -ErrorAction SilentlyContinue; "file exists" }catch{"that file does not exist"}
file exist

This is equivalent to "On Error Resume Next" in vbScript.

2013-08-13

Work around quirky Printui.dll on XP

In the previous post, I talked about issues I was seeing when using printui.dll to push per-machine connections on Windows XP. I did tons of testing and in the realized that XP's spooler service was too flaky for my purpose.

I spent some time diving deeper into what the commands are doing and if there was anything that I could do in the startup script that would make it more consistent...

Let's cut to the chase:

I concluded that the most reliable way to make sure printers will be pushed when users logged in was to make sure relevant connection entries are created by my script under HKLM during startup, instead of relying printui.dll /ga switch.

How did I arrive at this conclusion?

Well,  I used Process Monitor (aka Procmon) from SysInternals,
watched what happens if I issue commands like the ones below manually

  • rundll32 printui.dll,PrintUIEntry /ga /n\\PrintServer\Printer (add per-machine printer connection)
  • rundll32 printui.dll,PrintUIEntry /gd /n\\PrintServer\Printer (remove per-machine printer connection)

A Watched sequence of events during boot (Procmon > Options > Enable Boot-Logging)

Here are a few things to note that will help us understand what is going on :

  • Our Print Server is named PrintServer1 
  • Printers shared on it has sharenames Printer1.. PrinterN 
  • HKLM Connections key: HKLM\SYSTEM\CurrentControlSet\Control\Print\Connections
  • HKCU Connections key: HKCU\Printers\Connections
  • If there is no ",,PrintServer1,PrinterX" entry under HKLM Connections key, user does not get the PrinterX connection.

Q. What happens if we "add per-machine printer connection" in a user session?
A. Seemingly nothing, user does not see the new printer connections until Spooler service is started. In reality, a new registry key under HKLM Connections key is created.

Q. So, what happens if we re-start the spooler services?
A. Normally, user gets the printer connection. It does not show up immediately, spooler service takes its sweet time but unless something goes terribly wrong, new printers shows up in a minute or two. Here is how (shortened for relevancy):

Spooler Service checks the entries under HKLM Connections key and detects a new printer connection, which will be in the following form:
HKLM\SYSTEM\CurrentControlSet\Control\Print\Connections\,,PrintServer1,PrinterX
Server=\\PrintServer1 (RegSZ)
Provider=Win32spl.dll (RegSZ)
LocalConnection=0x0000001 (DWORD)

You probably noticed that \\PrintServer1\PrinterX has been transformed to ,,PrintServer1,PrinterX and became a key. Provider and LocalConnection always had the same value, "Server" was set to the name of the Print Server with '\\' prefix.

So far, so good. The rest is the tiring loop:

  • Spooler starts going through SIDS
  • Creates a new connection under HKCU\Printers\Connections,,PrinterServer1.PrinterX
  • Clones the 'Server', 'Provider', 'LocalConnection' settings from HKLM Connections key
  • Queries and creates a new port setting:

    HKLM\Software\Microsoft\Windows NT\CurrentVersion\Ports\NeXX: (Reg_SZ ),
    where Ne in my case went from Ne00 to Ne08
  • It then created an entry under devices that linked the port to printer:

    HKCU\Software\Microsoft\Windows NT\CurrentVersion\Devices\
    \\PrintServer1\PrinterX = Winspool,NeXX: (Reg_SZ)
  • In the next step, spooler creater a PrinterPort entry in the following form:

    HKCU\Software\Microsoft\Windows NT\CurrentVersion\PrinterPorts\
    \\PrintServer1\PrinterX = Winspool,NeXX:15,45 (Reg_SZ)
  • In the actual flow, HKCU was the last. Spooler actually started with SID S-1-5-19 (HKU\S-1-5-19\)and cycled through all SIDS under HKU but you get the point.
  • THEN, an important step comes: querying printer, getting a connection to it (I see a %windir%\System32\spool\PIPE file created), creating a registry key for printer and writing everything about that printer

    HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Print\Providers\LanMan Print Services\Servers\PrinterServer1\Printers\PrinterX
So, that's gist of what spooler seems to be doing. 

What's the problem?

Well, the problem I identified was that during the startup Spooler service would something be slacking. The command I ran to add a per-machine printer connection would return 258. 
Whenever rundll32 returned 0, that process ended up creating an HKLM connection entry and printer showed up just fine for user.
If, however, rundll32 returned 258, no connection was created.

So, what does return code 258 mean? Answer comes from Alan Morris of Microsoft Windows Printing team:

"this is the error
C:\>winerror 258
   258 WAIT_TIMEOUT <--> No NTSTATUS matched
printui does not validate the data that you are adding to the registry."
He goes on to explain:

"I'd check the number of printers and drivers as well as the drivers installed on a machine where this is failing.  The spooler may not be fully online and printui does call into spooler for these methods." 

Well, all good, but I had seen services.exe kicking off spooler service a full minute before any commands were issued to it. Additionally, spooler service would almost always map the first printer but the number of printers it connected was varying each time.

I was using the same XP VM and got considerably different results (sometimes tons of return code 258 errors, sometimes just 1 or two).

Techs in the field were reporting a similar pattern and a quick  Google search also got many hits in terms of people complaining about success rate of using these commands in the startup scripts.

What do we do now?

Well, as I said in the beginning, my approach was to help the spooler service a bit by creating the HKLM connections registry keys for it. In my tests, it worked like a charm (YMMV) every single time.

Below is modified sample script


on error resume next
Const ForReading = 1, ForWriting = 2, ForAppending = 8, CanCreate=True
ScriptVersion="2013-08-13-02"

Set oShell = CreateObject("WScript.Shell")
oShell.LogEvent 0, "WinXP_WSStartup.vbs: Script Version: " & ScriptVersion

'Get group membership for the machine
Set objSysInfo = CreateObject("ADSystemInfo")
Set objComputer = GetObject("LDAP://" & objSysInfo.ComputerName)

Groups = objComputer.GetEx("MemberOf")
' If Above Command does not find any groups for computer, it will error
if  Err.Number <> 0 Then  
 oShell.LogEvent 0, "WinXP_WSStartup.vbs: Computer is not member of any AD groups. Exiting."
 Wscript.Quit
end if

isFirst=vbTrue
Count=0
For Each group In Groups
  group=Replace(group,"/","\/")   ' escape groups with a slash in the object name   
  Set objGroup = GetObject("LDAP://" & group)
  If ( Instr(LCase(objGroup.CN),"grp.prn." ) > 0 ) then          
  If isFirst Then
   PrinterGroups = objGroup.CN
   isFirst=vbFalse
   Count=1
  Else
   PrinterGroups = PrinterGroups & ";" & objGroup.CN
   Count=Count+1
  End If   
  End if 
  'AllGroups = AllGroups & ";" & objGroup.CN
Next 

if (Count > 0) Then
   
 'Take care of setting up Reg File
 Set oFSO = CreateObject("Scripting.FileSystemObject") 
 sRegFile = oShell.ExpandEnvironmentStrings("%TEMP%")  & "\WinXP_WSStartup_PrinterConnections.reg" 
 If oFSO.FileExists(sRegFile) Then
  oFSO.DeleteFile(sRegFile)
 End If 
  
 Set oRegFile = oFSO.OpenTextFile(sRegFile, ForAppending, CanCreate)
 oRegFile.WriteLine "Windows Registry Editor Version 5.00" & vbCrLF & vbCrLF
  
 ' Now let's process each printer grou[
 
 arrPrinterGroups=Split(PrinterGroups,";") 
 for each PrinterGroup in arrPrinterGroups 
  arrGroup = Split(PrinterGroup,".")
  ' skipping first 2 grp and prn as groups are named grp.prn.ServerName.PrinterName
  Server=arrGroup(2)
  Printer=arrGroup(3)
  
  '' Update registry file
   oRegFile.WriteLine("[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Print\Connections\,," & Server & "," & Printer & "]" )
   oRegFile.WriteLine(chr(34) & "Server" & chr(34) & "=" & chr(34) & "\\\\" & Server & chr(34))
   oRegFile.WriteLine(chr(34) & "Provider" & chr(34) & "=" & chr(34) & "win32spl.dll" & chr(34))
   oRegFile.WriteLine(chr(34) & "LocalConnection" & chr(34) & "=dword:0000001" & vbCrLf)

  ''
'  cmdConnectPrinter="%comspec% /c rundll32 printui.dll,PrintUIEntry /ga /n\\" & Server & "\" & Printer
  oShell.LogEvent 0, "WinXP_WSStartup.vbs: Connecting to Printer: " & Printer & " on Server: " & Server
'  oShell.LogEvent 0, "WinXP_WSStartup.vbs: Running " & cmdConnectPrinter
  
  ' Hide Window and return immediately without waiting
'  oShell.Run cmdConnectPrinter,0,false
   
 Next 
 
 oRegFile.close
 
 ' Import Printer Connection file silently
 oShell.Run "regedit /s " & Chr(34) & sRegFile & Chr(34), 0, True 
 
End If

oShell.LogEvent 0, "WinXP_WSStartup.vbs: Nothing else to do. Exiting" 
Set oShell=Nothing

2013-08-10

Printer Connection Issues on XP

I've been hitting my head against the wall with the inconsistencies I see when trying to use Microsoft printui.dll to connect network printers on XP machines. Here goes the story...

Problem:

There are a bunch of "floor" printers that are published in a Windows 2008 R2 print Queue. We want every user who logs on to the computers on that floor to automatically get these printers.

Solution:

There are several well known ways, I won't go into details. Here is a nice blog that goes over them: http://blog.powershell.no/category/print-management/

In my case, I came up with a naming schema for printer groups

  • grp.prn.PrintServerName.PrinterShareName 

ComputerNames are added into these groups, which I then parse through a startup script and use well-known printui.dll to connect the machine to these printers. There were several technical but of course also some political reasons to do it this way. Your mileage may vary.

Windows 7 & PowerShell

Anyway, I wrote a Computer Startup PowerShell script that is deployed via Group Policy, put the logic in to detect which printers the computer should be connecting to, and wrapped it around the command below:

  • printui.exe /ga /n\\PrintServer\Printer

and all went well.

The switch (ga) is causes "per-machine" connection. Underneath, it caused creation of a registry key entries under HKLM and once users are logged in it "pushes" these connections to user, so that user is able to see and print to those printers without requiring them do anything.

Windows XP, still around...

XP is another story. Well, for one, xp does not have printui.exe but its older cousin rundll32 printui.dll,PrintUIEntry, which is well documented on technet, is still around and unfortunately, there are several XP machines without PowerShell in the environment, so I had to go back to vbscript.

It's a similar story, I back-ported the logic and script to vbscript:

  • rundll32 printui.dll,PrintUIEntry /ga /n\\PrintServer\PrinterName 

Note that, technically, it's not the printer name we use but the Printer Share Name, which may be different than the printer name). Tested it, yep works.

A couple of days later, I hear from one of the techs that printers are not getting mapped for his computers. Script logging clearly showed that it ran the command. Problem is, command does not have a return code to tell us whether command was successful or not. I could not find any documentation that explains the details of what goes on at that point.

Perhaps a debug flag somewhere would be useful but spooler is a very magical and at the same time finicky service especially on Windows XP. One thing is certain, it is inconsistent and there is no way to know why (yet)...

I hit other weird issues during troubleshooting. Below is one of them and the workaround.

Issue:

I wanted to remove some of the printers I connected to. Because these printers are connected per-machine using /ga switch, they need to be removed using /gd switch.

So, I run the appropriate command

  • Locally: rundll32 printui.dll,PrintUIEntry /gd /n\\PrintServer\PrinterName
  • Remotely: rundll32 printui.dll,PrintUIEntry /gd /c\\TargetComputer /n\\PrintServer\PrinterName


Printer connection cannot be removed. Operation could not be completed.

Workaround:

The fastest workaround I found for this issue is delete the relevant registry keys that are under:

  • HKLM\System\CurrentControlSet\Control\Printer\Connections\


Below are some of the other useful resources on Printing


Sample VBScript:

On error resume next
ScriptVersion="2013-08-09-02"
Set oShell = CreateObject("WScript.Shell")
oShell.LogEvent 0, "WinXP_WSStartup.vbs: Script Version: " & ScriptVersion

Set objSysInfo = CreateObject("ADSystemInfo")
Set objComputer = GetObject("LDAP://" & objSysInfo.ComputerName)

Groups = objComputer.GetEx("MemberOf")
' If Above Command does not find any groups for computer, it will error
if Err.Number <> 0 Then
oShell.LogEvent 0, "WinXP_WSStartup.vbs: Computer is not member of any AD groups. Exiting."
Wscript.Quit
end if

isFirst=vbTrue
Count=0
For Each group In Groups
group=Replace(group,"/","\/") ' escape groups with a slash in the object name
Set objGroup = GetObject("LDAP://" & group)
If ( Instr(LCase(objGroup.CN),"grp.prn." ) > 0 ) then
If isFirst Then
PrinterGroups = objGroup.CN
isFirst=vbFalse
Count=1
Else
PrinterGroups = PrinterGroups & ";" & objGroup.CN
Count=Count+1
End If
End if
Next

if (Count > 0) Then
arrPrinterGroups=Split(PrinterGroups,";")
for each PrinterGroup in arrPrinterGroups
arrGroup = Split(PrinterGroup,".")
' skipping first 2 grp and prn as groups are named grp.prn.ServerName.PrinterName
Server=arrGroup(2)
Printer=arrGroup(3)

cmdConnectPrinter="cmd /c rundll32 printui.dll,PrintUIEntry /ga /n\\" & Server & "\" & Printer
oShell.LogEvent 0, "WinXP_WSStartup.vbs: Connecting to Printer: " & Printer & " on Server: " & Server
oShell.LogEvent 0, "WinXP_WSStartup.vbs: Running " & cmdConnectPrinter

' Hide Window and return immediately without waiting
oShell.Run cmdConnectPrinter,0,false

Next
End If

oShell.LogEvent 0, "WinXP_WSStartup.vbs: Nothing else to do. Exiting"
Set oShell=Nothing

*******
In PowerShell, I wrote a couple of functions, thinking I might use the same logic to add user-side printer connections, hence the 'user' & 'computer' objectCategories below.

A single line calls the functions:


Connect-NetworkPrinter -objectCategory computer


Here are the functions:


Function Connect-NetworkPrinter {
[CMDLETBINDING()]
  Param(    
    [ValidateSet('user','computer')]
    [String]$objectCategory='computer',

[string]$PrinterGroupIdentifier='^cn=grp.prn.',
[string]$name )
if (!$name) { if ($objectCategory -eq 'computer') { $name=$($env:COMPUTERNAME) } if ($objectCategory -eq 'user') { $name=$($env:USERNAME) } } Write-Verbose "Connect-NetworkPrinter: PrinterGroupIdentifier set to $PrinterGroupIdentifier" Write-Verbose 'Connect-NetworkPrinter: Calling Function to Get Group Membership'
$groups = Get-ADGroupMembership -objectCategory $objectCategory -name $name
if (!$groups) { Write-Verbose "Connect-NetworkPrinter: No groups returned for current computer" logMsg "Connect-NetworkPrinter: No groups returned for current computer" return $false }
$printergroups=@() foreach ($group in $groups) {
Write-Verbose "Connect-NetworkPrinter: Checking group $group" if ($group -match $PrinterGroupIdentifier) { Write-Verbose "Connect-NetworkPrinter: Matches criteria $group"
$Server,$Printer,$null=($group -replace $PrinterGroupIdentifier -replace ',ou=.*$').split('.')
Write-Verbose "Connect-NetworkPrinter: Connecting to Printer: $Printer on Server: $Server" try { Invoke-Expression 'rundll32 printui.dll,PrintUIEntry /ga /n"\\${Server}\${Printer}"' ## rundll32 does not return anything so cannot check on error logMsg "Connecting to Printer: $Printer on Server: $Server" } catch { Write-Error "An error occured $_" logMsg "Error occured connecting to Printer: $Printer on Server: $Server" -entrytype 'Error' }
# [PSCustomObject]@{Server=$server;printer=$printer} ## return PSCustomObject } } Write-Verbose 'Connect-NYUPrinter: Exiting' logMsg "Printer connections complete"
}
function Get-ADGroupMembership { <# .SYNOPSIS Gets Active Directory Group membership for users or computers
.EXAMPLE Get-ADGroupMembership -name 'user1','user2' -Verbose Displays group membership for users user1 and user2
.Parameter name Name of the computers or users
.Parameter ObjectCategory Can be user or computer. By default it is 'computer'
.NOTES Output is string array of Distinguished Names. If nothing is found, it returns $false #>
[cmdletbinding()] param([Parameter(Mandatory=$true)] [string[]]$name, [ValidateSet('user','computer')] [String]$objectCategory='computer' ) Begin { Write-Verbose 'Get-ADGroupMembership: Creating Directory Search Object' $searcher=New-Object DirectoryServices.DirectorySearcher }
Process { $name | % { Write-Verbose "Get-NYUGroupMembership: Processing $_" $filter="(&(objectCategory=$objectCategory)(cn=$_))" ## this will only find user object $searcher.PageSize=1000 $searcher.Filter=$filter $result=$searcher.FindOne()
if ($result) { $result.Properties.Item("memberOf") } else { Write-Verbose '...is not member of any groups' } } }
END { Write-Verbose 'Get-ADGroupMembership: Disposing Directory Search Object' $searcher.Dispose() } }
I am not posting here the 'logmsg' function that the PowerShell functions are referring to, but it is basically just logging events into a Special Log I set up for all my scripts. It's so old, I did not even bother to name it PowerShell-way.

Enjoy!

2013-06-10

How to install iOS7 Beta on iPhone 5

I have captured some screenshots but do not have time to upload them yet. Upgrade process is pretty much the same as before:


  • Download iOS 7 Beta from developer.apple.com > iOS Downloads (You need to have iOS Developer Account with Apple to get there) Protip: Use Safari on Mac, if possible. For whatever reason, when I used Chrome on my mac, it would just direct me to 'maintenance' page, which said 'We'll be back soon'.
  • Open up the downloaded dmg and extract the .ipsw file on to your desktop
  • Launch iTunes & Connect your iPhone (Supported models iPhone 4, 4S, 5)
  • Click on  your device name and make sure to back it up (on iCloud)
  • You can also do a backup on your iPhone using Settings > iCloud menu.
  • Once done, click Option Key (Alt key if you are using PC keyboard) and click Restore on iTunes
  • On the File Picker window, click the Desktop on the left and choose the extracted iOS7 file
  • All in all this takes about 5mins.
  • Once completed, it will try to activate. Your phone needs to be registered in the iOS Dev Portal.







Update 2013-06-19:

After using iOS7 for a week, I rolled back. I worked out most of the annoyances but Google+ kept on crashing and I do use Automatic Backups for my pics. So, I can't wait for months to get it all sorted out.

Here are a couple of notes:

  • Updates did not work always. Your would see them but clicking update would not do anything.
  • Although I backed up everything to iCloud, most of the settings for apps did not come back I had to enter username/pwd for many apps as if just installed.
  • Google Authenticator was one of the apps where I lost data and I had to move all 2 factor authentication accounts using 'moving to new iphone' menu. Facebook two-factor authentication is easy to move as well.


2013-04-01

Change Location Settings via PowerShell

Today, I have seen a question on StackOverflow.com about how to turn on/off Location Settings in Windows 8 via PowerShell.

This setting is available in the Control Panel (Win + I > Control Panel), under "Location Settings".


To be able to locate the relevant registry key, I launched Procmon (aka. Process Monitor) from SysInternals. Anyone who used the application knows how overwhelming the data will be in just a few seconds of capture. So I had to be quick:

  • I started the capture, 
  • immediately cleared the checkbox next to "Turn on the Windows Location Platform", 
  • clicked Apply
  • And stopped Capture

Even though all that took a few secs, capture generated tens of thousands of events. I started from bottom, excluding all the irrelevant Operations, until I reached the one I am looking for "RegSetValue"





The path it referred to sounded promising:
HKLM\Software\Microsoft\Windows NT\CurrentVersion\Sensor\Overrides\{GUID}\SensorPermissionState

I checked another Windows 8 machine and confirmed that the GUID was the same:
Then, manually changed registry key from 0 to 1 and refresh Location Setting to confirm checkbox was back on.


Once that was done, doing it in PowerShell was quite trivial:

To turn on the Windows Location Platform set value to 1:


set-ItemProperty -Path 'HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Sensor\Overrides\{BFA794E4-F964-4FDB-90F6-51056BFE4B44}' -Name SensorPermissionState -Value 0x1

To turn off the Windows Location Platform set value to 0:


set-ItemProperty -Path 'HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Sensor\Overrides\{BFA794E4-F964-4FDB-90F6-51056BFE4B44}' -Name SensorPermissionState -Value 0x0

2013-01-23

Workaround to get VMWare boot from USB

Plop Boot Manager Menu
This is probably not news to most VMWare users: Even with the latest VMWare Workstation 9 version, there is still no support from VMWare to boot from a USB drive.

The workaround most people use is to download Plop Boot Manager from 'http://www.plop.at/en/bootmanager/download.html', which acts like a bootable CD and gives you a list of menu options to boot from, one of which is 'USB' (see the screenshot).

I must admit that compared to Live CD, .iso boot options, USB booting is less common but you would think the most advanced solution in this space can do that...

2013-01-13

First Few Days with Windows 8

On the first (work) day of the new year, I decided to upgrade my primary work machine to Windows 8. I was not however ready to be 'down' in case things did not work out well, so I did a fresh install on the second drive.

GUI

OK, what's all this fuss about Windows 8 being Vista-2? Reading about lots of complains and all the talk about steep learning curve etc. I was expecting to be frustrated. Instead, I felt quite at home. All the usual keyboard bindings I am used to worked, I was able to quickly find any application I needed to run. Similarly it felt natural to locate power settings to tell windows not to shut itself down when I am not using it.

To be fair, I might have been frustrated if I was put in front of a Windows 8 box without knowing a single thing about it. I knew a couple of things like 'Charm' bar,  how to 'close an application' via mouse gesture,  but that's pretty much it.

I guess Microsoft could have done a better job of training people during set up or by providing some sort of welcome wizard showing people how to accomplish most common tasks to get people going.

APPS & WINDOWS APPSTORE

The first thing I did was to visit Windows AppStore to see what's available. There are bunch of 'apps' in there, though not much really. It felt more like Chrome Apps, which are 'mostly' links to existing web pages. I always like to use keyboard shortcuts, so I installed a couple of apps like "Windows 8 Shortcuts",  I installed.

...

Well, things got in the way and I could not get to finish this blog post as I intended to. I've been using Windows 8 extensively and have a much better notion of good and bad now. But before I list any of them, I would suggest anyone reading this to stop here and take a look at Paul Allen's Windows 8 review. It covers pretty much everything I wanted to touch upon if I had finished this post in the first few days as I initially aimed.

Assuming you've read that review. Here are a few additional notes from me...

WINDOWS DEFENDER

As described here by Microsoft, Windows Defender is essentially the Antivirus previously included in Windows Essentials. Like Apple's XProtect, Microsoft built this feature into the OS.

You can access it by searching it in Applications: Ctrl+Q > Windows Defender

I happen to keep a folder full of malware. When I download any anti-virus app to see how effective it is, one of the things I do is visit that folder and see if real-time protection will detect them. Because I am not really launching any of the files, some of that do not detect them that way. I won't go into details/

Then, I run a full disk scan and check the results. Comodo, for example had detected a file in a different folder that none-other had detected before. I uploaded that file to VirusTotal.com, which runs many common  anti-virus engines and reports the results. Not surprisingly, Comodo was the only AV that reported it: a false positive.

Anyway, Windows Defender detected all of them successfully when I did a full scan, which is good because I had replaced Microsoft Essentials a year ago as it had failed to detect any.

Yet, I may still need to disable Windows Defender and go with a different solution, not because of its detection rate but because I am suspecting that it has a bug that causes Windows Explorer to crash.

MYSTERIOUS WINDOWS EXPLORER CRASHES

After doing an in-place upgrade from Windows 7, I did not notice any issues for a couple of days but then started to notice that sometimes taskbar would just disappear and re-appearing. I know from experience that this happens if Windows Explorer crashes.



When I checked the Event Viewer, sure enough I saw Event ID 1000 Errors in the Application log and a couple of "Information" entries afterwards that told me "The shell stopped unexpectedly and explorer.exe was restarted".

Slowly, I detected a pattern and was able to repro the issue: It would crash when I browsed to some folders where I had tons of files. Antivirus is a suspect in such situations... To see if Windows Defender may be the culprit,

  • I browsed to the problem folder and scrolled down quickly. Explorer crashed.
  • Repeated this a few times and Windows Explorer kept on crashing each time.
  • Then I brought up Windows Defender > Settings > Real-time protection, and Turned it off.
  • Repeated the test above. This time Windows Explorer did not crash.

I am not fully convinced that Windows Defender is causing the Windows Explorer crash yet but it is the primary suspect at this point. I still need to do more testing and possibly take a crash dump to see if there any clues there.

DUAL-SCREEN CHARMING WOES

I have two monitors attached to my machine. One of the features of Windows 8 was native support for multi-monitor situations, yet I am not sure I like the implementation.

My first "annoyance" is with bringing up Windows Charm Bar (Win + C), which is supposed to show up when I am point at the upper right corner edge of the screen. That works, but it is not smooth. Sometimes, mouse will jump into next screen and I have to pay special attention to where exactly I am pointing it to.

The second "annoyance" is about the taskbar, it is mirrored on each screen. It seems redundant to me but I could not yet find an option to tell Windows 8 to display it only on one screen.

SHORTCUTS


You can help yourself a lot by learning a couple of shortcuts. There are tons of lists, tips and tricks out there you can find on the net but here are just the few to get you started. Even, if you insist on not learning anything else, learn these...

On your keyboard:
Windows Key  = Win
Control Key = Ctrl
Alt Key = Alt

Shortcuts:
Win      ->  Simply pressing Windows key let you switch between desktop and Start screen
Win + E -> Windows Explorer
Win + C -> Charms Bar
Win + I -> Windows Settings
Win + D -> Windows Desktop
Win + Q -> Search installed apps
Win + X -> Menu (see screenshot below)

Alt + F4 -> Closes active window, keep doing it and you can even restart windows.

"Win + X" shortcut displays a menu at the bottom left corner of Desktop, where "Start" menu used to be.

The bottom option will take you to Desktop as well.









FINAL THOUGHTS
I personally felt it was well worth spending $40 to get this upgrade. Microsoft is here to stay with us for quite some time and so is Windows. Adapting to new environment is a "built-in feature" of human beings as is resisting to change yet as we learned from Star Trek "resistance is futile" (extra points if you are playing Ingress and you are 'Englightened').

I am sure Microsoft will keep on ironing out pain points for users and make the UI better but no, Metro is not a disaster and as far as I can tell, Windows 8 is an OK release so far.

2013-01-11

Getting Members of Large Groups via PowerShell


PowerShell AD module make checking group membership a trivial task. If I wanted to get the list of members for a group, I can use Get-ADGroupMember cmdlet. Problem with module is that, it's an 'extra' component on top of standard installation and if you will use it, you need to make sure that your script will have access to it. You may be using PowerShell as your logon/start-up script and your machines may not have the AD module. Yes, there are techniques remote-session techniques that can be employed to work around this but most would prefer to enumerate group members some other way.

One common method is using .Net classes, specifically "DirectoryServices.DirectorySearcher". I had a function that was using this class to enumerate group members, something like this:

function Get-GroupMembers {

  param ([string]$group)

  $searcher=new-object directoryservices.directorysearcher   
  $filter="(&(objectClass=group)(cn=${group}))"
  $searcher.PageSize=1000
  $searcher.Filter=$filter
  $result=$searcher.FindOne()
  $members = $result.properties.item("member")
  if ($members) {
    return $members
  }

  return $false

}


I noticed that this stopped working for a specific group, and when I manually looked into the group properties,  I found that "member" property of the group was empty. There was; however, another noticeable property that got populated instead: "member;range=0-1499".

Long story short, when members of a group exceed 1500, AD was populating that property instead of plain 'member' property and I needed to use a different technique called 'ranged retrieval'.

The main change is that we use 'PropertiesToLoad' property to feed $searcher object the range of members we would like to get and loop through them. When we give $searcher object an invalid range, it throws an error at us and we catch that to exit the loop. Here is the modified function.

## Return members of given AD group

function  Get-GroupMembers {

    param ([string]$group)

    if (-not ($group)) { return $false }
  

    $searcher=new-object directoryservices.directorysearcher   
    $filter="(&(objectClass=group)(cn=${group}))"
    $searcher.PageSize=1000
    $searcher.Filter=$filter
    $result=$searcher.FindOne()

    if ($result) {
        $members = $result.properties.item("member")

        ## Either group is empty or has 1500+ members
        if($members.count -eq 0) {                       

            $retrievedAllMembers=$false           
            $rangeBottom =0
            $rangeTop= 0

            while (! $retrievedAllMembers) {

                $rangeTop=$rangeBottom + 1499               

               ##this is how it would show up in AD
                $memberRange="member;range=$rangeBottom-$rangeTop"  

                $searcher.PropertiesToLoad.Clear()
                [void]$searcher.PropertiesToLoad.Add("$memberRange")

                $rangeBottom+=1500

                try {
                    ## should cause and exception if the $memberRange is not valid
                    $result = $searcher.FindOne() 
                    $rangedProperty = $result.Properties.PropertyNames -like "member;range=*"
                    $members +=$result.Properties.item($rangedProperty)          
                   
                     # UPDATE - 2013-03-24 check for empty group
                      if ($members.count -eq 0) { $retrievedAllMembers=$true }
                }

                catch {

                    $retrievedAllMembers=$true   ## we received all members
                }

            }

        }

        $searcher.Dispose()
        return $members

    }
    return $false   
}

2012-11-07

iPhone dead...and back

Today, all of a sudden, the screen of my AT&T iPhone 4S went black and it stopped responding. Hitting home button or pressing and holding 'power' button did not help. I connected it to a power source hoping somehow it just needed some juice yet there was no sign of charging animation on the screen, it was dead.

Then, my colleague suggested the following:
Try putting it in DFU mode to at least see if you can restore it. See instructions below.

  1. Plug your device into your computer.
  2. Turn off the device.
  3. Hold the Power button for 3 seconds
  4. Hold the Home button without releasing the Power button for 10 seconds
  5. Release the Power Button but keep holding the Home button
  6. Keep holding the Home button until you are alerted by iTunes saying that it has detected a device in Recovery Mode

I did and it worked. So, I had my iPhone back but it was as if I just bought it, I had to get my data back as well...

I connected it to iTunes on my Windows 7 x64 and attempted to restore the last backup. I got prompted for the backup password. After entering the password, a process seemed to start but never finished. Instead I kept on getting the following message:
"itunes could not restore the iphone because the iphone was disconnected"

I thought it might have been a bad password, that was not it. Upon entering a different password, iTunes warned me that I had incorrect password.

Then, I wondered if my iTunes was corrupted, that was not it. I downloaded the latest version (10.7.0.21) and chose to fix my version to no avail.

At this point, I was worried that my backup was bad. Instead, it turned out that the fix was to connect the phone to a different usb port. As soon as I switched the usb port, restore started to progress and I got   my settings back.

Restoring did not bring my applications back, but that's easy to fix by choosing the apps to install after launching 'App Store' > 'Updates' > 'Not on This iPhone'

2012-10-29

Can I use PowerShell functions before defining them

Yes and NO!

As I was learning syntax and intricacies of PowerShell, one of the first things I was told was that if I wanted to use a function, I had to define it first, which meant that I had to structure my script so that functions would be on top and the main script at the bottom. Obvious reason is that PowerShell parses the script sequentially starting from top, working its way to the bottom. This fact remains the same.

If you were always using PowerShell ISE and were not calling your script from the command line, this behavior would come as a surprise to you, because PowerShell ISE does not care where in the script the function is located. There is a dated but still a good intro to functions at this PowerShellPro.com link that mentions this but does not clarify the point. You can see that in the comments.

So, should you define your functions before using them? Sure! Do not bet against command line.

2012-09-12

Secrets of PowerShell Remoting

'Secrets of PowerShell Remoting' is the title of an eBook that can be downloaded from PowerShellBooks.com. It's written by PowerShell MVP Don Jones and Dr. Tobias Weltner and is the most detailed of any documentation I have ever seen about the subject.

In a standard domain environment where http is used as the default remoting protocol, it may come down to a simple command that should be run in administrator PowerShell console session: `enable-psremoting`, yet it's dizzying to look at the number of scenarios and steps in the eBook, especially if you want to use https.

Don Jones mentioned that newer versions of the book will be hosted on PowerShell.org.

2012-09-08

Copying recent files with PowerShell

I saw an interesting question in 'Scripting Guys' Facebook group today:

"I want to check which photos have changed or were added after a specific date on my disk, and copy those to the same path on my backup disk.

I managed to filter:

Get-ChildItem -Recurse | ? {$_.LastWriteTime -gt (get-date).adddays(-180)}
but I don't know how to pick each item's folder path and copy.

e.g.:
PS E:\fotos> Get-ChildItem Hornet01.jpg | fl *
...
And this file should be copied to L:\fotos\Hornet01.jpg"

Before I go about how I would solve this problem, I will explain a bit about how I store my pics and how I would copy them to a different drive.

I keep my pics in a network drive that's mapped as Z:, structure goes like this
Z:\Family\Pictures\{Year}\{Year.Month}\{Event or Place}\*.jpg

Note that Z:\Family\Pictures does not change, so we can think it of as the 'root'. Now, say, I wanted to find all the pictures in the last 6 months and copy them to a different drive maintaining the same structure like

Y:\MyPics\{Year}\{Year.Month}\{Event or Place}\*.jpg

For this, I need to do followings:

  1. Find all pictures that have changed in the last 6 months. 
  2. Get the 'unique' parent directory names of those pictures
  3. Replace those directory names with the new ones
  4. Create the destination directories
  5. Copy the files to new destinations

Well, I will talk about how this can be shortened to two steps but stick with me for the time being...

#1 has already been solved:

$recent_files=Get-ChildItem -Recurse | ? {$_.LastWriteTime -gt (get-date).adddays(-180)}


Of course, we could have filtered these if really wanted just pictures using "-filter" option but let's not bother with that for the moment.

#2 Now, we need to get unique directory names:

$unique_sourcedirs = $recent_files | %{ ($_.directory).fullname } |unique


Note that we ended up with a "System.String" type that we are going to manipulate

#3 We need to replace the first part (ie.z:\family\pictures with y:\mypics):

$dest_dirs=$unique_sourcedirs | %{$_ -replace 'Z:\\family\\Pictures','Y:\MyPics'}


Interestingly, you have to escape backslashes with Powershell's escape character, which happens to be backslash as the first part of replace operator needs a 'regular expression' but not the destination.

#4 Create the destination directories. 
If destination directories do not exist, copy operation will fail. And I am not aware of any parameters that would tell copy to create the directories in the way. This is why we are creating them first.

$dest_dirs | % { if (! (test-path $_)) { new-item -itemtype directory -path $_ }}


#5 We are almost ready to copy.
We have the source file list, and we know that destination directories are created now but we need destination paths. We could use the techniques above to get the destination paths.

$recent_files | %{ 
$target = ($_.directory).fullname -replace 'Z:\\family\\Pictures','Y:\MyPics'; 
copy-item ($_.fullname) $target}


I know, repeating code is just ugly, could not we combine the steps? Sure, above steps helps clarify the action items but it could be shortened to this:

  1. Find all pictures that have changed in the last 6 months
  2. For each file, determine the target directory name by replacing the parent directory name with the new name; create the destination directory if it does not exist; copy the file to new destination.

$recent_files | % { 
   $target = ($_.directory).fullname -replace 'Z:\\family\\pictures','Y:\MyPics';
   if (!(test-path $target)){ new-item -itemtype directory -path $target }; 
   copy-item -path ($_.fullname) -destination $target -force -whatif
}

Couple of Notes:

  • All of the above code-snippet is actually a single line if you are typing it in command line, I introduced line-breaks for better readability.
  • Parameter -whatif is only to see what will happen. You should remove it when executing the command
  • Parameter -force is not necessary if there are no files in destination directory.



2012-08-22

OS X 10.8 App Installation and Gatekeeper

OS X 10.8 Mountain Lion is here with us and so is a new security feature called Gatekeeper, which allows a user to only install applications that came from Mac App Store or from sources that have a certificate from Apple.

Gatekeeper options can be displayed by heading over to Settings > Security and Privacy




App Store is great but not so much for Enterprise as it is not realistic to expect in-house developed code to go through Apple every time. So, Developers who signed up with Apple get a Developer ID Certificate that can be used to sign the installation packages. Details of this process are explained here on the Official Apple pages and also here on 'unreleased notes' in layman terms.

If an application is not signed and is not available in the App Store, then Gatekeeper 'may' get in the way in its default form (Mac App Store + identified developers) but there are 'gotchas'.

Should I disable Gatekeeper?
You may be tempted to select 'Anywhere' option in Gatekeeper to avoid the hassle but consider this
Gatekeeper will "only" block the installation of applications downloaded from web.

You will still be allowed the installation of unsigned, non-flat packages:

  • if you already have a package repository on a network or USB etc. and copying from there
  • if you use tools like curl that does not set 'quarantine' flag (see below)
  • if you control click and choose open in Finder.
  • If you launch Installer App (`sudo /System/Library/CoreServices/Installer.app/`) and browse to the downloaded application
  • If you use command line as in `sudo installer -pkg /path/to/package -target /`
So, there are plenty of ways to let Gatekeeper work for you without disabling it.


Quarantine attribute on Downloaded Applications:
When you download any install package from Web with, let's say, Safari, a hidden 'com.apple.quarantine' attribute will be added to it.  You can use `ls -l@` to display those attributes. When you try to install such files, Gatekeeper will block the application!

You can use the following command to remove the quarantine attribute:
'xattr -d -r com.apple.quarantine /path/to/downloaded/package'

This will stop the prompts that tells you what you may already know: "ApplicationName" is from an unidentified developer. Are you sure you want to open it?

2012-08-17

Must watch this!

I had a blog post about time-less articles that I came across on Internet. TED is full of incredibly powerful speeches. Today, I watched some of them and decided to post the links here. This post will serve as a place holder for future video links.


Jill Bolte Taylor talks about 'brain'. It's very moving and enlightning
http://www.ted.com/talks/jill_bolte_taylor_s_powerful_stroke_of_insight.html

Alain de Botton talks about 'success'. What we mean by it now and realities of western-life
http://www.ted.com/talks/alain_de_botton_a_kinder_gentler_philosophy_of_success.html

Alain de Bottom discusses shortcomings of secular life and what it can borrow from religion
http://www.ted.com/talks/lang/en/alain_de_botton_atheism_2_0.html




2012-08-12

Using Mac as the primary machine and fixing Synergy


I had this zen moment the other day, and realized that there is very little reason that's keeping me from using a Mac as my primary machine right now. Although I had my mac up on the second monitor, I realized that I was using it less, as it was not connected to my primary screen.

So, I swapped my Mac and PC. Now, my mac is connected solely to the primary screen in front of me (Samsung SyncMaster 245BW at 1980x1200) with a displayPort to DVI adapter. Unfortunately, that meant that my PC had to be connected using a VGA as that was the only other input. 

My PC is also connected to the 20" ViewSonic VP2030b (1600x1200) on my right via DVI.  I have dual ATI Radeon HD 5700 in CrossFire setup (4xDVI out), so I would like to get a monitor that supports dual DVI input but there does not seem to be many options out there that I like. In fact, I like Dell UltraSharp U2412 the most at this point but still trying to decide.

Anyway,  I did not want to use multiple keyboards and mouses anymore, so I wanted to try the latest version of Synergy (v1.4.9 as of this writing). I used Synergy at work on 10.7 for some time but quickly had to give up on it when I upgraded one of my Macs to 10.8. 

I set it up so that My Windows 7 x64 would be the 'server' and Mac OS X 10.8 Mountain Lion as the 'client'. Setting it up is not really difficult, there is a single synergy.conf file that has to be common to both client and server (see mine below). 

The biggest trouble was that back and forward buttons of my mouse stopped working on Mac side. That was really annoying when browsing web sites and after some google'ing I found out that this was an issue that has been experienced by several others.

Some posts in that google code link put me into the right direction and after some trial and error, I figured out that using 'Windows + [' key was like hitting back button key, and 'Windows + ]' key was acting as forward key.

I solved the problem by mapping mousebutton(4) to keystroke(Meta+BracketL) and mouse(button5) to keystroke(Meta+BracketR). The whole config file is shown below:

section: screens
        AHPC:
                halfDuplexCapsLock = false
                halfDuplexNumLock = false
                halfDuplexScrollLock = false
                xtestIsXineramaUnaware = false
                switchCorners = none
                switchCornerSize = 0
        AHMac:
                halfDuplexCapsLock = false
                halfDuplexNumLock = false
                halfDuplexScrollLock = false
                xtestIsXineramaUnaware = false
                switchCorners = none
                switchCornerSize = 0
end

section: aliases
end

section: links
        AHPC:
                right = AHMac
        AHMac:
                left = AHPC
end

section: options
        mousebutton(4)=keystroke(Meta+BracketL)
        mousebutton(5)=keystroke(Meta+BracketR)
        relativeMouseMoves = false
        screenSaverSync = false
        win32KeepForeground = true
        switchCorners = none
        switchCornerSize = 0
end

2012-08-05

There be dragons


Here is a good one... I happened to be checking one of my yahoo addresses which I rarely use and noticed the following message from 'Blizzard'.

Language is off. You can tell from the first sentence that message is suspicious. Plus, I had never given my yahoo address to Blizzard, so clearly this was a scam. If you hover over the first link, it really points to the Blizzard but the second one was pointing a phishing site they want the person to go. Chrome actually recognizes the site as warns you about it.

Here is the lesson: NEVER click any link without checking the actual URL it's pointing to by looking at the status bar.

Greetings! 

We have already noted that you are trying to sell your personal World of Warcraft account (s). 
Terms of Use 

http://us.blizzard.com/en-us/company/legal/wow_tou.html 

It will be ongoing for further investigation by Blizzard Entertainment's employees. 
If you wish to not get your account suspended you should immediately verify your account ownership. You must complete the steps below to secure the account and your computer. 

STEP 1: ACCOUNT INVESTIGATION 
We now provide a secure website for you to verify that you have taken the appropriate steps to secure the account, your computer, and your email address. Please go to this site and follow the instructions: 

http://us.blizzard.com/support/article/securitywebform (points to the site in the picture. Link removed)

STEP 2: VERIFY YOUR SUBMISSION WAS RECEIVED 
We will contact you with further instructions once we have received and processed your submission. If you do not receive a reply within 48 hours of submitting this form, please resend it from the address listed above. 

Please be aware that if unauthorized access to this account, it may lead to further action against the account. 

Regards, 

Game Master Dunarthra 
Customer Services 
Blizzard Entertainment 
http://us.battle.net/support/en/


Update: I actually saw at least 5 different variations in my e-mail box all trying to entice me to phishing sites. Some of the subjects are below:
Mists of Pandaria Beta Test Invitation WoW MoP Beta
Mists of Pandaria Beta Test Invitation
World of Warcraft Mount: Heart of the Aspects (lol on this one)

Fixing pepper flash issue(?) on latest chrome


Problem:
 The latest version of Chrome (v22.0.1221.0 - I am on dev-m channel) causes some playback issues with flash videos on my pc. Top part of the video is cut diagonally and keeps on flickering. Here is the link to my bug report.

Screenshot shows how it looks on my screen.

Possible Cause:
The new 'Pepper Flash', result of collaboration between Adobe and Google is supposed to be more secure. Latest release of Chrome has it set as the default flash player on my Windows 7 machines and it seems to be the cause of this issue.

Workaround:
Until a fix is available, disabling Pepper Flash seems to be an easy solution.
In my case, typing chrome://plugins showed that I had 3 Flash plug-ins:

  • The new Pepper Flash plug-in
  • The built-in Flash plug-in that comes with Chrome
  • And the Flash plug-in installed separately for IE and other browsers. 

Clicking the details on the right side of the screen and checking the "disable" link under Pepper Flash seems to have fixed the issue for me.


Note: I still see the same diagonal line if I hover over a link item on the page that would normally overlay a menu, so I am actually not so convinced that this is a PepperFlash issue as I originally suspected. See Chromium discussions for further details (link above).

2012-07-20

Upgrading Android Nexus S to Jelly Bean 4.1.1

It's been quite a while since I embarked on getting my Nexus S upgrade to ICS early. I blogged that journey here. For the last 1.5 months I am carrying both my Nexus S and an iPhone 4S, and frankly I prefer using iPhone for battery life if not for anything else. My favorite activity on Nexus S is tethering my iPad Wi-Fi during my work commute. Anyway, iPhone 4S vs Android 4.x Nexus S discussion is for another day.

Today, Google released 4.1.1 for Nexus S (T-Mobile) and it's officially available if you want to download the binaries: http://android.clients.google.com/packages/ota/google_crespo/9ZGgDXDi.zip

I downloaded the zip file and renamed it to update.zip (renaming is optional)
Then connected phone to my pc and transferred the update.zip to SD Card
Powered off the phone
Pressed Power + Volume Up keys to bring the boot menu
Selected recovery
...and wait for it...

No cigar! I got the infamous red triangle with a black  exclamation mark on a green Android picture.

Apparently, one of the updates from 4.0 to 4.0.4 changed the boot files I had for custom booting before.

Interestingly, I found out that I was able to bypass this screen if I keep on trying to press Power + Volume UP key a couple of times (usually the 3rd was the charm).

I then got into the recovery mode where I was presented with the option I was looking for:
"apply update from /sdcard" (to select the option use Power button. Volume up/down to go up/down)

After selecting update.zip, install started and ...



-- Install /sdcard ...
Finding update package...
Opening update package...
Verifying update package...
Installing update...
Verifying current system...
assert failed: apply_patch_space(16570800)
E:Error in /tmp/sideload/package.zip
(Status 7)
Installation aborted.

failed :(

At this point, I thought I might be low on sdcard space and perhaps that was causing this issue. I did some clean up and free space jumped up from 700MB to 5GB. Unfortunately, the next attempt did not result in anything different.

So tried a couple of other options, all failed:

  • Wiped cache partition... 
  • Wiped cache partition + Wiped data/factory reset (which means you lose everything on the phone!)...
  • Wiped SDCard...


At this point, I started looking at Nexus S XDA forums. People suggested using ClockWorkMod to patch. I used it before, it's a great tool but instead I chose to use this trick to get official OTA update from Google. Steps (I changed the order a bit) are as follows:


  1. System settings
  2. Apps > All (If "all" is not displayed, pull the top bar that reads "running" to the left)
  3. Google-Service-Framework
  4. Force stop
  5. Clear Data
  6. Go to Homescreen
  7. System settings
  8. About Phone
  9. System updates (last time of update checking should be before 1970)
  10. Check now
  11. if you do not get the update, go to step 1.


After the second try I got offered the update. Rest, as they say, is history:





I am dying to try this trick, which is clearly quite harmless, on my wife's nexus S but she explicitly prohibited me from installing Jelly Bean / 4.1.1 until I confirm that it had been working without any hiccups on my phone for about a week :)

Now it's time check out  Highlights and read what's new?

2012-07-07

New Features in PowerShell 3.0

MS PowerShell Team blogged about some of the upcoming features in PowerShell 3.0 here.

I think there are some terrific improvements in the syntax that will make PowerShell more consistent or less unpredictable, if you prefer.

In my tests, some of the features are not yet fully working on Windows 7 but once the final release is out, current issues should go away.

Improved Custom PSObject Creation
One of the cool changes is about creating a custom object. It was already improved in Version 2.0 and now it got even better. Here is a common way of creating one:

Powershell 1.0

$MyCustomObject = New Object PSObject
$MyCustomObject | add-member NoteProperty name "Adil"
$MyCustomObject | add-member NoteProperty lastname "Hindistan"

PowerShell 2.0
$MyCustomObject = new-object PSObject -Property @{name="Adil";lastname="Hindistan"}

PowerShell 3.0
$MyCustomObject = [PsCustomObject]@{name="Adil";lastname="Hindistan"}

So basically you are now simply casting 'PsCustomObject' type like any other.

Local Variables with $using
Another feature I liked seeing is about using local variables. I had written a function a while ago to remove an application given its uninstall string. I needed that to remove unwanted VNC installations from an environment.
It goes something like this:
function remove-app {

    ## E.g. $vnc |%{ remove-app $_ '"C:\Program Files\RealVNC\VNC4\unins000.exe" /SILENT' }, where $vnc holds list of machines

    param ($computer="$env:ComputerName",
       [string]$UninstallString   ## Use QuietUninstallString when possible
        )

    $result="Encountered an error, sorry!"
 if (test-connection $computer -count 1) {
     "Pinging $computer successful"
     
     if ($UninstallString) {

          ## we have to use param and argumentlist in the script block to pass variable to the remote computers

      $result = invoke-command -computer $computer { param($UninstallString);&cmd /c $UninstallString} -ArgumentList $UninstallString

          ## New in PowerShell 3.0:

          ## $result = invoke-command -computer $computer { &cmd /c $using:$UninstallString}

     "...Done"

    }

 } else { "Pinging $computer failed"}

    $result

}

You see that invoke-command line over there? I had to use $UninstallString 3 times in that command to pass it to the remove machine. This got much simpler with PowerShell 3.0 with the addition of "$using:" :

$result = invoke-command -computer $computer { &cmd /c $using:$UninstallString}

By the way, I used that function together with another here that returned a list of uninstall strings from the registry:


function get-appsreg {

    param ($computer="$env:ComputerName",

       [string]$product

        )

    $result="Encountered an error, sorry!"

 if (test-connection $computer -count 1) {

        if ($product) {

            ## we have to use param and argumentlist in the script block to pass variable to the remote computers

   $result = invoke-command -computer $computer { param($product);get-itemproperty hklm:\software\microsoft\windows\currentversion\uninstall\* |?{$_.DisplayName -match $product}|Select DisplayName,UninstallString,QuietUninstallString } -ArgumentList $product

        } else {   

   $result = invoke-command -computer $computer { get-itemproperty hklm:\software\microsoft\windows\currentversion\uninstall\*,hklm:\software\wow6432node\microsoft\windows\currentversion\uninstall\* |Select DisplayName,UninstallString,QuietUninstallString } -erroraction silentlycontinue
       }
 }
    $result
}

Count and Length
With PowerShell 3.0 "count" and "length" properties are also becoming universal. I.e. even if an object does not have these properties currently, you will be able to use them in the new version.

Indexing
Similarly, every object is getting 'indexing'. So, using the custom object example above, it will be possible to use the following syntax:

$MyCustomObject[0]   ## returns the object itself. Other indexes return nothing

It does not look useful really but it does improve the consistency!

2012-06-26

Must Read This!

Internet is a beautiful place. Sometimes, you hit an old article that makes you feel like you found a  treasure, it may teach you a great life lesson.

For me, the best way to get back to them is simply link to them in this blog. Here are a few and I intent to update this post with such findings in the future:

  • Submarine - Paul Graham [2005]: This article is about PR firms and how 'news' get planted.
  • Nastiness - Tim Bray [2009]: I admired Tim Bray ever since reading up his take on XML specification, which he co-edited. Here, he tells a story about some nastiness that went on in a Ruby conference, which is totally insignificant, and gives us his recommendation on when to apologize, which is golden!