“Failed to resolve selected task sequence dependencies. Code(0x80040104)” or “Failed to find CCM_SoftwareDistribution object …” for an OSD Driver Package that is on a Distribution Point

A few months ago while working with a consultant to get OSD up and running in my environment the consultant had mentioned a curious bug he had seen whereby if a driver package version was 1 it would appear to be unavailable to a task sequence.  At the time my test driver packages had been updated a few times so I didn’t think much of it but made a note of it “just in case”.  Fast forward to present day, and after creating some new driver packages my previously functional task sequence all of a sudden started failing.  Checking the SMSTS.log I noted the following:

Getting policy for CCM_SoftwareDistribution[AdvertID="P01201B1", PackageID="P0100187", ProgramID="*"]
FALSE, HRESULT=80040104 (e:\nts_sms_fre\sms\framework\tscore\tspolicy.cpp,2301)
Failed to find CCM_SoftwareDistribution object for AdvertID="P01201B1", PackageID="P0100187", ProgramID="*" (*iTSReference)->Resolve( pTSPolicyManager, dwResolveFlags ), HRESULT=80040104 (e:\nts_sms_fre\sms\framework\tscore\tspolicy.cpp,2862)
m_pSelectedTaskSequence->Resolve( m_pPolicyManager, TS::Policy::TaskSequence::ResolvePolicy | TS::Policy::TaskSequence::ResolveSource, fpCallbackProc, pv, hCancelEvent), HRESULT=80040104 (e:\nts_sms_fre\sms\client\tasksequence\tsmbootstrap\tsmediawizardcontrol.cpp,1208)
Failed to resolve selected task sequence dependencies. Code(0x80040104) 
hrReturn, HRESULT=80040104 (e:\nts_sms_fre\sms\client\tasksequence\tsmbootstrap\tsmediaresolveprogresspage.cpp,408)

A quick investigation revealed that P0100187 was one of my new driver packages.  I had put this package together as I was headed for the door on friday night, had I forgotten to send it to the distribution points?  A quick check of the package status in the Configuration Manager console showed that the package had in fact been distributed.  A little time with Google and the SCCM database led me to the fact that although the package had been created and copied to the distribution points the hash values had not been populated in the database.  The scenario that led to this situation was as follows:

  1. Right click the Drivers node in the SCCM console and choose import
  2. On the Add Drivers To Package step create a new package and uncheck the box to update distribution points
  3. Complete the import of the drivers
  4. Right click the new driver package and choose Manage Distribution Points
  5. In the Manage Distribution Points wizard select Copy the package to new distribution points, and complete the wizard

The package will proceed to replicate to the distribution points normally, however upon completion even though the package appears to be installed correctly in the package status in the console if you examine the SMSPackages table in the database you will see that the Hash and NewHash fields are empty for the new package.  Because the hash fields are empty the MP appears to not return any distribution points for that package when the Task Sequence requests them.  The solution is fairly simple:

  1. Right click the driver package and choose Manage Distribution Points
  2. Choose update all distribution points with a new package source version

The package hash will be calculated and the package will be re-distributed to the distribution points (although since the files are already on the distribution points the actual data transfer is fairly minimal), at which point the Task Sequence will no longer fail searching for the driver package source.  Although it’s nothing more than a guess, I’m betting this is the exact behaviour the consultant was seeing.  Given that the fix causes the package source value to be incremented from 1 to 2 it could appear as if the issue was in fact caused by the package having a source value of 1.

To GUI or not to GUI…

That really is the question in my environment.  First, a little background, I serve an environment with a bunch of knowledge workers.  Unfortunately these knowledge workers cover different areas in their field and more unfortunately because of this diversity it’s hard to break most of them down into nice tidy groups for software distribution.  Some may work in areas A, B and C, while others work in areas C, D, and E, and still others work in A, D, and F. That is to say while we have about 5 apps that run 80% of the business functions, another 50 or so fill niche roles depending on what type of work a user is doing, and those don’t always overlap cleanly.

To make matters worse these folks like to move around so we also have a very “self service” culture.  For areas where licensing permits we allow users to determine what software load they really need and self install from Run Advertised Programs.  However, many of the calculations done with the niche software change on a periodic basis and so that software must be regularly updated for those who have self installed.

This arrangement presents some unique challenges, not the least of which is that when a user self installs from RAP we want them to see the installation progressing and at the end be notified of the installation’s success or failure.  However, when updating, where possible we don’t want to bother the user and just want the update to install silently.  Normally this would dictate doubling up programs for each piece of software to be deployed, one for the self service install with UI and one for the silent updates, but not being a fan of doing everything twice I’ve come up with what I think is a rather novel solution.

It all starts with a way to determine if the installation in question has been started via Run Advertised Programs or via a mandatory assignment.  The former we assume to be a user directed self service installation for which we should show a GUI, and the latter an update which should run silently.  So the question becomes how to make that determination.

Enter C++.  Ok, quit groaning, I realize that the SCCM crowd is more relegated to the likes of VBScript, PowerShell, and .NET, but in this case I had my reasons.  You can’t go mucking around with window handles and the like in VBScript and I use this tool in builds where .NET may not be installed, which excludes .NET or PowerShell.  So for the tool to work for me it needed to have no dependencies (I’ve even statically linked the C Runtime in this case).  Fortunately if C++ is not your native tongue, I’ve provide all the bits for you and you can just skip ahead to how to use the tool.  For those that are interested I’ve included the source so feel free to have a look and/or modify at will, but if you make modifications please respect the GPL and post your source (a link back would be appreciated too but certainly isn’t required).

So how does the ShowGUI.exe tool make the determination that it’s running from Run Advertised Programs?  Through some careful examination with one of my favorite Sysinternals tools, Process Explorer, I’ve found that Run Advertised Programs and its related processes always load the module “ccmcore.dll”.  So basically the process goes something like:

  • Get a window handle to the foreground window
  • Find the process ID that belongs to that window handle
  • Enumerate the loaded modules to see if one of them is ccmcore.dll

Now, some of you may be asking “If you’ve got the handle for the foreground window why not just make a call to GetWindowText and see if the title is ‘Run Advertised Programs’?”. and this is in fact what the first iteration of ShowGUI did.  I happen to be in an organization that is English only, and before starting this blog I used ShowGUI operating that way for quite some time without issue.  However as I got ready to release this little utility out into the wild it dawned on me that what this first iteration failed to account for was localization.  If you’ve installed any of the Client operating system localization ICPs Run Advertised Programs may not be titled as such. So I thought I would do you my dear reader a favor and retool things to work regardless of the window title.

So that’s all great theory, but how do we put things into practice?  Well first another bit of background is in order.  The people I work for generally shun all out repackaging.  Sure an MST here and there are fine, but a full blown repackage is usually out of the question.  Why?  Well over the years we’ve been burned by people who didn’t do it well, sure, but more explicitly when you tell a vendor’s tech support that you repackaged their app for installation generally all bets are off.  Also, given that most of the people who I work with are by no means coders but most can hack out a little VBScript here and there, most of our installations are wrapped up in a nice bit of script.  Further, not being a fan of doing things twice you’ll find that I’ve developed a pretty extensive VBScript snippet library (much will land on this blog throughout future posts so stay tuned) which really reduces most of the scripting to “coding by Lego” as I like to call it.  In other words you’ve got all the building blocks, go snap them together to make something cool.

To that end you will also find attached to this blog the “standard script template” that makes using ShowGUI.exe easier.  I won’t cover it all here, but we’ll look at a few of the bits relevant to this discussion.  First we have a header that among other things includes a silent flag and defaults it to false.

Dim bSilent: bSilent = False  ' Silent Flag

Enough for the warm up, moving on we have a call to a function ScriptInit.  This is where the meat of the automatic GUI determination happens.  ScriptInit looks like this:

Private Sub ScriptInit()
      ' Auto check for silent mode if helper exe exists
      Dim objFSOSilent: Set objFSOSilent = CreateObject("Scripting.FileSystemObject")
      Dim ScriptPathSilent: ScriptPathSilent = objFSOSilent.GetParentFolderName(WScript.ScriptFullName)
      If Right(ScriptPathSilent, 1) <> "\" Then ScriptPathSilent = ScriptPathSilent & "\"
      If objFSOSilent.FileExists(ScriptPathSilent & "ShowGUI.exe") Then
            Dim objShellExecuteSilent: Set objShellExecuteSilent = CreateObject("Wscript.Shell")
            Dim iFailSafeSilent
            For iFailSafeSilent = 1 To 5
                  Dim testResultSilent: testResultSilent = objShellExecuteSilent.Run("""" & ScriptPathSilent & "ShowGUI.exe" & """", 0, True)
                  If testResultSilent <> 1 Then
                        bSilent = Not cBool(testResultSilent)
                        Exit For
                  End If
                  WScript.Sleep(500)
            Next
            If testResultSilent = 1 Then bSilent = True ' Fail silent. 1 means that GetForegroundWindow in ShowGUI.exe returned Null
                                                        ' Usually this is because a window was deactivating or the desktop on which
                                                        ' the app is running is not the active desktop (i.e. computer is locked etc.)
            Set objShellExecuteSilent = Nothing
      End If
      Set objFSOSilent = Nothing
      ' Force if flag is Set
      Dim strArg
      For Each strArg In WScript.Arguments
          strArg = UCASE(strArg)
          Select Case strArg
            Case "/S", "-S", "--SILENT", "/QN"
                  bSilent = True
            Case "/QB", "/QB!"
                  bSilent = False
              Case "/TS"
                  bSilent = True
                  bTaskSequence = True
            End Select   
      Next
End Sub

First ScriptInit checks to see if the ShowGUI helper EXE exists in the script path.  If not we’re not doing any auto GUI mode determination and the script will proceed to check for any explicit flags, which we’ll cover that later.  If the ShowGUI helper exists in the script path then ScriptInit will go into a loop and execute it up to 5 times at half second intervals.  Why execute the helper EXE up to 5 times?  Well if you read the documentation for the GetForgroundWindow API you’ll notice that it is possible for there to be no foreground window.  If anything failed while executing, ShowGUI returns 1 and usually delaying a few milliseconds and trying again will get a valid result.  I picked half a second and 5 tries because they’re nice round numbers, and at most it will delay the program start by 2.5 seconds which is tolerable.  One instance where the script will not get a valid result even after waiting is when there is no user logged on or the console is locked.  In this scenario ScriptInit defaults to silent mode under the assumption that if there is no foreground window after 5 attempts then there is probably no user logged in or the console is locked and it’s a safe bet the user didn’t initiate the program from RAP.

Once any automated GUI mode detection has been made ScriptInit enumerates all of the command line parameters passed to the script and if any of them are in a list of well known silent or basic GUI flags (i.e. /s, /qn, /qb, etc.) it will override the automatic determination.  The assumption is that if you explicitly specify a GUI mode on the command line you probably know what you are doing and that should take precedence over anything the script decided on its own.

So, the bSilent flag  is set appropriately, what now?  Now the real work happens and this is generally left up to your imagination, but here’s a quick example.  Let’s say we’re installing a simple MSI.  No changes needed, nothing fancy, just “MSIEXEC.EXE /I MyInstaller.msi /QB” will give you your basic hands off install.  Well, to get that wrapped in the standard script template would be:

Dim objShellExecute: Set objShellExecute = CreateObject("Wscript.Shell")
iRetVal = objShellExecute.Run("MSIEXEC.EXE /I MyInstaller.msi /QB", 1, True)

However instead of relegating our installation to always use the /QB switch and thus always show a basic UI, what if we did something like this:

Dim objShellExecute: Set objShellExecute = CreateObject("Wscript.Shell")
iRetVal = objShellExecute.Run("MSIEXEC.EXE /I MyInstaller.msi " & IIF(bSilent, "/QN", "/QB"), 1, True)

Now if bSilent is true the command will be run with the /QN switch and be silent, otherwise it will be run with the /QB switch and show our basic UI.  Viola!  One package, program, and script that executes silently when run from a mandatory assignment and with a basic GUI when run by the user from RAP.

To be fair this system isn’t perfect.  In a download and install situation if the user checks the box to automatically run the program when the download completes and then closes RAP before the program runs, well, it’s going to run silent.  Also, if your environment changes the foreground window all bets are off.  In other words you can’t call ShowGUI.exe from a batch file or PowerShell script because they both create a new console window which steals the focus (but if you’re creative I’m sure you can figure out how to run ShowGUI.exe from a VBScript helper and then launch your batch file or PowerShell script from there).  If you find a better way I’m all ears (I’ll even do the coding for you), but I posted this question in the MyITForum.com forums several months ago and got no replies, so this is what I’ve come up with.

Keep in mind, the “standard disclaimer” applies.  This works for me, it probably will not work for you.  In fact, you probably shouldn’t use it at all as it may do things in your environment that can’t be foreseen, and let’s face it I very well may not have any idea what I’m talking about.  But, if you’re brave and decide you really really want to give it a try despite my warnings, then you should probably do some extensive testing in a non-production environment first.  I take no responsibility if this code and/or information causes your garden to wither and die, your hair to fall out, and your car keys to be forever missing.  In fact I take no responsibility for anything this code may or may not do.  Use it at your own risk.

ShowGUI.zip

DefaultTemplate.vbs

Dell Ships Version 2.0 of the Windows 7 Notebook Combo Driver CAB

If you’re a Dell shop using OSD you’re probably a fan of their one size fits all “Combo Driver CABs”. Dell just released version 2.0 for notebooks which adds support for the new 3rd generation E series (i.e. the Exx20 models). Be aware however, from the notes on the site, it looks like there may be issues with the Nvidia driver with the E6420 / E6520 at the moment.

Using VBScript to Check Partitions for Advanced Format (4K / 512e) Sector Alignment

As you may know Dell recently announced that they will start shipping Advanced Format (4k/512e) drives in their enterprise systems in May 2011.  For those not wanting to follow the links, the cliff’s notes version is that Advanced Format drives use a 4096 byte (4k) sector rather than a 512 byte sector.  This allows higher bit densities by reducing the amount of space tied up in ECC and sector gap.

Old and New Sector Formats Credit: Dougolsen / Wikimedia Commons
Old and New Sector Formats Credit: Dougolsen / Wikimedia Commons

So what’s the big deal then?  Well in order to make the transition, 4K/512e advanced format drives still present themselves externally as a 512 byte per sector drive, and internally lump reads and writes into eight logical 512 byte sectors to match up with their physical 4096 byte sectors.  This has the unfortunate side effect that if your partitions are not aligned to a 4K boundary the disk may have to perform a Read-Modify-Write across two physical sectors.  This process can incur extra latency waiting for an additional rotation of the disk platters which drastically impacts performance.  Further compounding the matter Windows XP/2003 operating systems and most disk management tools create the first partition on the disk starting at LBA sector 63 which if you do your math correctly is not 4K aligned.

Recently the question came up as to whether or not it was possible to use a script to detect partitions that were not 4K aligned and therefore may be causing performance issues, or to perhaps use in an OSD task sequence to halt the OS install if the partition is not correctly aligned.  After some browsing through the WMI classes I found what I was looking for.  The Win32_DiskPartion class has a property StartingOffset that, according to MSDN is in fact the “starting offset (in bytes) of the partition”.  A little modulus division later and you have a simple way to determine if your partition is 4K sector aligned.

Option Explicit

Dim strComputer: strComputer = "."
Dim objWMIService: Set objWMIService = GetObject("winmgmts:\\" & strComputer & "\root\cimv2")
Dim colPartitions: Set colPartitions = objWMIService.ExecQuery("Select * from Win32_DiskPartition",,48)

Dim objPartition
For Each objPartition In colPartitions
    Dim iResult: iResult = objPartition.StartingOffset / 4096
    If iResult = Fix(iResult) Then
        WScript.Echo objPartition.Caption & " is 4K/512e sector aligned."
    Else
        WScript.Echo objPartition.Caption & " is NOT 4K/512e sector aligned."
    End If
Next

There is one caveat I should mention if you’re planning to use this in an OSD task sequence from WinPE.  It seems that the Win32_DiskPartition class has been removed from WinPE 3.x.  Luckily others already have a solution for you.

UPDATE: 5/26/11

Per the request by Eric in the comments I’ve updated the script to show the drive letter associated with the partition. 

Option Explicit

Dim strComputer: strComputer = "."
Dim objWMIService: Set objWMIService = GetObject("winmgmts:\\" & strComputer & "\root\cimv2")
Dim colPartitions: Set colPartitions = objWMIService.ExecQuery("Select * from Win32_DiskPartition",,48)

Dim objPartition

For Each objPartition In colPartitions
    Dim strDriveLetter: strDriveLetter = PartitionToDrive(objWMIService, objPartition.DeviceID)
    Dim iResult: iResult = objPartition.StartingOffset / 4096
    If iResult = Fix(iResult) Then
        WScript.Echo "(" & strDriveLetter & ") " & objPartition.Caption & " is 4K/512e sector aligned."
    Else
        WScript.Echo "(" & strDriveLetter & ") " & objPartition.Caption & " is NOT 4K/512e sector aligned."
    End If
Next

Private Function PartitionToDrive(objWMIService, DeviceID)
    Dim objLogcialDisks: Set objLogcialDisks = objWMIService.ExecQuery( _
    "ASSOCIATORS OF {Win32_DiskPartition.DeviceID='" & DeviceID & _
    "'} WHERE AssocClass = Win32_LogicalDiskToPartition")

    Dim objLogicalDisk
    For Each objLogicalDisk In objLogcialDisks
        PartitionToDrive = objLogicalDisk.DeviceID
        Exit Function
    Next

    PartitionToDrive = ""
End Function

Application Compatibility Walkthrough with ACT 5.6

I’m a big believer in least privileges and since the organization I work for decided to run all users as non-admins back during its implementation of Windows XP, when running non-admin was more difficult because almost every vendor gave you a solution of “run it as administrator”, I’m also a big fan of Aaron Margosis’ blog.  This month Aaron brings us a great walk-through of troubleshooting an XP to 7 application compatibility issue using some of the Sysinternals tools, and then goes on to present both the “less correct” fix via registry hacking and the “more correct” fix via the Application Compatibility Toolkit (ACT) 5.6.  As I myself am on the verge of a companywide XP to 7 migration this is a subject near and dear to me so I thought I’d share.

-J

April Visual Studio Update Downloads Fail

Here’s a quick tip for something that bit me earlier today. Downloading KB2465361 and KB2455033 fail with an invalid certificate signature error. This is a known issue documented here and noted on the SCCM Support Team blog  here in reference to some prior Visual Studio updates.  The issue only affects you if you’re running the SCCM console from and OS prior to Vista (i.e. XP / 2003).  It looks like the only workaround for now is to run the console from a Vista or better machine.

Welcome

My family got their first computer when I was 4 years old (a Commodore VIC-20 for those who want to get nostalgic). Since then my interest in electronics and computers has grown into a full-time career spanning the past 15+ years. I landed in the SMS, SCCM and Managed Desktop world about 6 or so years ago and havn’t looked back since.

While there isn’t much here yet, look for tips, tricks, and tools relating to using SCCM to provide a seamless managed desktop user experience to land here in the near future. For those coders and scripters among you my primary weapons of choice are C# and VB Script with a little PowerShell and C++ thrown in for good measure. Hopefully you’ll find it useful.

-J