C# Wuapi BeginInstall callback gets called with wrong information in EventArgs

269 views Asked by At

I'm writing a program that will make windows update availabe in an offline environment via wsusscn2.cab.

As I want feedback on how the update is going im using the asynchronous BeginInstall method. The problem I'm seeing is that the Callback is getting called twice with the same update information in the eventArgs parameter, this also means some updates are not getting reported on. The update index that are getting doubled or missing varies from one run to another. Sometimes it even goes without an issue.

I did make a short test program to play around with things, code is below. The program is running on a VMWare Workstation with Windows 10 1909 and its finding Office 2010 updates as I have an unupdated Office SP2 Installation to try against.

    static void Main(string[] args)
    {
        Console.WriteLine("Debug");
        Console.WriteLine(String.Format("Debugger is Attached:{0}", Debugger.IsAttached));

        Update();
        Console.Write("Done!");
        Console.ReadKey();
    }

    static void Update()
    {
        EventWaitHandle waitHandle = new EventWaitHandle(false, EventResetMode.ManualReset);
        OnProgressChange onProgress = new OnProgressChange(waitHandle);
        OnComplete onComplete = new OnComplete(waitHandle);
        State state = new State();

        UpdateSession session = new UpdateSession();
        UpdateServiceManager serviceManager = session.CreateUpdateServiceManager();
        IUpdateService updateService = serviceManager.AddScanPackageService("Offline Sync Service", @"C:\wsusscn2.cab",1);
        Console.WriteLine("Adding scanpackageservice complete");

        IUpdateSearcher searcher = session.CreateUpdateSearcher();
        searcher.ServerSelection = ServerSelection.ssOthers;
        searcher.ServiceID = updateService.ServiceID;
        
        ISearchResult result = searcher.Search("IsInstalled=0");
        Console.WriteLine("Search complete");

        IUpdateInstaller installer = session.CreateUpdateInstaller();
        installer.ClientApplicationID = "TEST";
        installer.Updates = new UpdateCollection();
        foreach (IUpdate update in result.Updates)
        {
            int i = installer.Updates.Add(update);
            Console.WriteLine(String.Format("{0,2} - Adding: {1}", i, update.KBArticleIDs[0]));                            
        }

        installer.ClientApplicationID = "TEST";

        IInstallationJob installationJob = installer.BeginInstall(onProgress, onComplete, state);
        waitHandle.WaitOne();
        IInstallationResult installationResult = installer.EndInstall(installationJob);
        serviceManager.RemoveService(updateService.ServiceID);
    }
}

public class State
{

}

public class OnProgressChange : IInstallationProgressChangedCallback
{
    EventWaitHandle eventWait;
    int Counter = 0;
    public OnProgressChange(EventWaitHandle handle)
    {
        eventWait = handle;
    }
    public void Invoke(IInstallationJob installationJob, IInstallationProgressChangedCallbackArgs callbackArgs)
    {
        int CurrentUpdateIndex = callbackArgs.Progress.CurrentUpdateIndex;
        Thread.Sleep(500); // Test delay
        string Title = installationJob.Updates[CurrentUpdateIndex].Title;
        string ID = installationJob.Updates[CurrentUpdateIndex].KBArticleIDs[0];
        Console.WriteLine(String.Format("Index: {0,2} - ID: {1} --- Args {2} - Control:{3}", CurrentUpdateIndex, ID, callbackArgs.Progress.CurrentUpdateIndex,Counter++==CurrentUpdateIndex));
    }
}

public class OnComplete : IInstallationCompletedCallback
{
    EventWaitHandle eventWait;
    public OnComplete(EventWaitHandle handle)
    {            
        eventWait = handle;
    }        
    public void Invoke(IInstallationJob installationJob, IInstallationCompletedCallbackArgs callbackArgs)
    {
        eventWait.Set();
        Console.WriteLine("In End");
    }
}

The typical result from the program looks like this:

Debug
Debugger is Attached:True
Adding scanpackageservice complete
Search complete
 0 - Adding: 2850016
 1 - Adding: 2880971
 2 - Adding: 2553154
 3 - Adding: 2956076
 4 - Adding: 2920812
 5 - Adding: 2553313
 6 - Adding: 2881029
 7 - Adding: 2956063
 8 - Adding: 3114414
 9 - Adding: 3114885
10 - Adding: 3191908
11 - Adding: 3203468
12 - Adding: 3213626
13 - Adding: 3213631
14 - Adding: 4011610
15 - Adding: 3115197
16 - Adding: 3115248
17 - Adding: 4022206
18 - Adding: 4022208
19 - Adding: 3213636
20 - Adding: 3114565
21 - Adding: 2553332
22 - Adding: 4461625
23 - Adding: 4484238
24 - Adding: 4484266
25 - Adding: 4484235
26 - Adding: 4032216
27 - Adding: 3203462
28 - Adding: 4484415
29 - Adding: 4484373
30 - Adding: 4484382
31 - Adding: 4484458
Index:  0 - ID: 2850016 --- Args 0 - Control:True
Index:  1 - ID: 2880971 --- Args 1 - Control:True
Index:  2 - ID: 2553154 --- Args 2 - Control:True
Index:  3 - ID: 2956076 --- Args 3 - Control:True
Index:  4 - ID: 2920812 --- Args 4 - Control:True
Index:  5 - ID: 2553313 --- Args 5 - Control:True
Index:  6 - ID: 2881029 --- Args 6 - Control:True
Index:  7 - ID: 2956063 --- Args 7 - Control:True
Index:  8 - ID: 3114414 --- Args 8 - Control:True
Index:  9 - ID: 3114885 --- Args 9 - Control:True
Index: 11 - ID: 3203468 --- Args 11 - Control:False
Index: 11 - ID: 3203468 --- Args 11 - Control:True
Index: 13 - ID: 3213631 --- Args 13 - Control:False
Index: 13 - ID: 3213631 --- Args 13 - Control:True
Index: 15 - ID: 3115197 --- Args 15 - Control:False
Index: 15 - ID: 3115197 --- Args 15 - Control:True
Index: 17 - ID: 4022206 --- Args 17 - Control:False
Index: 17 - ID: 4022206 --- Args 17 - Control:True
Index: 18 - ID: 4022208 --- Args 18 - Control:True
Index: 20 - ID: 3114565 --- Args 20 - Control:False
Index: 20 - ID: 3114565 --- Args 20 - Control:True
Index: 22 - ID: 4461625 --- Args 22 - Control:False
Index: 22 - ID: 4461625 --- Args 22 - Control:True
Index: 24 - ID: 4484266 --- Args 24 - Control:False
Index: 24 - ID: 4484266 --- Args 24 - Control:True
Index: 25 - ID: 4484235 --- Args 25 - Control:True
Index: 26 - ID: 4032216 --- Args 26 - Control:True
Index: 27 - ID: 3203462 --- Args 27 - Control:True
Index: 28 - ID: 4484415 --- Args 28 - Control:True
Index: 29 - ID: 4484373 --- Args 29 - Control:True
Index: 30 - ID: 4484382 --- Args 30 - Control:True
Index: 31 - ID: 4484458 --- Args 31 - Control:True
In End
Done!

As you can see updateindex 10 is missing and 11 gets reported on twice. Same goes for 12 and 13. How much of this and which one varies from run to run.

What I have seen is that if I run without a debugger attached it works in more of the test runs. If I do a release run the success rate is close to 100%

I hope I've explained this well enough and that I can get some help with this as I've used up all my ideas at the moment.

Also my first post in here so I'll gladly take some feedback on my post in general as well :)

1

There are 1 answers

0
marsh-wiggle On

The callback is not reliable. After your update finishes, you can check the state of the updates you wanted to install like this:

IInstallationProgress jobProgress = job.GetProgress();

for (int updateindex = 0; updateindex < installer.Updates.Count; updateindex++)
{
    IUpdateInstallationResult updateInstallResult = jobProgress.GetUpdateResult(updateindex);

    switch (updateInstallResult.ResultCode)
    {
        case OperationResultCode.orcNotStarted:
            // ....
            break;
        
        case OperationResultCode.orcInProgress:
            // ....
            break;
        
        case OperationResultCode.orcSucceeded:
            // ....
            break;
        
        case OperationResultCode.orcSucceededWithErrors:
            // ....
            break;
        
        case OperationResultCode.orcFailed:
            // ....
            break;
        
        case OperationResultCode.orcAborted:
            // ....
            break;
    }

}