CsWinRT icon indicating copy to clipboard operation
CsWinRT copied to clipboard

InvalidCastException when trying to get string array from ApplicationData

Open tipa opened this issue 1 year ago • 20 comments

Describe the bug

When building the app with PublishAot=true, getting a string array from ApplicationData.Current.LocalSettings and casting it to the string[] type I get: System.InvalidCastException: 'Unable to cast object of type 'WinRT.IInspectable' to type 'System.String[]'.'

Steps to reproduce the bug

Set in csproj: <PublishAot>true</PublishAot>

ApplicationData.Current.LocalSettings.Values["test"] = new string[] { "a", "b", "c" };
var array = (string[])ApplicationData.Current.LocalSettings.Values["test"];

Expected behavior

No crash

Screenshots

image

NuGet package version

Windows App SDK 1.6.0: 1.6.240829007

Packaging type

Packaged (MSIX)

Windows version

Windows 11 version 22H2 (22621, 2022 Update)

IDE

Visual Studio 2022

Additional context

Example project: Test.zip

tipa avatar Sep 10 '24 13:09 tipa

I think I met the same/related issue on another example (WASDK v1.6, AOT enabled):

SelectionChangedEventArgs args = new([], []);

Throws invalidcastexception: "specified cast is not valid".

fabianoriccardi avatar Sep 11 '24 12:09 fabianoriccardi

@manodasanW Does this look related to the collection expression marshaling issue?

Scottj1s avatar Sep 11 '24 17:09 Scottj1s

I'm also seeing crashes when setting an IEnumerable as ItemSource, like this: comboBox.ItemsSource = Enumerable.Range(0, 10).Select(x => new TestItem(x)); No crash when using .ToList() comboBox.ItemsSource = Enumerable.Range(0, 10).Select(x => new TestItem(x)).ToList();

image

If this is a separate issue and you want me to open a new issue, please let me know

tipa avatar Sep 11 '24 19:09 tipa

Any hope this will be resolved with the next release? It's the only thing blocking me from using NativeAot.

tipa avatar Jan 08 '25 19:01 tipa

The problem still happens with 1.7.250109001-experimental2

tipa avatar Jan 11 '25 11:01 tipa

"I'm also seeing crashes when setting an IEnumerable as ItemSource, like this: comboBox.ItemsSource = Enumerable.Range(0, 10).Select(x => new TestItem(x));"

That's expected and not supported. You need either ToList() or ToString().

Sergio0694 avatar Jan 14 '25 17:01 Sergio0694

Can repro with CsWinRT 2.2.0, only if PublishAot is set. Otherwise the cast works (both to string[] and interfaces).

Sergio0694 avatar Jan 28 '25 02:01 Sergio0694

If this helps, we should look into ComWrappersSupport. GetRuntimeClassForTypeCreation and TypeNameSupport. They determine which type the return value is, and it's woefully incomplete for AOT scenarios.

dongle-the-gadget avatar Jan 28 '25 04:01 dongle-the-gadget

Just ran into something similar using a FlipView with an ObservableCollection<string> as the ItemsSource. The only way to get around it was to set the AllowUnsafeBlocks to true in the csproj - weird.

System.InvalidCastException: 'Unable to cast object of type 'WinRT.IInspectable' to type 'System.String[]'.'

GuildOfCalamity avatar Feb 06 '25 17:02 GuildOfCalamity

That's expected and by design. If you use CsWinRT 2.2.0 you'll get a proper warning telling you to enable that.

Sergio0694 avatar Feb 06 '25 17:02 Sergio0694

I've reported this issue six months ago. Is there any hope this will get fixed any time soon? It's the only blocker that prevents me using Native AOT for this particular app. Should I just JSON-serialize my string array and write the resulting string to my local app settings? It will be a pain migrating my users, but I don't see any progress here...

tipa avatar Mar 08 '25 17:03 tipa

@tipa I had to do the json serialize/deserialize workaround as well, not sure how this was overlooked in the first place? Setting a collection as a source is a VERY common routine. People claiming it's "by design" - I don't buy that excuse. I guess the other question becomes How to properly cast a 'System.String[]' to 'WinRT.IInspectable' if the BCL is unable to perform this step natively? Furthermore, dynamic binding would appear to be the root cause due to the performance AOT is claiming?

GuildOfCalamity avatar Mar 08 '25 17:03 GuildOfCalamity

This is incredibly hard to achieve in AOT because WinRT only provides type information during runtime, but the type itself could have been trimmed due to AOT.

There's no precise way to check what would be cast when, either. Sure, a scenario where you cast immediately within a line might be easily analyzable. However, what if you decide to keep the original type (that is of no use) and decide to later on, in another file, cast it?

dongle-the-gadget avatar Mar 08 '25 18:03 dongle-the-gadget

@dongle-the-gadget I'm not sure that scenario is common, is it? Typically casting is of the sub/super flavor, e.g. Control to FrameworkElement. At any rate, maybe a reserved keyword should be allocated for such scenarios to help the AOT. Also, even with AOT disabled it still complains about the WinRT.IInspectable even though it's not relevant anymore. Possibly AOT could just switch to a reflection mode to drop out of the performance mode in these cases.

GuildOfCalamity avatar Mar 08 '25 22:03 GuildOfCalamity

The IInspectable to string[] in WinRT is a boxing scenario where it makes use of Windows.Foundation.IReferenceArray<string>. This code path currently does have issues on AOT due to lack of static type information from the projection. But there are plans to try to address this similar to how we address Windows.Foundation.IReference<>. At least for primitive types such as this, we should be able to get it to work. I had made an initial attempt, but there were a couple issues which I will try to pick up and get addressed.

Note the by design reference was to needing to have AllowUnsafeBlocks enabled for certain scenarios to work. This is because to enable certain scenarios our source generator generates sources that make use of unsafe which would otherwise not compile if that wasn't enabled.

manodasanW avatar Mar 08 '25 23:03 manodasanW

@manodasanW Thanks, this is the best explanation I've heard so far - I'll definitely check out your IReferenceArray2!

GuildOfCalamity avatar Mar 08 '25 23:03 GuildOfCalamity

Possibly AOT could just switch to a reflection mode to drop out of the performance mode in these cases.

This is in the hands of the .NET team, who has decided not to offer such an option.

dongle-the-gadget avatar Mar 09 '25 04:03 dongle-the-gadget

@manodasanW thanks for your response. I see your linked fork was last active in September last year. I am not too familiar with the topic to understand if I can use this code to workaround the problem myself in the meantime or if I need to wait for an official release. After 6 months, I'd like to make a decision now on how to proceed. Is there any chance this bug will be fixed in the next few weeks or (1-2) months? If not, I will have to JSON-serialize my array and save that string (I would like to avoid that since the previously stored string array essentially in the local app settings is "lost" and users fall back to the default)

tipa avatar Mar 10 '25 09:03 tipa

@manodasanW I spent a considerable amount of time, trying to understand your IReferenceArray2 code and apply it to my problem, but without luck. Unfortunately, I am not familiar with the internals of CsWinRT. Can you please give a rough timeline when this issue will be fixed - or if it will not be fixed at all?

tipa avatar Mar 21 '25 07:03 tipa

I am still having this issue and it blocks me from migrating the rest of my apps to WinUI3 - is there any hope this will be fixed? It would be greatly appreciated! :)

tipa avatar May 22 '25 21:05 tipa

@manodasanW @Sergio0694 This happens again with .net10-rc1 - can this issue be reopened? Example project: Test (1).zip

tipa avatar Sep 30 '25 21:09 tipa

@tipa is this with CsWinRT 2.3.0-prerelease.250720.1?

manodasanW avatar Sep 30 '25 22:09 manodasanW

@manodasanW Yes. This time reproducing it isn't as easy as just running it via the IDE in Release mode, I had to build the package and install it, but the problem is the same, with a slightly different error message: "Specified cast is not valid".

The type of the object that is returned from the application settings is again of type WinRT.IInspectable and GetRuntimeClassName() returns Windows.Foundation.IReferenceArray`1<String>

tipa avatar Oct 01 '25 06:10 tipa