Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WUApi error -> System.InvalidCastException : Specified cast is not valid. #496

Closed
MacKey-255 opened this issue Dec 2, 2024 · 4 comments
Closed

Comments

@MacKey-255
Copy link

Hello. I run the test WUApiTests.TestInstaller and it throws:

System.InvalidCastException : Specified cast is not valid.
   at System.StubHelpers.InterfaceMarshaler.ConvertToNative(Object objSrc, IntPtr itfMT, IntPtr classMT, Int32 flags)
   at Vanara.PInvoke.WUApi.IUpdateDownloader.set_Updates(IUpdateCollection value)
   at Vanara.WindowsUpdate.UpdateDownloader..ctor(IEnumerable`1 updates) in C:\...\RiderProjects\Vanara\WindowsUpdate\WindowsUpdate.cs:line 1618
   at Vanara.PInvoke.Tests.WUApiTests.TestInstaller() in C:\...\RiderProjects\Vanara\UnitTests\PInvoke\WUApi\WUApiTests.cs:line 72

The method set_Updates on IUpdateDownloader from Vanara.PInvoke.WUApi causing the issue.

I test it code and give me the same error:

// Create Downloader
var session = new IUpdateSession();
var downloader = session.CreateUpdateDownloader();
var updatesToDownload = new IUpdateCollection();
// ... add or not some Updates on updatesToDownload collection
downloader.Updates = updatesToDownload; // <------ issue

SAlu2s

dahall pushed a commit that referenced this issue Jan 11, 2025
…tion of Windows Update when empty collection provided.
@dahall
Copy link
Owner

dahall commented Jan 11, 2025

Do you know if this a poor response failure code to assigning an empty collection?

@MacKey-255
Copy link
Author

MacKey-255 commented Jan 11, 2025

Do you know if this a poor response failure code to assigning an empty collection?

@dahall no, two element to download. Let me share you the screenshot and a full test code.

Variables screenshot:
Screenshot 2025-01-11 202831
Full test code:

using System.Runtime.Versioning;
using Vanara.PInvoke;
using static Vanara.PInvoke.WUApi;

namespace MyProject.Tests;

[SupportedOSPlatform("Windows")]
public class WindowUpdatesTest
{
	[Test]
	public void UpdateTest()
	{
		// Create searcher
		var session = new IUpdateSession();
		var updateSearcher = session.CreateUpdateSearcher();

		// Create list of updates to install
		var updatesToInstall = new IUpdateCollection();
		var searchResult = updateSearcher.Search("IsInstalled=0");
		var updates = (from IUpdate update in searchResult.Updates select update).ToList();

		// Check updates downloaded
		var downloadable = updates.Where(u =>
				!u.IsDownloaded && (!u.Title.Contains("Internet Explorer 9") || !u.Title.Contains("Internet Explorer 10")))
			.ToList();
		
		// Add downloaded to updates to install
		foreach (var x in updates.Where(x => !downloadable.Contains(x)))
			updatesToInstall.Add(x);

		if (downloadable.Count > 0)
		{
			// Create Downloader
			var downloader = session.CreateUpdateDownloader();
			var updatesToDownload = new IUpdateCollection();

			foreach (var update in downloadable)
				updatesToDownload.Add(update);

			// Create awaiter
			var downloadTask = new TaskCompletionSource<IDownloadResult>();

			// Download updates
			IUpdate? latest;
			downloader.Updates = updatesToDownload; // <------ issue
			Console.WriteLine("Downloading updates ...");
			downloader.BeginDownload(new DownloadProgressChangedCallback(progress =>
			{
				// Calculate percent
				Console.WriteLine($"Downloading {progress.PercentComplete}% ...");
				switch (progress.CurrentUpdateDownloadPhase)
				{
					case DownloadPhase.dphDownloading:
						latest = downloader.Updates[progress.CurrentUpdateIndex];
						Console.WriteLine($"Downloading {latest.Title} in {progress.CurrentUpdatePercentComplete}% ...");
						break;
					case DownloadPhase.dphVerifying:
						updatesToInstall.Add(downloader.Updates[progress.CurrentUpdateIndex]);
						break;
				}
			}), new DownloadCompletedCallback((job, _) =>
			{
				try { downloadTask.TrySetResult(downloader.EndDownload(job)); }
				catch (Exception ex) { downloadTask.TrySetException(ex); }
			}), null);

			// Get download result
			try
			{
				var downloadResult = downloadTask.Task.Result;
				if (downloadResult.ResultCode == OperationResultCode.orcFailed)
					Console.WriteLine("Failed to download!");
			}
			catch (Exception ex)
			{
				Console.WriteLine($"Failed to download updates: {ex.Message}");
			}
		}

		if (updatesToInstall.Count > 0)
		{
			// Create installer
			// TODO: Solve problem with set updates to downloader and installer instances
			var installer = session.CreateUpdateInstaller();
			installer.Updates = updatesToInstall;
			installer.AllowSourcePrompts = false;
			installer.IsForced = true;

			// Create awaiter
			var installTask = new TaskCompletionSource<IInstallationResult>();

			installer.BeginInstall(
				new InstallationProgressChangeCallback(progress =>
					Console.Write($"Installing {progress.PercentComplete}% ...")),
				new InstallationCompletedCallback(job =>
				{
					try { installTask.TrySetResult(installer.EndInstall(job)); }
					catch (Exception e) { installTask.TrySetException(e); }
				}), null);

			// Get installation result
			try
			{
				var installResult = installTask.Task.Result;
				if (installResult.ResultCode == OperationResultCode.orcFailed)
				{
					Console.WriteLine("Failed to install!");
					return;
				}
			}
			catch (Exception ex)
			{
				Console.WriteLine($"Failed to install updates: {ex.Message}");
				return;
			}
		}
		
		Console.WriteLine("Patching complete!");
	}
}


public class DownloadProgressChangedCallback(Action<IDownloadProgress> action) : IDownloadProgressChangedCallback
{
	HRESULT IDownloadProgressChangedCallback.Invoke(IDownloadJob job, IDownloadProgressChangedCallbackArgs args)
	{
		try { action.Invoke(args.Progress); }
		catch (Exception ex) { return ex.HResult; }

		return HRESULT.S_OK;
	}
}

public class DownloadCompletedCallback(
	Action<IDownloadJob, IDownloadCompletedCallbackArgs> action
) : IDownloadCompletedCallback
{
	HRESULT IDownloadCompletedCallback.Invoke(IDownloadJob job, IDownloadCompletedCallbackArgs args)
	{
		try { action.Invoke(job, args); }
		catch (Exception ex) { return ex.HResult; }

		return HRESULT.S_OK;
	}
}

internal class InstallationProgressChangeCallback(
	Action<IInstallationProgress> action
) : IInstallationProgressChangedCallback
{
	HRESULT IInstallationProgressChangedCallback.Invoke(IInstallationJob job,
		IInstallationProgressChangedCallbackArgs args)
	{
		try { action.Invoke(args.Progress); }
		catch (Exception ex) { return ex.HResult; }

		return HRESULT.S_OK;
	}
}

internal class InstallationCompletedCallback(Action<IInstallationJob> action) : IInstallationCompletedCallback
{
	HRESULT IInstallationCompletedCallback.Invoke(IInstallationJob job, IInstallationCompletedCallbackArgs? args)
	{
		try { action.Invoke(job); }
		catch (Exception ex) { return ex.HResult; }

		return HRESULT.S_OK;
	}
}

@dahall
Copy link
Owner

dahall commented Jan 12, 2025

Thanks for this. Oddest error ever. I've been directly implementing CoClass's over interfaces for 20 years. This was the first time an assignment of an interface created by a CoClass has caused a cast exception. For just IUpdateCollection, you have to create it now with

IUpdateCollection coll = (IUpdateCollection)new UpdateCollectionClass();

Then it all works. You can pick up the pre-release builds from AppVeyor (see project README.md for instructions). I'll likely be releasing this version this coming week.

@dahall dahall closed this as completed Jan 12, 2025
@tajbender
Copy link
Contributor

Thanks for this. Oddest error ever. I've been directly implementing CoClass's over interfaces for 20 years. This was the first time an assignment of an interface created by a CoClass has caused a cast exception. For just IUpdateCollection, you have to create it now with

IUpdateCollection coll = (IUpdateCollection)new UpdateCollectionClass();

Then it all works. You can pick up the pre-release builds from AppVeyor (see project README.md for instructions). I'll likely be releasing this version this coming week.

Thank you very much, @dahall. I'll use the AppVeyor build for my Shell32 @ WinUI3 stuff asap, too.

I'll let you know when I get unexpected behaviour. 👍

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants