and other brilliant error messages

I recently bought a Microsoft Surface and I have been wanting to watch a few programmes on BBC iPlayer whilst out of the country for Christmas. I discovered Tunlr – a free media proxy service which allows access to Hulu and iPlayer regardless of geolocation. However, editing DNS server settings by hand is time consuming and awkward without using the trackpad, so I wanted a script to automate the task. This will also work for other proxy services such as unblock-us.com – just replace the DNS IPs in the script. I had previously written a quick script for changing IP configuration which used netsh commands but these don’t work on Windows RT. Some other PowerShell methods I found weren’t supported either but I did find new network settings cmdlets for the purpose that were added in Windows 8/RT.

The next problem was elevation to get sufficient rights to change the network settings. It transpires that the PowerShell and VBScript environments are heavily restricted in Windows RT, which prevents auto-prompting for elevation. Fortunately Windows RT does allow Run as Administrator from the right-click menu for .cmd scripts. If you’re using touch control, you just touch and hold then release for the right-click. The script will remind you if you forget to do this. Hover your mouse over the top right corner of the script below, and use the View Source button to save the following to your desktop as Tunlr.cmd:

I recently needed to make an interactive batch script elevate for admin privileges. I found an example script by jagaroth, and then refined it to make it even more compact. It only writes out one temporary script file, and passes the rest of the required variables on the command line. It can cope with paths containing spaces. It was something of a shell escaping nightmare as you can see from line 14!

This method will build FFmpeg 2.7.1 with the shared libraries (linked with relative paths) required for Serviio DLNA media server, using a Ubuntu/Xubuntu Desktop 14.04 virtual machine, however the same method probably works with many other Linux distributions. The target CPU architectures are ARMv5, ARMv7, ARM hard float ABI with NEON, Intel i686, Intel x64, and PowerPC e500v2.

The >10MB size of the static executable is kind of getting out of hand especially on embedded systems with very limited RAM. Using shared libs means many concurrent instances of FFmpeg can use broadly the same memory footprint, and package distribution binaries can be smaller – our target systems already have libmp3lame, libm, libz, librt, libgmp, libssl, libcrypto, libfreetype, libexpat, and libpthread which add up to several megabytes. I’m also guessing that the OS will decide when to unload them from RAM which could help when FFmpeg is being launched repeatedly in a short period of time (e.g. during library scanning).

The notes below also detail how to compile the Synology fork of FFmpeg 2.0.2 with Intel Evansport SMD (Streaming Media Drivers) hardware transcoding support. This source is itself based upon a fork by Intel which seems to form part of the Evansport SDK. It was not straightforward to compile, and now that I have released Serviio for Synology with hardware transcoding support I’m sharing my method in the hope it can help others. This older FFmpeg needs a patch to fix a long standing bug with DTS audio remuxing (reported by me) where the stream ID was incorrectly set:

Although the unmodified FFmpeg source code will compile successfully for PowerPC CPUs, the FFmpeg binary will core dump when running any command on a video file. I contacted Synology Support for help with this issue back in October 2012 and was given a patch to use at that time which changes the use of a CPU register in the DSP assembly code. The FFmpeg source code has changed a bit recently meaning the patch was no longer valid, but I was able to discover the appropriate location in the code and create a new working patch (code was moved from dsputil_ppc.c to blockdsp.c):

Here’s how the library dependencies look. There are no links to any libraries in /opt, and DSM-bundled libraries in /lib are linked to where possible. All the other new libraries are kept within the Serviio lib folder (../lib relative to the ffmpeg binary).

Bliss is a Java application written by Dan Gravell which can manage file names, tags and album art for your music collection and which can also enforce their consistency. It is designed to be left running once installed so that albums you add later will have these same policies applied to them automatically. It supports a wide range of music formats, and effortlessly deals with very large collections – apparently even ones containing quite obscure recordings. My own collection didn’t really put this to the test, since it doesn’t contain bootlegs, live sets or rarities plus I had also already obtained cover art for it (from back when CoverFlow first graced the screen of my iPhone 2G).

I could see from referrals to this blog that people were asking Dan for a Synology package which he didn’t have the time to investigate, and so I thought that it would make an interesting little project, especially since a NAS is the ideal device to run Bliss on. Although there was already a Howto post on the Synology forums, that guide only really covered getting the basic functionality of Bliss up and running – it was missing the best bits: the filesystem watching and audio fingerprinting features. These depend on natively compiled binaries, which Bliss doesn’t include for ARM or PowerPC CPUs. Getting these working provided precisely the sort of challenge I like. Not only were they difficult to compile, but getting them integrated into the various OSGi ‘bundles’ that make up Bliss was quite involved too.

Bliss uses an open source library called Chromaprint, itself part of the wider Acoustid project. The aim is to scan an audio file, to produce a fingerprint of the sound, and then to compare this against an open online database such as MusicBrainz.org to identify music regardless of compression codec used. Its author Lukáš Lalinský explains how it works.

Synology Package Installation

The repository will push its certificate automatically to the NAS, which is used to validate package integrity. Set the Trust Level to Synology Inc. and trusted publishers:

Since Bliss is a Java application, you will need to install one of my Java SE Embedded packages first (Java 7 or 8) if you have not already done so. Read the instructions on that page carefully too.

Now browse the Community section in Package Center to install Bliss:
The repository only displays packages which are compatible with your specific model of NAS. If you don’t see Bliss in the list, then either your NAS model or your DSM version are not supported at this time. DSM 5.0 is the minimum supported version for this package, though you will need DSM 6.0 or later for audio fingerprinting support.

When the Bliss package is running you can manage it using the icon in the main DSM application menu using the button in the top left corner:

Package scripts

For information, here are the package scripts so you can see what it’s going to do. You can get more information about how packages work by reading the Synology 3rd Party Developer Guide.

20160606-0011 Substantial overhaul for DSM 6.0, incorporating many enhancements developed for other packages, updated to Bliss version 20160606, DSM 6.0 newer is now required for audio track fingerprinting (fpcalc is compiled to depend on ffmpeg 2.7.1), added support for several newer Synology products, improved accuracy of temp folder detection, in-app updating should also be fixed

20150522-0010 Substantial re-write (hence the long delay):
Updated to Bliss version 20150522
DSM 5.0 newer is now required (fpcalc is compiled to depend on FFmpeg 2.0.2)
Now that Intel systems running DSM 5.0+ use a newer glibc, replacement Intel binaries are no longer needed
Added support for Mindspeed Comcerto 2000 CPU in DS414j
Added support for Intel Atom C2538 (avoton) CPU in various models
Added support for ppc853x CPU in older PowerPC models
Added support for Marvell Armada 375 CPU in DS215j
Added support for Intel Evansport CPU in DS214Play and DS415Play
Switched to using root account – no more adding account permissions, package upgrades will no longer break this
DSM Firewall application definition added
Tested with DSM Task Scheduler to allow package to start/stop at certain times of day, saving RAM when not needed
Daemon init script now uses a proper PID file instead of the unreliable method of using grep on the output of ps
Daemon init script can be run from the command line
Switched to .tar.xz compression for native binaries to reduce web hosting storage footprint
Improved accuracy of temp folder detection
Package is now signed with repository private key
User Agent customization while downloading Bliss package from blisshq.com to allow download stats gathering

20130213-0009 Updated to Bliss 20130213, and will correctly report version in Package Center after an in-app update

20130131-0008 Updated to Bliss 20130131

20121112-0007 Fixes for DSM 4.2

20121112-006 Updated to Bliss 20121112

20121019-005 Updated to Bliss 20121019

20121002-004 Updated to Bliss 20121002

20120830-003 Added support for Freescale QorIQ PowerPC CPUs used in some Synology x13 series products, PowerPC processors in previous Synology generations with older glibc versions are not supported

Build Notes

Chromaprint uses some complex maths functions that FFmpeg can provide (specifically Fourier Transform), and FFmpeg’s shared libraries are already included with Synology DSM. Building Chromaprint linked to those existing libraries results in a minuscule 78KB build of fpcalc, rather than the statically compiled ones for various OS and CPU architectures included with Bliss, which weigh in at several megabytes each. I think I’m finally ‘getting’ what open source is all about, which is nice since that was my objective in experimenting with my NAS. To prevent fpcalc building and linking to its dynamic library libchromaprint.so and to get it to detect FFmpeg properly I had to carefully inspect the Makefiles to find the correct build syntax:FFMPEG_DIR=${TOOLCHAIN} cmake -DCMAKE_BUILD_TYPE=Release -DBUILD_EXAMPLES=ON -DBUILD_SHARED_LIBS=NO .
FFMPEG_DIR is the base folder from which CMake will look for lib/libavcodec.so and include/libavcodec/avfft.h.

For watching the filesystem Bliss uses JNotify to hook into the Linux kernel’s inotify subsystem. Getting this compiled was tricky. It seems no one has reported successfully compiling it for an ARM CPU, and JNotify’s author Omry Yadan wasn’t aware of anyone doing this either. The problem is that compilation halts with these errors:

In file included from ../net_contentobjects_jnotify_linux_JNotify_linux.c:43:
../inotify-syscalls.h:35:1: error: "__NR_inotify_init" redefined
In file included from /opt/include/sys/syscall.h:25,
from ../inotify-syscalls.h:4,
from ../net_contentobjects_jnotify_linux_JNotify_linux.c:43:
/opt/include/asm/unistd.h:344:1: error: this is the location of the previous definition
In file included from ../net_contentobjects_jnotify_linux_JNotify_linux.c:43:
../inotify-syscalls.h:36:1: error: "__NR_inotify_add_watch" redefined
In file included from /opt/include/sys/syscall.h:25,
from ../inotify-syscalls.h:4,
from ../net_contentobjects_jnotify_linux_JNotify_linux.c:43:
/opt/include/asm/unistd.h:345:1: error: this is the location of the previous definition
In file included from ../net_contentobjects_jnotify_linux_JNotify_linux.c:43:
../inotify-syscalls.h:37:1: error: "__NR_inotify_rm_watch" redefined
In file included from /opt/include/sys/syscall.h:25,
from ../inotify-syscalls.h:4,
from ../net_contentobjects_jnotify_linux_JNotify_linux.c:43:
/opt/include/asm/unistd.h:346:1: error: this is the location of the previous definition

By searching in a very generic way for a solution I found this post on stackoverflow.com which helped me to patch the header inotify-syscalls.h to get around the problem. Since there was no JDK for ARM embedded systems at build time I included the headers from OpenJDK6 which I took from a Ubuntu VM.

Compiling for Intel also required a fix. I was getting the same error as featured in this post on the JNotify user forum on SourceForge.net:expected specifier-qualifier-list before ‘pid_t’

Despite some people’s apparent success simply rearranging the order of the includes in net_contentobjects_jnotify_linux_JNotify_linux.c this didn’t help me. I’m not sure quite how I stumbled upon it, but I found the solution staring at me on this page:The size_t, ssize_t, uid_t, gid_t, off_t and pid_t types are defined as described in sys/types.h

I inserted an additional include in for sys/types.h and it compiled ok. It’s worth pointing out that, although all Intel Synology units are x86-64 architecture, the Oracle JRE for Embedded is x86 (32 bit), so I used the i686 toolchain. Synology DSM’s FFmpeg shared libraries are also 32 bit so my build of fpcalc needed to comply with this. Bliss nonetheless expects the binary to be called fpcalc_linux64 since Bliss is detecting the underlying Linux CPU architecture.

Once I had got past the obstacle of compiling the native code, I needed to liaise back and forth with Dan to understand how Bliss was dealing with its libraries and how I could replace the built-in versions. Originally this was quite a kludge but Dan has since abstracted out the native binaries into their own OSGI bundle fragments which makes things a lot easier, and allows Bliss to survive in-app updates until those native components are superseded. The Synology package provides the following architecture-specific jar files (with corresponding edits to their manifest) which contain the fpcalc binary from Chromaprint. Thank you to Dan for all the quick answers!

This script will build Windows PE 4.0 (for x86, or AMD64 or both) including scripts and drivers of your choosing, it will create ISO images with both BIOS and UEFI support, and will also upload the resulting WIM boot images to your WDS server automatically (and freshen them if they have been re-created). This reduces the tiresome task of boot image maintenance to just a couple of clicks.

It uses only the standard Microsoft Windows ADK tools, which is the new name for WAIK. Just save the code below as Build_WinPE.cmd and right-click on it to Run as Administrator. Notice the defined variables at the start, particularly the %SOURCE% folder. It supports using either the 32bit or the 64bit ADK, and only the Windows PE and Deployment Tools ADK components are required. The script expects the following folders:

Notice the optional components section at lines 90-95. Modify this if you need your image to contain additional items, for instance PowerShell or .NET Framework 4.

One further observation is that Macs don’t seem to be able to boot this version of Windows PE. I’m not sure whether this is a GOP display driver issue, or whether only true UEFI firmwares are required (Macs are EFI which is an earlier specification). To carry out an unattended Windows 8 install on a Mac via BootCamp you will need to build a Windows PE 3.0 ISO since Macs can’t PXE boot.

For many years I have used scripts of my own design to build workstations and to roll out software updates. At the time I created these I found that most of the tools which could accomplish these tasks were unwieldy. Group Policy software deployment in particular never really seemed fit for purpose since it extended login times so dramatically. My experience gained in a previous job spent packaging applications for deployment had taught me that all installed software populates consistent information in the Windows Registry, so in my current job I tended to audit this data directly via my scripts. This was saved into an SQL database from where it could be queried, or manipulated via a data source in Excel.

I’m working my notice period at the moment ready for a new job I’ll start in October, and so I’m going over the stuff I have created in the current job in order to prepare my handover documents. Mindful of the dependency my current employer has on these custom scripts I decided to get a quote for a Dell KACE solution, thinking that since it’s a Virtual Appliance, and since there are only 150 PCs here it shouldn’t be too expensive – after all it’s only really providing what my scripts already do (workstation builds, drivers, software deployment, and auditing). But here’s the thing – they wanted something like £13,000! (I can’t recall the precise figure). To put it in context this figure is around one third of the cost of replacing all the workstations with new ones, or say half the annual salary of an IT support technician – quite out of the question.

Unsurprisingly I have decided instead to simply tidy up my scripts to make them easier to use. Sure, you could accomplish these tasks with SCCM but that’s not free either. In an SME, why spend huge amounts of money on something that can be automated without much trouble using mechanisms that are built in. Heck, even the uninstall command line is stored in the registry for virtually all software – that’s how the Add/Remove Programs Control Panel works! And most software can be installed silently in the desired way provided you research the command line arguments to do so. It’s no accident that AppDeploy.com which was a great crowdsourced repository of this knowledge became KACE which was then acquired by Dell. It still exists, though the content doesn’t seem to be as well maintained as it was.

I have used a startup script written in VBScript to keep software up to date on workstations. A startup script runs as the SYSTEM account so permissions are not an issue. Since I also maintain an unattended installation I already have a package folder with all the scripts to install each package. All I needed to code was a way to audit the Registry for each package and add some logic around that. Up until now, I had tended to write sections of the script specifically tailored for each package, and from there it’s not much of a stretch to apply packages to a workstation based on its OS version, or Active Directory OU or group membership. For the script I have published below, I have recreated this logic as a single function which can be invoked with a one line entry for each package (see the highlighted part) – everything else is taken care of. I hope it helps someone to save £13,000 :)

The script

'startup.vbs
'patters 2006-2012
Option Explicit
Dim objNetwork, objShell, objReg, strKey, colProcess, objProcess, arrSubKeys
Dim strFileServer
Const HKEY_CURRENT_USER = &H80000001
Const HKEY_LOCAL_MACHINE = &H80000002
'set up objects
Set objNetwork = CreateObject("WScript.Network")
Set objShell = CreateObject("WScript.Shell")
Set objReg = GetObject("winmgmts:{impersonationLevel=impersonate}!\\.\root\default:StdRegProv")
strFileServer = "YOURSERVERHERE"
MapNetworkDrive "U:","unattended"
Package "flash.cmd", "Adobe Flash Player", "11.4.402.265", "u:\packages\flash\uninstall_flash_player.exe -uninstall", False, True, "iexplore.exe"
Package "paintnet.cmd", "Paint.NET", "3.60.0", "/qb-!", False, False, ""
Package "adobe.cmd", "Adobe Reader", "10.1.4","/qb-!",False, False, array("outlook.exe","iexplore")
Package "photogal.cmd", "Photo Gallery", "16.4.3503.0728", "/qb-!", False, False, "iexplore.exe"
Package "mendeley.cmd", "Mendeley Desktop", "1.6", "/S", True, False, "winword.exe"
objNetwork.RemoveNetworkDrive "U:", True, True
WScript.Echo VbCrLf & "Finished software checks"
Function Package(strPackageName, strTargetDisplayName, strTargetVersion, strExtraUninstParams, boolExtraUninstQuotes, boolUninstForceOverride, ProcessToKill)
'=============================================================================
'To understand this function you need to know that installed software packages
'will populate keys below these branches of the Registry:
' HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall
' HKLM\SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall
' (the latter for 32bit software on 64bit Windows)
'This is the data that is mined when you look at Add/Remove Programs
'in the Control Panel
'strPackageName is the package script on your package server (e.g. flash.cmd)
'strTargetDisplayName can be a full or partial match of the Registry key
'DisplayName (matches from the left)
' "Java(TM)" would match "Java(TM) 6 Update 5" and all other versions
'strTargetVersion is the full version number from DisplayVersion in the Registry
'Each decimal point of precision will be compared in turn.
'If the Registry key DisplayVersion is not used by a package, the same number
'of digits is parsed from the right hand side of the DisplayName string
'strExtraUninstParams is used when you want to override the command line
'specified by QuietUninstallString in the Registry, or for when that value is
'missing for example, sometimes InnoSetup packages will specify the switch
'/SILENT in QuietUninstallString, but you may need to override by appending
'/VERYSILENT to the command line in UninstallString
'If neither QuietUninstallString and UninstallString are present, the script
'will use strExtraUninstParams as the full uninstall command line
'Some packages define UninstallString as a long filename but forget to
'surround it with quotes. You can correct this by setting
'boolExtraUninstQuotes = True
' Package "mendeley.cmd", "Mendeley Desktop", "1.6", "/S", True, False, "winword.exe"
'In some cases you may want to ignore the value of both QuietUninstallString
'and UninstallString and override the command completely. To do this, set
'boolUninstForceOverride to True
' Package "flash.cmd", "Adobe Flash Player", "11.4.402.265", "u:\packages\flash\uninstall_flash_player.exe -uninstall", False, True, "iexplore.exe"
'Finally, ProcessToKill is a string or array containing the name(s) of any
'running process(es) you need to kill, if plugins are being installed for Word
'or Internet Explorer for instance.
'=============================================================================
Dim arrBranches, strBranch, boolRemoval, strActualDisplayName, strActualVersion
Dim strQuietUninstall, strUninstall
WScript.Echo VbCrLf & "Running software package check for " & strTargetDisplayName & "..."
'we need to iterate through both the 32 and 64bit uninstall branches of the Registry
arrBranches = Array("SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\", "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\")
For Each strBranch In arrBranches
'firstly, remove old version of package if it's present
objReg.EnumKey HKEY_LOCAL_MACHINE, strBranch, arrSubKeys
If IsArray(arrSubkeys) Then
For Each strKey in arrSubkeys
objReg.GetStringValue HKEY_LOCAL_MACHINE, strBranch & strKey, "DisplayName", strActualDisplayName
If Left(strActualDisplayName, Len(strTargetDisplayName)) = strTargetDisplayName Then
'we've found the target software package
WScript.Echo " Registry data found at branch """ & strKey & """"
'is there a version string (not all software will have one)?
objReg.GetStringValue HKEY_LOCAL_MACHINE, strBranch & strKey, "DisplayVersion", strActualVersion
If Not IsNull(strActualVersion) Then
Else
'if there's no version string we'll try to grab the same number of chars from the right hand side of the DisplayName string
strActualVersion = Right(strActualDisplayName, Len(strTargetVersion))
End If
If (IsUpgradeNeeded (strActualVersion,strTargetVersion)) = True Then
strQuietUninstall = ""
WScript.Echo " Removing old version " & strActualVersion
KillProcess ProcessToKill
'check the package's registry settings
objReg.GetStringValue HKEY_LOCAL_MACHINE, strBranch & strKey, "UninstallString", strUninstall
objReg.GetStringValue HKEY_LOCAL_MACHINE, strBranch & strKey, "QuietUninstallString", strQuietUninstall
If Not strExtraUninstParams = "" Then
'Extra parameters were sent to the function
If boolUninstForceOverride = True Then
'Entire uninstall command line was forced so use strExtraUninstParams, regardless of what's in the Registry
WScript.Echo " Override detected, running """ & strExtraUninstParams & """"
WScript.Echo " " & strExtraUninstParams
WinExec strExtraUninstParams
ElseIf Not IsNull(strUninstall) Then
'use the basic UninstallString plus the additional parameters
If boolExtraUninstQuotes = True Then
strUninstall = """" & strUninstall & """"
End If
strUninstall = strUninstall & " " & strExtraUninstParams
WScript.Echo " Using UninstallString from the Registry, plus """ & strExtraUninstParams & """"
WScript.Echo " " & strUninstall
WinExec strUninstall
Else
'no UninstallString was found in the Registry, so assume that strExtraUninstParams is the full removal command line
WScript.Echo " No UninstallString found, running """ & strExtraUninstParams & """"
WScript.Echo " " & strExtraUninstParams
WinExec strExtraUninstParams
End If
Else
'No extra parameters were sent to the function
'if there's already a value for QuietUninstallString then use that command line
If Not IsNull(strQuietUninstall) Then
WScript.Echo " Using QuietUninstallString directly from the Registry"
WScript.Echo " " & strQuietUninstall
WinExec strQuietUninstall
ElseIf Not IsNull(strUninstall) Then
'no QuietUninstallString was found, fall back to UninstallString
If boolExtraUninstQuotes = True Then
strUninstall = """" & strUninstall & """"
End If
WScript.Echo " Using UninstallString directly from the Registry"
WScript.Echo " " & strUninstall
WinExec strUninstall
Else
WScript.Echo " ERROR - this package doesn't seem to have any UninstallString defined - you'll need to send one to the Package function (see script source for details)"
Exit Function
End If
End If
Else
'IsUpgradeNeeded (strActualVersion,strTargetVersion) is False
'package was detected, but version is >= than the one specified
WScript.Echo " " & strTargetDisplayName & " is already installed and up to date."
Exit Function
End If
End If
Next
End If
Next
'install package
WScript.Echo " Installing " & strTargetDisplayName & " " & strTargetVersion
KillProcess ProcessToKill
WinExec "U:\packages\" & strPackageName
End Function
Function IsUpgradeNeeded(strVerActual,strVerDesired)
Dim arrActualVersion, arrDesiredVersion, i
'Break software version down on decimal points
arrActualVersion = split(strVerActual,".")
arrDesiredVersion = split(strVerDesired,".")
WScript.Echo " Comparing detected version " & strVerActual & " against desired version " & strVerDesired
'iterate, comparing each sub-version number starting from left
For i = 0 To UBound(arrActualVersion)
'WScript.Echo " comparing digit... is " & arrActualVersion(i) & " less than " & arrDesiredVersion(i)
If arrActualVersion(i) < arrDesiredVersion(i) Then
'installed version is out of date
IsUpgradeNeeded = True
Exit Function
ElseIf arrActualVersion(i) > arrDesiredVersion(i) Then
'installed version is newer
IsUpgradeNeeded = False
Exit Function
End If
Next
'thus far the version numbers are the same, but there may be additional
'decimal points of precision in the desired version
' e.g. Adobe Reader 10.1.4 is newer than 10.1
If UBound(arrDesiredVersion) > UBound(arrActualVersion) Then
IsUpgradeNeeded = True
Else
IsUpgradeNeeded = False
End If
End Function
Function MapNetworkDrive(strDriveLetter, strSharePath)
On Error Resume Next
'if the share name is not a UNC path, assume it's on the normal fileserver
If Not Left(strSharePath,2) = "\\" Then
strSharePath = "\\" & strFileServer & "\" & strSharePath
End If
If objFSO.DriveExists(strDriveLetter) Then
objNetwork.RemoveNetworkDrive strDriveLetter, True, True
End If
objNetwork.MapNetworkDrive strDriveLetter, strSharePath
If Err.Number <> 0 Then
WScript.Echo "Error - " & Err.Description
Err.Clear
End If
On Error Goto 0
End Function
Function WinExec(strExec)
Dim objExec, eTime
WinExec = True
Set objExec = objShell.Exec(strExec)
eTime = DateAdd("s", 120, Now)
Do While objExec.Status = 0
WScript.Sleep 1000
Loop
End Function
Function KillProcess(Process)
Dim strProcessElement
If IsArray(Process) Then
For Each strProcessElement in Process
KillIndividualProcess(strProcessElement)
Next
ElseIf Not Process = "" Then
KillIndividualProcess(Process)
End If
End Function
Function KillIndividualProcess(strProcess)
Dim colProcess, objProcess
Set colProcess = objWMI.ExecQuery("Select * from Win32_Process")
For Each objProcess in colProcess
If LCase(objProcess.Name) = LCase(strProcess) Then
WScript.Echo " Killing " & strProcess
'occasionally one parent process may kill all children leading to an object error
'so disable error handling temporarily
On Error Resume Next
objProcess.Terminate()
On Error Goto 0
End If
Next
End Function

Though it seems primarily pitched at home users, Microsoft’s Windows Photo Gallery is a useful image management tool even in a professional environment. It’s distributed as part of a suite of software known collectively as Windows Essentials 2012. I don’t understand why these tools aren’t included in Windows itself, but since they were until recently part of the Live family I’m presuming that they were designed to encourage the use of Microsoft’s online services. The apparent home user bias to the setup (a single installer for the whole suite, which downloads on demand, which asks for a Live sign-in, and which alters homepage and search provider) consequently makes Photo Gallery quite difficult to deploy and automate.

What held me up for a while is that you can no longer target only the Photo Gallery app – MovieMaker and Photo Gallery are bundled together with 2012. So I arrived at this one-liner which I invoke from a more complex workstation startup script, if it’s needed:

The HKCU registry customizations are pretty much the same as for the 2011 version, so to suppress the EULA and Microsoft account sign-in prompt, and to prevent nags about file type associations you will need to set the following in your login script (this is an extract from my VBScript one, but it’s pretty human-readable):

To install on a Windows 8 workstation you’ll need the .Net Framework 3.5 “feature” to be installed, which isn’t there by default (Control Panel > Programs > Uninstall a program > Turn Windows Features on or off). This is problematic if you’re using WSUS – the attempt to download the update will fail with error 0x800f0906. Microsoft have an MDSN article about this, but the prescribed fix of using DISM to fetch the feature from the install media didn’t work for me on Windows 8 Enterprise. I had to remove my PC from an OU which inherits WSUS settings, run gpupdate /force then try again, this time successfully.

In my organization, the requirement for Photo Gallery is for users to interact with a centralized image library. This is stored on a Window 2008 R2 server, and I discovered that I could not add this folder to the Pictures library unless it was indexed on the server side (well, without enabling offline folders – which I don’t want). The relevant information on this topic can be found in this Technet post. In summary, you need to enable the Windows Search Service on the file server, which is a “Role Service” under the File Services role in Server Manager.

The missing piece of the puzzle so far is how to programmatically add this image repository location to each user’s Pictures library. I found a page about this, though the tools did not seem to actually work. Admittedly it’s a few years old, so maybe there are some more official tools now. More research to follow…