I'm back from Seattle, and after my outbound trip, the return was uneventful. I'd hoped to come back with plenty of stuff to blog about but most of what I heard that was interesting is still confidential. Before I left I had time to do "the Walk of Pork" and check out some more of Seattle's Pigs on Parade . This one was actually sponsored by Microsoft. Who says we don't have a sense of humour ?
One of the side meetings we had in Seattle was a "Bloggers' town hall meeting", and the usual subjects came up. A commonly asked question goes "If I can't write it on a 'work' blog can I write it on a 'personal' one ?" . "Ben", my blog, stalker mailed Eileen (as none@none.com but we have his IP address), because he didn't think I should post about "business travel rage"; he suggested I get "two blogs; one that deals with IT from a Microsoft angle that I can legitimately read at work and a 'violence' blog where we can all get our daily fix of automatic weapons, the 'red mist'". I'm unapologetic that I don't stick squarely to my technology cluster, but blog interesting things that I come into contact with in my working life. The problem with having multiple blogs is that as soon mention that you work for Microsoft and things that you say will be linked to that if it makes sense. Political issues do have a place on a work blog because policies set by governments affects what we do, but Party politics shouldn't appear on a company platform. - I want to write "Gordon Brown is a moron" or "Gordon Brown is a genius" a private blog would be the place and no-one is likely to run a headline "Microsoft attacks/praises Prime Minister" as a result. However if I were to write either of those about, say, Steve Jobs, you can see the headline "Microsoft attacks/praises Apple Boss" followed by "A Microsoft employee, writing on his blog said ..." Talking to Steve (Lamb - not Jobs) he thought it was a sad that we can't have individual freedom of speech because what we say isn't seen as the speech of individuals. Of course people can blog under a pseudonym (I quite fancy "Borg-Pig 4/19"); but if you have to guard your anonymity that's hardly individual freedom of speech either. There are things which might make good posts which I self-censor but so few could go on an out-of-Microsoft blog that I don't see the point of having one.
It was in the process of explaining the importance of context that a lawyer from our games division told us the following - which he was happy for me to retell. In making modern video games it's now quite common to make clay sculptures of the characters. These characters are dressed in their costume so that the clothes look right - which matters if the digitized image of the figure appears in the final game; underneath the characters are naked. The characters also need to be seen by people from legal, and during the production of Shadowrun they had to check out the figures of Naked Dwarves, Elves, Humans and Trolls. It was in this context that a lawyer came to demand a naked dwarf in his office. Not that the person who overheard him necessarily understood that at the time ...
My experiences with PowerShell continue and every so often realize that I never really understood something I thought I'd "got" some while back
I've spent most of my time trying not to use the WRITE cmdlets to send output to the screen. After all if I want to write "Hello, World" to the screen I can have a function
function hw { 'Hello, World' }
No need for a Print, Write, Echo or any other command: in PowerShell an output with nowhere else to go ends up on the screen. If I want formatted output FORMAT-LIST and FORMAT-TABLE do a great job, especially since I "discovered" calculated fields. Since I always used to get my queries in Access to do loads of work, shoving the work onto FORMAT-TABLE seems natural enough, even if the syntax is little weird. The field is actually written as a hash table @{ label="Text", Expression={some code} }. Of course "Some code" can do anything you want and what it returns goes in the table. So I had a bright idea over the weekend. I can have a menu using format-table. Here the code
Function Choose-Process{$global:Counter=-1$proc=Get-ProcessFormat-Table -inputobject $Proc @{ Label = "ID"; Expression={($global:counter++) }} , processName$proc[(read-host "Which one ?")]
So I set a counter, get a list of processes, and then format a table, in formatting the table I output and increment the counter; the I ask the user to input a number to choose one and that gives me the offset into the array of processes of the one to return. When I run it here's what I get:
PS C:\Users\Jamesone> choose-proc ID ProcessName-- -----------0 audiodg1 CcmExec Which one ?: 1 Handles NPM(K) PM(K) WS(K) VM(M) CPU(s) Id ProcessName------- ------ ----- ----- ----- ------ -- ----------- 744 29 13128 10560 92 2204 CcmExec
ID ProcessName-- -----------0 audiodg1 CcmExec
Which one ?: 1
Handles NPM(K) PM(K) WS(K) VM(M) CPU(s) Id ProcessName------- ------ ----- ----- ----- ------ -- ----------- 744 29 13128 10560 92 2204 CcmExec
Of course I want to save this. So I type in $proc=choose-proc and I get
Which one ?:
What the ... ? Where did my menu go ? And the answer is: the menu was OUTPUT. Where did I tell PowerShell that format-table wasn't to go to standard output but $proc[offset] was ? I didn't so both ended up being stored in the result.
Anyone who's explored PowerShell's providers will have found there is one for variables. Now I get it ... $varName= MyFunction is actually MyFunction > Variable:varName. This behaviour can work for or against you depending on the situation, and here I have to use a for loop and use WRITE-HOST to force output to go the screen [Update: no I don't need to. See Jeffrey's comment below]
I guess I was still thinking in Basic, where the last line of your function usually returns the result. PowerShell's Outputs are different, they go down the "Standard Output" pipe unless told otherwise and standard output ends up on the screen if the "end of pipe" is left Open; just like you'd expect from a Shell. And since I was quoting my university lecturers a little while ago here's something which was written on the board in our first programming class.
It is practically impossible to teach good programming style to students that have had prior exposure to BASIC; as potential programmers they are mentally mutilated beyond hope of regeneration. (Edsger Dijkstra )
Peter sent me a mail last week suggesting a blog post.
"You mentioned in http://blogs.technet.com/jamesone/archive/2007/02/21/the-campaign-for-real-numbers.aspx that a transport rule would be cool. Whislt I'm not yet a E2K7 user, wouldn't it be good if you could knock up a powershell script to create the rule and publish it on your blog?
you get treble value - powershell, exchange, and crappy phone number layout"
I thought I'd try to do three things.
This post just deals with the fist of these.
I wanted two powershell functions. The first takes a String which holds the phone number and re-formats it.
function Format-Number-as-E164 ([string]$Number){if ($number.length -gt 5) {if ($number.substring(0,2) -ne '08') {if ($number.substring(0,2) -eq '00') {$number='+' + $number.substring(2) } if ($number.substring(0,1) -eq '0') {$number='+44 '+ $number.substring(1) } $number = $number.replace("+440", "+44") $number = $number.replace("+44 0", "+44") $number = $number.replace("+44(0)", "+44") $number = $number.replace("+44 (0)", "+44") $number = $number.replace("+44(0", "+44(") $number = $number.replace("+44 (0", "+44 (")} }$number}
The logic is pretty simple:
The second function takes an outlook contact object and calls the first for each of the 13 different phone numbers - here I'm just showing one.
function Format-contact-as-e164 ($contact) { if($contact.CompanyMainTelephoneNumber) {$temp = Format-Number-as-E164($contact.CompanyMainTelephoneNumber) if ($temp -ne $contact.CompanyMainTelephoneNumber) {$contact.CompanyMainTelephoneNumber=$temp $changed=$TRUE} } if ($changed) {write-host $contact.fileAs changed $contact.save() }}
function Format-contact-as-e164 ($contact)
{ if($contact.CompanyMainTelephoneNumber) {$temp = Format-Number-as-E164($contact.CompanyMainTelephoneNumber) if ($temp -ne $contact.CompanyMainTelephoneNumber) {$contact.CompanyMainTelephoneNumber=$temp $changed=$TRUE} } if ($changed) {write-host $contact.fileAs changed $contact.save() }}
So now I need to feed my outlook contacts into this function converting code I've been using for years gives me this - Get Default folder can get your inbox, calendar, contacts and so on. 10 is the code for the contacts folder.
$Outlook= New-Object -comobject "outlook.application"$namespace=$outlook.GetNamespace("MAPI")$myContacts=$namespace.GetDefaultFolder(10).name
Of course this can be squeezed into one line.
$myContacts=(new-object -comobject "Outlook.Application").getnamespace("mapi").getDefaultFolder(10)
I haven't tried it by I guess you could dispense with the variable altogether and update my address book with
foreach ($item in(new-object -comobject "Outlook.Application").getnamespace("mapi").getDefaultFolder(10).items) {Format-contact-as-e164($item) }
Since I didn't trust my code to work first time (nor should you) I did two things to check it worked properly. The first was to do a global replace of {$contact. with {write-host so I could see what it was doing - Powershell syntax is that write-host CompanyMainTelephoneNumber=$temp will treat CompanyMainTelephoneNumber= as a literal string and expand the variable $temp. There are cases where this is infuriating (at least for new converts) but here it's exactly what I want.
When I was satisfied that it was working correctly, I changed the code back and tested a contact which I knew I could fix so I just invoked Format-contact-as-e164( $myContacts.Items.item(1)) when that worked, it was time to go for it and update all the affected contacts with
foreach ($item in $myContacts.Items) {Format-contact-as-e164($item) }
It worked. (Phew). I've attached the .ps1 file with the code in it. As with all code posted to this blog it is provided as an example. It doesn't necessarily represent "best practice" and comes with no warranty or support (so if you want to use it against real data of your own, take suitable precautions).
Since I posted about Parlano, a couple of people have asked me about persistent chat.
So.. (very briefly) most people understand Mail and Instant messaging. IM is usually 1:1, but we can have multiple users in a a chat. One of the major grumbles about LCS 2005 and Communicator 2005 was that when you escalated to Voice or Video chat it couldn't do more than 1:1. This has changed in OCS 2007 with the new communicator client: we have a "hub" component (called an MCU) which brings together the voice and video streams. Although it wasn't strictly necessary when 2007 users go from 1:1 to n-way IM conversation we also use an MCU.
Most people know about Internet Chat rooms; a hub of some kind has virtual "places" - so the room can be "persistent" i.e. it's always there even if no-one is on-line chatting in it. But the conversations are transient. If you're not in the room when someone says it you don't see it.
Persistent chat combines the idea of "Rooms" with the idea the chat itself being persistent - so you can look at what was said before you joined. The screen shot gives you some idea, the "rooms" or "Channels" as the Mind--Align product calls them are on the left, the participants on the right and the chat is in the middle. It's easier to manage and search than a Distribution List or Public folder / Newsgroup where each comment is a separate item. But it's not ad-hoc like an IM chat or chat room.
As I said before I never found myself pining for this functionality, but I know of plenty of customers who did. It will be interesting to see how this integrates with the existing products. It would be interesting too if the next version of Groove linked into both this and traditional IM instead of Groove's silo'd IM.
If you're a regular reader you may know that I've been doing quite a lot of stuff with powershell of late, and Ive mentioned that I'm helping with some of the utilities for the the OCS resource Kit.
One of the questions I got asked was "Can we have a script to look up an SRV record", I thought this was a bit odd because you can do this from the command line with NSLookup although everyone thinks of NS-Lookup as a tool that you user interactively. If you want to look up a record for a service (SRV) you simply invoke NSLookup with a switch and specify the name of the service, e.g.
nslookup -q=SRV _sip._tls.microsoft.com
Next I was trying to test WMI remotely managing a machine and I could not get a connection to the relic which sits in my study running XP, after various searches and much head-scratching I found that if you get the error
Get-WmiObject : The RPC server is unavailable. (Exception from HRESULT: 0x800706BA)
It may be because the Windows firewall is blocking it. To unblock you use the NetSh. command as follows
Netsh firewall set service RemoteAdmin
Finally, what about pinging from a script - when I have trouble with a network connection I run IPConfig. That tells me if I have an IP address and what my DNS servers are. I was taught to ping the default gateway first, but I've always found going straight to DNS was easier.
In Powershell I can get my IPConfig information with Get-WmiObject -Class Win32_NetworkAdapterConfiguration -Filter IPEnabled=TRUE and if I can get the DNS servers out of this information by piping the results to Select-Object -ExpandProperty DNSServerSearchOrder WMI has a PingStatus class so I can use it from Powershell by piping the list of servers into ForEach-Object -Process {Get-WmiObject -Class Win32_PingStatus -Filter ("Address='" + $_ + "'")} and Finally I can display it nicely with Select-Object -Property Address,ResponseTime, TimeToLive
String them all togehter and I get
PS C:\Users\Jamesone> Get-WmiObject -Class Win32_NetworkAdapterConfiguration -Filter IPEnabled=TRUE |>> Select-Object -ExpandProperty DNSServerSearchOrder |>> ForEach-Object -Process {Get-WmiObject -Class Win32_PingStatus -Filter ("Address='" + $_ + "'")} |>> Select-Object -Property Address,ResponseTime,timeToLive>> Address ResponseTime timeToLive------- ------------ ----------194.168.4.100 17 128194.168.8.100 18 128
Address ResponseTime timeToLive------- ------------ ----------194.168.4.100 17 128194.168.8.100 18 128
I have been carrying on with my project to make EXIF data available in PowerShell.
When I searched for Powershell and EXIF it turned up this Blog post of Scott Hanselman's. He talks about a "nice little photo library" that extracts and interprets EXIF data from images, but when I tried to get hold of copy the link was dead, so I thought "How hard can it be ?" and looked the example in the VB Express's help. The answer was "Not very hard", and I built a class library I built goes like this.
Public Class ExifImage ' Declare Constants, variables, subs and functions ' - use PUBLIC to make them available to things which use the class. Public BitMap As System.Drawing.Bitmap ' I have lots of constants and a couple of functions to process information - just one shown here Public Const ExifIDDateTimeTaken As Integer = 36867 ' NEW is invoked when an item of this class is instantiated. Here I want to get a BITMAP object Public Sub New(ByVal s As String) Me.BitMap = New System.Drawing.Bitmap(s) End Sub ' Now declare code to return properties. I don't want to write properties, so I declare them read only ReadOnly Property DateTimeTaken() As String Get DateTimeTaken = {what ever code you need} End Get End Property End class
Public Class ExifImage
' Declare Constants, variables, subs and functions
' - use PUBLIC to make them available to things which use the class.
Public BitMap As System.Drawing.Bitmap
' I have lots of constants and a couple of functions to process information - just one shown here
Public Const ExifIDDateTimeTaken As Integer = 36867
' NEW is invoked when an item of this class is instantiated. Here I want to get a BITMAP object
Public Sub New(ByVal s As String)
Me.BitMap = New System.Drawing.Bitmap(s)
End Sub
' Now declare code to return properties. I don't want to write properties, so I declare them read only
ReadOnly Property DateTimeTaken() As String
Get
DateTimeTaken = {what ever code you need}
End Get
End Property
End class
[reflection.assembly]::loadfile("C:\...longpath...\OneImage.dll")
Now I can do something like this to get an image with my exif properties,
$foo = New-Object oneimage.exifimage -argumentlist "I:\DCIM\100PENTX\IMGP3797.JPG"
PS C:\Users\Jamesone\Pictures> $foo.Flash Flash off
Flash off
That post of Scott's talks about "spot-welding" new properties on to existing objects. Not object inheritance, mind you, "super-gluing." Like it or hate it, like super-glue, you have to respect that it solves problems. I copied his XML wholesale replaced his type name with mine and loaded it with
Update-TypeData My.Types.PS1XML
Which let me get a directory with the DatePhotoTaken field. I got a bit more adventurous and build a different XML file which would show me bytes per pixel - a quick way to find out which files have been heavily compressed and which haven't
<Types> <Type> <Name>System.IO.FileInfo</Name> <Members> <ScriptProperty> <Name>BytesPerPixel</Name> <GetScriptBlock> if ($this.Extension -match "jpg|raw") { $photo = new-object oneimage.exifimage $this.FullName $this.length / ($photo.Height * $photo.width) } </GetScriptBlock> </ScriptProperty> </Members> </Type></Types>
I've attached the VB code and DLL so you can experiment. Disclaimer . Like any code on my blog, this code is provided as an Example for illustration purposes is only. It comes with No support and No warranty that it is fit for any purpose whatsoever. Update - the code has been revised, and the link below has changed
Having mentioned Powershell in several recent posts, I said in passing I'd discovered that it had a Get-EventLog cmdlet
I also talked about the problems I was having with sleep - or more to the point the machine waking up and running till it crashed, I said
"Looking through the event log I found when the machine boots, the event logging service records an Error 6008 - "The previous system shutdown at {time} was unexpected.", if it wasn't shut down cleanly. Now I don't know how it works out the time it happened. There are 5 of these events in the last 40 days. Roughly two hours before each is a message recorded by Power-Troubleshooting . "The system has resumed from sleep. Sleep Time: {time} Wake Time: {time}. Wake Source: Device -ACPI Lid""
In order to do this looking I'd actually exported the information from Event viewer, merged it in Excel and read down it; this process didn't take hours and hours - but it did take a good few minutes. What would it take to get this information in Powershell. I want
So a Where-object cmdlet which checks for these, and takes input from Get-EventLog and sends it to Format-Table should do the trick. It took a moment to find that Power-Troubleshooter was actually Microsoft-Windows-Power-Troubleshooter. And then it's east.
Get-EventLog -logname system | where-object {(($_.source -eq "EventLog") -and (($_.get_eventID() -eq 6005) -or ($_.get_eventID() -eq 6008))) -or (($_.source -eq 'Microsoft-Windows-Power-Troubleshooter') -and ($_.Message -like '*ACPI Lid*')) } | format-table -Property TimeGenerated, message
where-object {(($_.source -eq "EventLog") -and (($_.get_eventID() -eq 6005) -or ($_.get_eventID() -eq 6008))) -or (($_.source -eq 'Microsoft-Windows-Power-Troubleshooter') -and ($_.Message -like '*ACPI Lid*')) } |
format-table -Property TimeGenerated, message
And here's the output. It's pretty easy to see when the system woke up shortly before an unexpected shutdown.
TimeGenerated Message------------- -------19/08/2007 13:25:40 The description for Event ID '1' in Source 'Microsoft-Wi...19/08/2007 12:38:41 The Event log service was started.18/08/2007 00:34:22 The Event log service was started.18/08/2007 00:34:22 The previous system shutdown at 22:33:54 on 17/08/2007 w...16/08/2007 22:05:32 The description for Event ID '1' in Source 'Microsoft-Wi...16/08/2007 20:42:06 The description for Event ID '1' in Source 'Microsoft-Wi...16/08/2007 10:15:40 The description for Event ID '1' in Source 'Microsoft-Wi...15/08/2007 11:31:25 The Event log service was started.14/08/2007 22:35:33 The description for Event ID '1' in Source 'Microsoft-Wi...14/08/2007 06:16:28 The description for Event ID '1' in Source 'Microsoft-Wi...13/08/2007 16:57:21 The Event log service was started.13/08/2007 10:50:57 The Event log service was started.13/08/2007 10:50:57 The previous system shutdown at 10:24:05 on 13/08/2007 w...13/08/2007 10:26:37 The description for Event ID '1' in Source 'Microsoft-Wi...09/08/2007 22:27:28 The Event log service was started.09/08/2007 22:27:28 The previous system shutdown at 22:16:20 on 09/08/2007 w...09/08/2007 21:07:52 The description for Event ID '1' in Source 'Microsoft-Wi...08/08/2007 22:14:21 The description for Event ID '1' in Source 'Microsoft-Wi...08/08/2007 15:02:44 The description for Event ID '1' in Source 'Microsoft-Wi...08/08/2007 14:53:49 The description for Event ID '1' in Source 'Microsoft-Wi...08/08/2007 11:00:40 The description for Event ID '1' in Source 'Microsoft-Wi...08/08/2007 00:10:23 The Event log service was started.08/08/2007 00:10:23 The previous system shutdown at 20:29:48 on 07/08/2007 w...07/08/2007 18:53:35 The description for Event ID '1' in Source 'Microsoft-Wi...07/08/2007 10:07:11 The description for Event ID '1' in Source 'Microsoft-Wi...02/08/2007 18:53:27 The description for Event ID '1' in Source 'Microsoft-Wi...02/08/2007 11:41:22 The description for Event ID '1' in Source 'Microsoft-Wi...01/08/2007 14:53:19 The description for Event ID '1' in Source 'Microsoft-Wi...01/08/2007 14:09:56 The description for Event ID '1' in Source 'Microsoft-Wi...01/08/2007 10:04:26 The Event log service was started.01/08/2007 07:19:30 The Event log service was started.01/08/2007 07:19:30 The previous system shutdown at 20:36:18 on 31/07/2007 w...31/07/2007 18:53:17 The description for Event ID '1' in Source 'Microsoft-Wi...29/07/2007 14:00:22 The description for Event ID '1' in Source 'Microsoft-Wi...29/07/2007 05:42:25 The description for Event ID '1' in Source 'Microsoft-Wi...28/07/2007 02:49:07 The description for Event ID '1' in Source 'Microsoft-Wi...
I said when I posted about fixing the phone numbers in your Outlook Contacts that I'd come back and do the same thing for Active Directory. Richard told people to look out for that, so no pressure to get it done then...
Using the Active Directory Services interface (ADSI) from Powershell seems to fill some people with fear. One of the books I picked up said "don't bother" use VB. I found the Mastering PowerShell in your lunch-break pages quite helpful on ADSI; and there are a bunch of tools worth investigating. I wanted to do this without creating a dependency on those, if I could.
So here are a few bits that might be useful before I dive into the code.
I've kept the same structure as I used in the Outlook Contacts version. I've kept the same function Format-number-as-E164 and I have another function which takes a user or contact object, checks to see if phone numbers are present and if they are, it calls Format-number-as-E164. This follows the same pattern as the outlook one, but is named function Format-ADPerson-as-e164 and takes an LDAP path as a parameter. The AD fields it looks at are facsimileTelephoneNumber, homePhone, ipPhone, mobile, pager, PrimaryInternationalISDNNumber , TelephoneAssistant and TelephoneNumber
Rather than show identical code for all these attributes, I've just shown one below.
function Format-ADPerson-as-e164 ($LDAPpath) { $changed=$false $Contact=[ADSI]($LDAPPath) if($contact.psbase.properties.facsimileTelephoneNumber) {$temp = Format-Number-as-E164($contact.facsimileTelephoneNumber) if ($temp -ne $contact.facsimileTelephoneNumber) {$contact.facsimileTelephoneNumber=$temp $changed=$TRUE} } if ($changed) { write-host $contact.name changed $contact.setInfo() } }
{ $changed=$false $Contact=[ADSI]($LDAPPath) if($contact.psbase.properties.facsimileTelephoneNumber) {$temp = Format-Number-as-E164($contact.facsimileTelephoneNumber) if ($temp -ne $contact.facsimileTelephoneNumber) {$contact.facsimileTelephoneNumber=$temp $changed=$TRUE} }
if ($changed) { write-host $contact.name changed $contact.setInfo() } }
Then it needs to be called for many AD users, By using [ADSI]"", and filtering to the "Person" objectClass, I selected every user and contact in my domain but you could specify a single OU or a more restrictive filter.
$searcher= New-Object directoryServices.DirectorySearcher([Adsi]"") $searcher.filter="(objectclass=person)" $searcher.findall() | forEach-Object {$_.path}
$searcher.filter="(objectclass=person)"
$searcher.findall() | forEach-Object {$_.path}
The last line is there to show which objects are going to be processed. When I'm happy that I've go the right objects and the right code
$searcher.findall() | foreach-object {Format-ADPerson-as-e164($_.path)}
Does the processing.
The code is attached for you to play with, but as I've said before, and will say in the future; any code I provide here is for example purposes. No Warranty or support is provided. You should take steps to validate any such code or yourself before running it.
One of the Microsoft internal discussion lists has had an interesting thread over the last couple of days which started like this. Poster: "I know about bugs in [X], but I work on [Y] and all the objectives I'm measured on are based on getting [Y] right. I can't find time to file bug reports on another teams product." Others: "That's terrible"
And from there the conversation descended into criticism of product [x], the Poster's attitude, the Poster's management's attitude and the bug reporting process, the philosophical nature of objectives, and has now reached the point of someone posting a picture of their cat...
This reminded me of something from Long ago, when I was working in product support for RM, and our Managing Director picked up the Juran Quality Improvement (JQI) methodology used in Japan (he talks about it here). I remember 3 principles of JQI, and I'm convinced there was a fourth but I can't track it it down - probably something about counting defects and trying to get to zero. (I was visiting a high tech customer a couple of weeks back and saw a chart which showed defect rates way below what I'd expect for their business - zero in one recent month - with the words "Defect rates remain unacceptably high" in the text beneath it. That's JQI thinking.) The three principles I remember are:
And of course this last point is the one which links to the discussion on the thread. It's also the most difficult one to implement because, responsibility without power achieves nothing. The easiest way to turn "Quality is everybody's responsibility" into an empty slogan, is to make any action that such responsibility requires a career limiting move. To work, management must shift their thinking to be self critical and when their actions hinder meeting the customer's needs they must embrace criticism. I'm not surprised that (in Britain at least) quality improvement went out of fashion in favour of quality assurance (ISO 9000) systems which assume that management knows the best possible way to do things and what's needed is ensure people follow a process: empowering them to change things or criticize management decisions isn't desirable or even necessary.
Of course the opposite of "Quality is everybody's responsibility" is the Bystander effect or what Douglas Adams called the "Someone else's problem field" (I mentioned this before when talking about mail.). Last week one of the Proxy servers which hooks us up to the Internet had a fault. Steve complimented me on my endurance after I spent 48 minutes on the phone to the "Help desk" to report it. Our help-desk used to be based in Dublin, and it helped. You'd often get someone with a mixed Dutch and Dublin accent (the staff we multi-lingual) and they'd improvise to solve the problem when they needed to. To cut the cost per call the function our helpdesk moved to India, but it's more than the accents that have changed: the staff now won't depart from their scripts, even if the script isn't right for the problem (which I associate with implementations of ISO9000 )
In the old days I would have called Dublin said "When I try to go some web sites I get an error that says Proxy server 11 is out of space" and they'd have answered "We'll send yer-man Dermot down there with a big a hammer. Try again in a few minutes". Now I call Bangalore and someone opens a service request and launches into the "Problem accessing the Internet" Script. He remotes into my machine, relaxes my security settings, turns off auto proxy detection (so IE won't work at home), disables third party add ons, and finds that the proxy server is still giving the same error. So he mails himself the results of IPconfig and tracert. He's amazed when I use "Mode Con COLS=132" in a command window to stop the text from wrapping (which makes me wonder about his ability to go off-script). Normally I'd commend someone for spending 48 minutes trying to find problem on my machine - except I told him the first 48 seconds the error came from the Data centre and affected other people. When he's done I have to put my machine back to its working state and close the service request. Total cost in my time - about an hour. I still price of my time at the billing rate it was when I was a consultant 1 hour = £200.
This experience isn't unique: When a I called to get a disabled network port re-connected, the help desk pulled out the "Network connection problem" script and asked my IP address. I couldn't get an IP address because the port was disabled. "Oh". They said can we have the MAC address then. I gave it to them. "We can't see that address". "No" I said, "When I plug into the port on this desk the lights on my network socket don't light up, the port is dead. How did you expect to find me on the network."
A colleague's SmartCard split: the chip looked OK but his card wouldn't work in any machine, other cards worked in his machine. He called to find out how to get a replacement Dublin would have said "Nip down to the security office and they'll get a new one for you". Bangalore got out the "smartcard problem" script and walked him through re-installing the Smartcard Crypto provider, drivers for the reader (he protested every step of the way that the problem was physical damage to the card). After an hour the call went to second line support (who eventually told him how to get a new card).
Of course Microsoft isn't the organization where people are getting experiences like these. They teach us don't call the help desk; problems affecting shared systems are someone else's problems, and if it really is your problem, find someone on your team to help. With fewer calls and each costing less offshoring the help-desk looks like a success. Unless you look at the meeting the needs of the helpdesk customers (like me).
Adam sent me a mail, saying that he noticed my gripe about Live Writer: that it's behaviour around words like colour was likely to antagonise* English people (it would say it's behavior around words like color was likely to antagonize*). Thanks to him I found a fix. Adam says that the handling of the personal dictionary in Live writer is sloppy - It seems to be fixed in the build I'm running.
* It always surprises me how few British English writers know that it is perfectly acceptable to write Antagonize. Here's what Gowers says in "Plain Words"
On the Question of whether words like Organise or Organisation should be spelt with an s or a z, authorities differ. There are some words (e.g Advertise, comprise, despise, advise, exercise and surmise) which are never spelled with a z. There are others (such as Organize) for which the spelling with z is the only American form , and is also a very common British one. This being so the British writer has the advantage over the American in that we may, if we wish, use an s all the time, for that will never be wrong, whereas a Z sometimes will be. But do not condemn those who use z in it's right place.
Windows Live writer is proving popular. I've been using it in beta for a year and there are only two things that I don't like
The Auto-save is matters at this point, because in place of the witty and erudite post I was writing yesteday (and I was borrowing from something I had listened to from Clive James) you have this one.I never turned Auto-save on. And last night when unzipped my bag, I could smell that my post was toast. Toast being the key word here - though the smell was not the comforting one of bread browning, but the distinctive tang of hot plastic. I knew before I touched the computer was hot. With no LEDs on there was a slim chance the computer had hibernated but I've seen this before it came as no surprised it had run that battery to zero. Recently, too judging from the heat - I was concerned that it might have damaged the machine made me a little concerned for it (doubly so as I hadn't removed the secondary drive after doing my last backup). Since the laptop had been in my car for last 3-4 hours and most of that time the car had been parked on the driveway, and this isn't the first time it's happened I want to know what's going on here. According to the event log it woke up at 18:53 and this was triggered by the ACPI Lid. What's odd about this is that the laptop doesn't power up when I open the lid I have to press the power button. Worse, closing the lid sends it to sleep ONLY when running on battery.
Looking through the event log I found when the machine boots, the event logging service records an Error 6008 - "The previous system shutdown at {time} was unexpected.", if it wasn't shut down cleanly. Now I don't know how it works out the time it happened. There are 5 of these events in the last 40 days. Roughly two hours before each is a message recorded by Power-Troubleshooting . "The system has resumed from sleep. Sleep Time: {time} Wake Time: {time}. Wake Source: Device -ACPI Lid"
I'd love to hear from anyone out there who has a Dell Latitude D820 which seems to have a problem of this kind. One hypothesis I'm working on is that this a scheduled service is waking the computer up (maybe to record something a 7PM) and the switch message is spurious, and it may be that if the machine wakes with it's lid closed something about the state of the lid switch then prevents it from sleeping - whether this is a bug or a hardware fault.
Feels like I've got to do a load more investigation before I even call the help desk.
Update. After a quick swap of hard disks between my laptop and Steve's the failure to sleep on closing lid when powered follows the drive not the chassis. Vista is definitely set-up to sleep in both states. But it's something on the hard disk .
Since I was talking about experiences from long ago in my previous post, here's another. When I was at University one of my tutors, Dr Brian Lings, used the word "Paradigm" in a lecture. And we all went "WHAT !", and went running to dictionaries to find out what he meant. He actually used in the context of looking at something written in one language but actually having the way-of-doing-things that you'd see in another.
I was reminded of this when I was asked to help with some powershell scripts for the OCS resource kit. I was sent a script which looked like this.
# Get all SIP domains $SIPDomains = Get-WmiObject -class MSFT_SIPDomainData # For every SIP domain in environment write SIP domain info to standard output foreach ($SIPDomain in $SIPDomains) { write-host "*******************************" write-host SIP domain: $SIPDomain.Address write-host "*******************************" write-host `t Default Domain : $SIPDomain.DefaultDomain write-host `t Authoritative : $SIPDomain.Authoritative }
$SIPDomains = Get-WmiObject -class MSFT_SIPDomainData
# For every SIP domain in environment write SIP domain info to standard output
foreach ($SIPDomain in $SIPDomains) {
write-host "*******************************"
write-host SIP domain: $SIPDomain.Address
write-host `t Default Domain : $SIPDomain.DefaultDomain
write-host `t Authoritative : $SIPDomain.Authoritative
}
Getting the WMI Object class MSFT_SIPDOMAINdata, gives a collection of Objects: one per domain. So we store that collection in a variable and use a for loop to go through each object and write its properties to to the screen. Simple
Well except for that fact that write-host can't be redirected to a file. We can't use Get-SIPDomains > domains.txt.
But even fixing that this isn't powershell "style" - though I'm still learning what that is, exactly. It's written like we'd write VB.
Using LISP at university meant that grew to I like one function to feed into the next, so piping functions into each other seems natural and powershell gives me cmdlets like format-table to take the results of Get-WMIobject and reduce the For loop to one line.
Get-WmiObject -class MSFT_SIPDomainData | Format-Table Address, Authoritative, DefaultDomain.
I'm beginning to see why they named it POWER shell; one of the other scripts I need to write for the OCS resource kit needs to get information from the event log. I was presently surprised to find PowerShell has a Get-EventLog cmdlet. With Where-Object to filter the events I can do this in one line.
I had a mail from the Unified communications team over the weekend. They were happy to announce that the Microsoft Office Communications Server 2007 Trial Downloads (both Enterprise Edition and Standard Edition Servers) together with their associated client, the Office Communicator 2007 Trial download are now available. The OCS resource kit is still being worked on - I've proof-read some bits of it - but an early cut of the tools is also available. So is the Software Development Kit.
Initially these trials will be English only, the plan is to make Japanese, German, French, Spanish, Italian, Korean, Traditional Chinese, Simplified Chinese and Brazilian Portuguese versions available on September 24th. Like all the best laid plans they are subject to change.
The idea of a trial is to help customers to evaluate the features, capabilities and scenarios delivered by these products in a lab environment. The evaluation code will expire after 180 days, if you were to use it production it is possible upgrade it with an appropriately licensed version of the product. We have public forums where customers can post questions and feedback.
I realize that voice / telephony integration is a big piece of the new product and not many organizations have a PBX in their labs. But the what's new guide runs to 15 pages which aren't all telephony or greater finesse in Communicator . New scenarios worth looking at include notably extranet connections, finer grain permissions, IMing distribution lists and On-premise conferencing; if you want to test the latter to full there is a download of the Web conference scheduler. To save yourself a lot of individual downloads, I'd recommend the documentation roll-up too.
By the way: if you're in the UK and you click through to the Office Communications server home page (http://www.microsoft.com/office/livecomm) from any of the links above you'll fetch up at a LCS 2005 page not an OCS 2007 one. The usual course of action in this case is either
I mentioned the post that I lost last night and that it referred to Clive James, a writer whose words I can hear as I read them. I can't think of many others like that: I hear Bill Bryson's words in the on-the-brink-of-mania tones with which Kerry Shale read them on the radio, rather than Bryson's easy paced burr.
When I read a transcript of James' essay "Smoking, my lost love" from the series "A point of view" I could hear it in my head. This, I thought, is someone whose blog I'd subscribe to, if he had one - which he doesn't. He does have a web site and I found myself in the audio section listening to a piece called "Insult to the Language" It shows a side of him which these days we are supposed to call "pro-detail" for fear that the a constable from the Political Correctness police will whisk us away if we describe someone as "pedantic", but nonetheless it has some quotable passages which show that his best writing is on the subject of the writing of others. For example
"Such blunders... drive the reader to re-work the sentence himself before he can figure out what the writer must mean. When the writer is getting all of the fee, and the reader is doing at least half the labor the discrepancy can cause resentment.......There is a brand of Lumpen prose which ... weighs like lead because the reader continually has to join in the writing. " and quoting an example "This is just a mass of raw material waiting for the reader to make something of it"
{Perhaps I should point the irony of quoting this after the passage I pasted into my previous post, lest someone else should point it out for me} But this idea of sending a "mass of raw material" for the reader to sort out is what I referred to in Why am I the one doing this as "Bad E-mail. Why am I the one who has to organize the writers thoughts into something coherent ?"
To show how sloppy writing distorts meaning, he talks about metaphors which people use incorrectly because they do not understand the practice which gave rise to them "He shot himself in the foot" James tells us "Originally referred to a solider in the great war, who hoped that a self inflicted wound would buy him a ticket out of the trenches. Perhaps because of the irresistible mental image of a western gunslinger pulling the trigger while getting his revolver out of its holster, the metaphor is nowadays almost universally used to evoke clumsiness, rather than cowardice" . Rather disappointingly neither of the editions of Brewer's Dictionary of Phrase and Fable that I have at home has this phrase in it.
Of course I'd need something newer than my 1980's copy of Brewer's to look up a term like Sockpuppet.
A little time ago I was writing about Open XML and the process of moving from an ECMA ratified standard to an ISO modified one; it was something that was in the news and I felt I had something I wanted to say on the subject. This week an update reached the mail boxes of many Microsoft Bloggers, with a suggestion that we might want to blog about it. I don't mind if someone mails me to say "we've got an event which may be of interest to your customers will you spread the word ?". I have more trouble when it's a news story where I don't really have an angle. The news in this case, and it is good news for Microsoft, is that the Commonwealth of Massachusetts now support both Open XML and ODF formats. Great! You can read their comments about the process they went through here - with a link to the comments they received and the policy itself, though if I am to be honest, I really hope you have something better to do with your time. (Reading and listening to Clive James for example.)
I talked to Steve about it. "If we all blogged it at the same time wouldn't we look like a bunch of Sockpuppets" I said. To my surprise an upto-the-minute, on every-social-network, sidewalk-surfer-dude like Steve didn't know the term ,so I checked with both Wikipedia and Wordspy. The thing about a sockpuppet is that it is a false identity, I was spreading the term in the wrong sense - in just the kind of way that Clive James had talked about.
So having us all blog about it at the same time would make us ... well I don't know what the term is (suggestions please. Any along the lines of "Typical Microsoft employees" won't be accepted.). Of course I'd never blog about the subject. Oh no. Perish the thought. I might mention it in passing to illustrate something rather more literary, but that's completely different :-)
I was talking to a customer (I know he reads this blog, so no names). He was having some trouble because two parts of Microsoft weren't communicating the way you'd expect them to. And I said in passing that one thing which we are unjustly accused of from time to time is underhand collusion: for example product "A" supposedly has some secret API that only the people working on product "B" know about. What we actually do is a bit like that scene at the very end of "Raiders of the lost Ark" where the Ark is hidden by placing it in crate and stored with thousands upon thousands of similar crates. Whilst I can't say for sure that never happened, people who believe "secret collaboration" is widespread really don't understand what Microsoft is like inside. You'd hope that all Microsoft web properties would:
But lighting up the search box needs a <link rel="search" ... > tag on each page; and some parts of the empire are starting to get the message. I was pleased when I saw IE's search drop-down go orange when I was on the support site, clicking it show the "This site only search"
.
But I'll bet something from my "Swag" cupboard that we don't have this on the Microsoft home page before Christmas. (If we do - first person to send a mail to me pointing it out gets the prize) .