From 755f3f3f0ec7a16d0f750f0ca41490b40e9e54ea Mon Sep 17 00:00:00 2001 From: Jason Ginchereau Date: Tue, 24 Oct 2023 06:56:58 -1000 Subject: [PATCH] Improve WPF example (#163) --- examples/wpf/README.md | 7 +- examples/wpf/Window1.xaml | 14 ++ examples/wpf/Window1.xaml.cs | 75 ++++++ .../wpf/{wpf.csproj => WpfExample.csproj} | 3 + examples/wpf/example.js | 24 +- src/NodeApi.DotNetHost/ManagedHost.cs | 23 +- src/NodeApi.Generator/Program.cs | 14 ++ src/NodeApi.Generator/SymbolExtensions.cs | 217 ++++++++++++++---- .../TypeDefinitionsGenerator.cs | 3 +- 9 files changed, 325 insertions(+), 55 deletions(-) create mode 100644 examples/wpf/Window1.xaml create mode 100644 examples/wpf/Window1.xaml.cs rename examples/wpf/{wpf.csproj => WpfExample.csproj} (81%) diff --git a/examples/wpf/README.md b/examples/wpf/README.md index dc6cde7c..69667c6e 100644 --- a/examples/wpf/README.md +++ b/examples/wpf/README.md @@ -1,8 +1,11 @@ ## Example: Calling WFP APIs from JS -The `example.js` script dynamically loads the WPF .NET assemblies and shows a simple message box. +The `example.js` script loads WPF .NET assemblies and shows a WPF window with a WebView2 +control with a JS script that renders a mermaid diagram. -_**.NET events** are not yet projected to JS ([#59](https://github.com/microsoft/node-api-dotnet/issues/59)). WPF capabilities will be limited until that issue is resolved._ +_**.NET events** are not yet projected to JS +([#59](https://github.com/microsoft/node-api-dotnet/issues/59)). +WPF capabilities will be limited until that issue is resolved._ | Command | Explanation |----------------------------------|-------------------------------------------------- diff --git a/examples/wpf/Window1.xaml b/examples/wpf/Window1.xaml new file mode 100644 index 00000000..eb86fa13 --- /dev/null +++ b/examples/wpf/Window1.xaml @@ -0,0 +1,14 @@ + + + + + diff --git a/examples/wpf/Window1.xaml.cs b/examples/wpf/Window1.xaml.cs new file mode 100644 index 00000000..4d7d38e4 --- /dev/null +++ b/examples/wpf/Window1.xaml.cs @@ -0,0 +1,75 @@ +using System; +using System.Threading; +using System.Windows; +using Microsoft.Web.WebView2.Core; + +namespace Microsoft.JavaScript.NodeApi.Examples; + +public partial class Window1 : Window +{ + private readonly string markdown; + + public static void CreateWebView2Window(string markdown) + { + StaThreadWrapper(() => { new Window1(markdown).ShowDialog(); }); + } + + private Window1(string markdown) + { + this.markdown = markdown; + InitializeComponent(); + } + + private static void StaThreadWrapper(Action action) + { + var t = new Thread(o => + { + action(); + ////System.Windows.Threading.Dispatcher.Run(); + }); + t.SetApartmentState(ApartmentState.STA); + t.Start(); + t.Join(); + } + + protected override async void OnContentRendered(EventArgs e) + { + base.OnContentRendered(e); + await webView.EnsureCoreWebView2Async(); // This will work just fine + + webView.CoreWebView2.WebMessageReceived += CoreWebView2_WebMessageReceived; + + string html = $@" + + + + + +
+ + + "; + webView.NavigateToString(html); + } + + /// + /// Triggers when Mermaid svg is generated + /// + private void CoreWebView2_WebMessageReceived(object sender, CoreWebView2WebMessageReceivedEventArgs e) + { + string data = e.TryGetWebMessageAsString(); + Console.Write(data); + ////Close(); + } +} diff --git a/examples/wpf/wpf.csproj b/examples/wpf/WpfExample.csproj similarity index 81% rename from examples/wpf/wpf.csproj rename to examples/wpf/WpfExample.csproj index a4192bff..675bdddf 100644 --- a/examples/wpf/wpf.csproj +++ b/examples/wpf/WpfExample.csproj @@ -7,6 +7,7 @@ bin esm true + true @@ -20,6 +21,8 @@ + + diff --git a/examples/wpf/example.js b/examples/wpf/example.js index ed02edc3..f2b8d853 100644 --- a/examples/wpf/example.js +++ b/examples/wpf/example.js @@ -3,7 +3,29 @@ // @ts-check +import * as path from 'node:path'; +import { fileURLToPath } from 'node:url'; +const __filename = fileURLToPath(import.meta.url); +const __dirname = path.dirname(__filename); + import dotnet from 'node-api-dotnet'; import './bin/PresentationFramework.js'; +import './bin/WpfExample.js'; + +// Explicitly load some assembly dependencies that are not automatically loaded +// by the NodeApi assembly resolver. (This may be improved in the future.) +dotnet.load('System.Configuration.ConfigurationManager'); +dotnet.load('System.Windows.Extensions'); +dotnet.load(__dirname + '/pkg/microsoft.web.webview2/1.0.2088.41/lib/netcoreapp3.0/Microsoft.Web.WebView2.Wpf.dll'); +dotnet.load('PresentationFramework.Aero2'); + +// Explicitly load some native library dependencies. +dotnet.load('wpfgfx_cor3'); +dotnet.load(__dirname + '/bin/runtimes/win-x64/native/WebView2Loader.dll'); + +// Show a simple message box. (This doesn't need most of the dependencies.) +////dotnet.System.Windows.MessageBox.Show('Hello from JS!', "Example"); -dotnet.System.Windows.MessageBox.Show('Hello from JS!', "Example"); +// Show a WPF window with a WebView2 control that renders a mermaid diagram. +const diagram = 'graph TD\n A[Hello from JS!]'; +dotnet.Microsoft.JavaScript.NodeApi.Examples.Window1.CreateWebView2Window(diagram); diff --git a/src/NodeApi.DotNetHost/ManagedHost.cs b/src/NodeApi.DotNetHost/ManagedHost.cs index 46d68d11..8ff7ac88 100644 --- a/src/NodeApi.DotNetHost/ManagedHost.cs +++ b/src/NodeApi.DotNetHost/ManagedHost.cs @@ -422,6 +422,10 @@ public JSValue LoadModule(JSCallbackArgs args) /// /// A JS object that represents the loaded assembly; each property of the object /// is a public type. + /// + /// Also supports loading native libraries, to make them available for assemblies to + /// resolve using DllImport. + /// public JSValue LoadAssembly(JSCallbackArgs args) { string assemblyNameOrFilePath = (string)args[0]; @@ -429,13 +433,13 @@ public JSValue LoadAssembly(JSCallbackArgs args) if (!_loadedAssembliesByPath.ContainsKey(assemblyNameOrFilePath) && !_loadedAssembliesByName.ContainsKey(assemblyNameOrFilePath)) { - LoadAssembly(assemblyNameOrFilePath); + LoadAssembly(assemblyNameOrFilePath, allowNativeLibrary: true); } return default; } - private Assembly LoadAssembly(string assemblyNameOrFilePath) + private Assembly LoadAssembly(string assemblyNameOrFilePath, bool allowNativeLibrary = false) { Trace($"> ManagedHost.LoadAssembly({assemblyNameOrFilePath})"); @@ -481,6 +485,21 @@ private Assembly LoadAssembly(string assemblyNameOrFilePath) LoadAssemblyTypes(assembly); } + catch (BadImageFormatException) + { + if (!allowNativeLibrary) + { + throw; + } + + // This might be a native DLL, not a managed assembly. + // Load the native library, which enables it to be auto-resolved by + // any later DllImport operations for the same library name. + NativeLibrary.Load(assemblyFilePath); + + Trace("< ManagedHost.LoadAssembly() => loaded native library"); + return null!; + } finally { _loadingPath = previousLoadingPath; diff --git a/src/NodeApi.Generator/Program.cs b/src/NodeApi.Generator/Program.cs index e3c76abc..a17e0c4d 100644 --- a/src/NodeApi.Generator/Program.cs +++ b/src/NodeApi.Generator/Program.cs @@ -62,6 +62,20 @@ public static int Main(string[] args) for (int i = 0; i < s_assemblyPaths.Count; i++) { + if (Path.GetFileName(s_assemblyPaths[i]).StartsWith( + typeof(JSValue).Namespace + ".", StringComparison.OrdinalIgnoreCase)) + { + // Never generate type definitions for node-api-dotnet interop assemblies. + continue; + } + + if (s_assemblyPaths.Take(i).Any( + (a) => string.Equals(a, s_assemblyPaths[i], StringComparison.OrdinalIgnoreCase))) + { + // Skip duplicate references. + continue; + } + // Reference other supplied assemblies, but not the current one. List allReferencePaths = s_referenceAssemblyPaths .Concat(s_assemblyPaths.Where((_, j) => j != i)).ToList(); diff --git a/src/NodeApi.Generator/SymbolExtensions.cs b/src/NodeApi.Generator/SymbolExtensions.cs index d971e963..db6558f5 100644 --- a/src/NodeApi.Generator/SymbolExtensions.cs +++ b/src/NodeApi.Generator/SymbolExtensions.cs @@ -82,10 +82,23 @@ public static void Reset() /// public static Type AsType(this ITypeSymbol typeSymbol) { - return typeSymbol.AsType(genericTypeParameters: null); + return typeSymbol.AsType(genericTypeParameters: null, buildType: true); } - private static Type AsType(this ITypeSymbol typeSymbol, Type[]? genericTypeParameters) + /// + /// Gets either the actual type (if it is a system type) or a symbolic type + /// for the type symbol. + /// + /// Generic parameters from the containing type, + /// if the type is a nested type and the containing type is generic. + /// True to force building (AKA emitting) the Type instance; if false + /// then an unbuilt TypeBuilder instance may be returned. (It is a subclass of Type, but + /// does not support some reflection operations.) Delayed type building is necessary in + /// complex object graphs where types have circular references to each other. + private static Type AsType( + this ITypeSymbol typeSymbol, + Type[]? genericTypeParameters, + bool buildType = false) { if (typeSymbol is IArrayTypeSymbol arrayTypeSymbol) { @@ -94,7 +107,7 @@ private static Type AsType(this ITypeSymbol typeSymbol, Type[]? genericTypeParam throw new NotSupportedException("Multi-dimensional arrays are not supported."); } - return arrayTypeSymbol.ElementType.AsType().MakeArrayType(); + return arrayTypeSymbol.ElementType.AsType(genericTypeParameters).MakeArrayType(); } if (typeSymbol is ITypeParameterSymbol typeParameterSymbol) @@ -105,8 +118,12 @@ private static Type AsType(this ITypeSymbol typeSymbol, Type[]? genericTypeParam } else { +#if NETFRAMEWORK throw new NotSupportedException( "Generic type parameters are not supported in this context."); +#else + return Type.MakeGenericMethodParameter(typeParameterSymbol.Ordinal); +#endif } } @@ -115,10 +132,8 @@ private static Type AsType(this ITypeSymbol typeSymbol, Type[]? genericTypeParam throw new NotSupportedException($"Unnamed types are not supported: {typeSymbol}"); } - string typeFullName = typeSymbol.ContainingNamespace + "." + typeSymbol.Name; + string typeFullName = GetTypeSymbolFullName(namedTypeSymbol); ITypeSymbol[] genericArguments = namedTypeSymbol.TypeArguments.ToArray(); - typeFullName += genericArguments.Length > 0 ? - "`" + genericArguments.Length : string.Empty; Type? systemType = typeof(object).Assembly.GetType(typeFullName) ?? typeof(JSValue).Assembly.GetType(typeFullName); @@ -133,6 +148,10 @@ private static Type AsType(this ITypeSymbol typeSymbol, Type[]? genericTypeParam return systemType; } + // Generating the containing type will also generate the nested type, + // so it should be found in the SymbolicTypes dictionary afterward. + typeSymbol.ContainingType?.AsType(); + if (SymbolicTypes.TryGetValue(typeFullName, out Type? symbolicType)) { if (genericArguments.Length > 0) @@ -141,27 +160,26 @@ private static Type AsType(this ITypeSymbol typeSymbol, Type[]? genericTypeParam genericArguments.Select((t) => t.AsType(genericTypeParameters)).ToArray()); } + if (buildType && symbolicType is TypeBuilder typeBuilder) + { + symbolicType = typeBuilder.CreateType()!; + SymbolicTypes[typeFullName] = symbolicType; + } + return symbolicType; } - if (typeSymbol.ContainingType != null) + symbolicType = typeSymbol.TypeKind switch { - Type containingType = typeSymbol.ContainingType.AsType(); - symbolicType = containingType.GetNestedType(typeSymbol.Name)!; - } - else - { - symbolicType = typeSymbol.TypeKind switch - { - TypeKind.Enum => BuildSymbolicEnumType(typeSymbol, typeFullName), + TypeKind.Enum => BuildSymbolicEnumType(namedTypeSymbol, typeFullName), - TypeKind.Class or TypeKind.Interface or TypeKind.Struct or TypeKind.Delegate => - BuildSymbolicObjectType(typeSymbol, typeFullName), + TypeKind.Class or TypeKind.Interface or TypeKind.Struct or TypeKind.Delegate => + BuildSymbolicObjectType( + namedTypeSymbol, typeFullName, genericTypeParameters, buildType), - _ => throw new NotSupportedException( - $"Type kind not supported: {typeSymbol.TypeKind}"), - }; - } + _ => throw new NotSupportedException( + $"Type kind not supported: {typeSymbol.TypeKind}"), + }; // Update the map entry to refer to the built type instead of the type builder. SymbolicTypes[typeFullName] = symbolicType; @@ -175,11 +193,30 @@ private static Type AsType(this ITypeSymbol typeSymbol, Type[]? genericTypeParam return symbolicType; } + /// + /// Gets the full name of a type symbol. It is the same as , + /// but this is used before the Type instance is built from the type symbol. + /// + private static string GetTypeSymbolFullName(INamedTypeSymbol typeSymbol) + { + string ns = typeSymbol.ContainingType != null ? + GetTypeSymbolFullName(typeSymbol.ContainingType) : + typeSymbol.ContainingNamespace.ToString()!; + string name = (ns.Length > 0 ? ns + "." : "") + typeSymbol.Name; + + if (typeSymbol.TypeParameters.Length > 0) + { + name += "`" + typeSymbol.TypeParameters.Length; + } + + return name; + } + private static Type BuildSymbolicEnumType( - ITypeSymbol typeSymbol, + INamedTypeSymbol typeSymbol, string typeFullName) { - Type underlyingType = ((INamedTypeSymbol)typeSymbol).EnumUnderlyingType!.AsType(); + Type underlyingType = typeSymbol.EnumUnderlyingType!.AsType(); EnumBuilder enumBuilder = ModuleBuilder.DefineEnum( typeFullName, TypeAttributes.Public, underlyingType); foreach (IFieldSymbol fieldSymbol in typeSymbol.GetMembers().OfType()) @@ -208,25 +245,41 @@ private static TypeAttributes GetTypeAttributes(TypeKind typeKind) } private static Type BuildSymbolicObjectType( - ITypeSymbol typeSymbol, - string typeFullName) + INamedTypeSymbol typeSymbol, + string typeFullName, + Type[]? genericTypeParameters, + bool buildType) { + Type? baseType = typeSymbol.BaseType?.AsType(genericTypeParameters, buildType); + + // A base type might have had a reference to this type and therefore already defined it. + if (SymbolicTypes.TryGetValue(typeFullName, out Type? thisType)) + { + return thisType; + } TypeBuilder typeBuilder = ModuleBuilder.DefineType( name: typeFullName, GetTypeAttributes(typeSymbol.TypeKind), - parent: typeSymbol.BaseType?.AsType()); + parent: baseType); + + if (typeSymbol.TypeParameters.Length > 0) + { + genericTypeParameters ??= Array.Empty(); + genericTypeParameters = typeBuilder.DefineGenericParameters( + typeSymbol.TypeParameters.Select((p) => p.Name).ToArray()); + } // Add the type builder to the map while building it, to support circular references. SymbolicTypes.Add(typeFullName, typeBuilder); - BuildSymbolicTypeMembers(typeSymbol, typeBuilder); + BuildSymbolicTypeMembers(typeSymbol, typeBuilder, genericTypeParameters); // Preserve JS attributes, which might be referenced by the marshaller. foreach (AttributeData attribute in typeSymbol.GetAttributes()) { - if (attribute.AttributeClass!.ContainingAssembly.Name == - typeof(JSExportAttribute).Assembly.GetName().Name) + if (attribute.AttributeClass!.ContainingNamespace.ToString()!.StartsWith( + typeof(JSExportAttribute).Namespace!)) { Type attributeType = attribute.AttributeClass.AsType(); ConstructorInfo constructor = attributeType.GetConstructor( @@ -247,12 +300,46 @@ static PropertyInfo GetAttributeProperty(Type type, string name) => type.GetProperty(name, BindingFlags.Public | BindingFlags.Instance) ?? throw new MissingMemberException( $"Property {name} not found on attribute {type.Name}."); + + if (!buildType) + { + return typeBuilder; + } + + // Ensure the base type is built before building the derived type. + if (baseType != null) + { + BuildBaseType(typeSymbol); + } + + // Ensure this type is only built once. + if (SymbolicTypes.TryGetValue(typeFullName, out thisType) && thisType is not TypeBuilder) + { + return thisType; + } + return typeBuilder.CreateType()!; } + private static void BuildBaseType(INamedTypeSymbol typeSymbol) + { + string baseTypeFullName = GetTypeSymbolFullName(typeSymbol.BaseType!); + if (SymbolicTypes.TryGetValue(baseTypeFullName, out Type? baseType) && + baseType is TypeBuilder baseTypeBuilder) + { + if (typeSymbol.BaseType != null) + { + BuildBaseType(typeSymbol.BaseType); + } + + baseTypeBuilder.CreateType(); + } + } + private static void BuildSymbolicTypeMembers( ITypeSymbol typeSymbol, - TypeBuilder typeBuilder) + TypeBuilder typeBuilder, + Type[]? genericTypeParameters) { foreach (Type interfaceType in typeSymbol.Interfaces.Select(AsType)) { @@ -271,11 +358,11 @@ private static void BuildSymbolicTypeMembers( (methodSymbol.MethodKind == MethodKind.Ordinary || methodSymbol.MethodKind == MethodKind.DelegateInvoke)) { - BuildSymbolicMethod(typeBuilder, methodSymbol); + BuildSymbolicMethod(typeBuilder, methodSymbol, genericTypeParameters); } else if (memberSymbol is IPropertySymbol propertySymbol) { - BuildSymbolicProperty(typeBuilder, propertySymbol); + BuildSymbolicProperty(typeBuilder, propertySymbol, genericTypeParameters); } else if (memberSymbol is IEventSymbol eventSymbol) { @@ -297,9 +384,8 @@ private static void BuildSymbolicTypeMembers( parent: nestedTypeSymbol.TypeKind == TypeKind.Enum ? typeof(Enum) : null); // TODO: Handle nested enum members (fields). - BuildSymbolicTypeMembers(nestedTypeSymbol, nestedTypeBuilder); - - nestedTypeBuilder.CreateType(); + BuildSymbolicTypeMembers( + nestedTypeSymbol, nestedTypeBuilder, genericTypeParameters); } } } @@ -336,7 +422,9 @@ private static ConstructorBuilder BuildSymbolicConstructor( } private static void BuildSymbolicMethod( - TypeBuilder typeBuilder, IMethodSymbol methodSymbol) + TypeBuilder typeBuilder, + IMethodSymbol methodSymbol, + Type[]? genericTypeParameters) { bool isDelegateMethod = typeBuilder.BaseType == typeof(MulticastDelegate); MethodAttributes attributes = MethodAttributes.Public | (methodSymbol.IsStatic ? @@ -347,16 +435,21 @@ private static void BuildSymbolicMethod( attributes, methodSymbol.IsStatic ? CallingConventions.Standard : CallingConventions.HasThis); - GenericTypeParameterBuilder[]? genericTypeParameters = null; - if (methodSymbol.IsGenericMethod) + GenericTypeParameterBuilder[]? genericMethodParameters = null; + if (methodSymbol.TypeParameters.Length > 0) { - genericTypeParameters = methodBuilder.DefineGenericParameters( + genericMethodParameters = methodBuilder.DefineGenericParameters( methodSymbol.TypeParameters.Select((p) => p.Name).ToArray()); } - methodBuilder.SetReturnType(methodSymbol.ReturnType.AsType(genericTypeParameters)); + Type[]? genericParameters = + genericTypeParameters == null ? genericMethodParameters : + genericMethodParameters == null ? genericTypeParameters : + genericTypeParameters.Concat(genericMethodParameters).ToArray(); + + methodBuilder.SetReturnType(methodSymbol.ReturnType.AsType(genericParameters)); methodBuilder.SetParameters( - methodSymbol.Parameters.Select((p) => p.Type.AsType(genericTypeParameters)).ToArray()); + methodSymbol.Parameters.Select((p) => p.Type.AsType(genericParameters)).ToArray()); BuildSymbolicParameters(methodBuilder, methodSymbol.Parameters); if (isDelegateMethod) @@ -373,13 +466,16 @@ private static void BuildSymbolicMethod( } private static void BuildSymbolicProperty( - TypeBuilder typeBuilder, IPropertySymbol propertySymbol) + TypeBuilder typeBuilder, + IPropertySymbol propertySymbol, + Type[]? genericTypeParameters) { PropertyBuilder propertyBuilder = typeBuilder.DefineProperty( propertySymbol.Name, PropertyAttributes.None, - propertySymbol.Type.AsType(), - propertySymbol.Parameters.Select((p) => p.Type.AsType()).ToArray()); + propertySymbol.Type.AsType(genericTypeParameters), + propertySymbol.Parameters.Select( + (p) => p.Type.AsType(genericTypeParameters)).ToArray()); MethodAttributes attributes = MethodAttributes.SpecialName | MethodAttributes.Public | (propertySymbol.IsStatic ? MethodAttributes.Static : @@ -391,8 +487,9 @@ private static void BuildSymbolicProperty( propertySymbol.GetMethod.Name, attributes, propertySymbol.IsStatic ? CallingConventions.Standard : CallingConventions.HasThis, - propertySymbol.GetMethod.ReturnType.AsType(), - propertySymbol.GetMethod.Parameters.Select((p) => p.Type.AsType()).ToArray()); + propertySymbol.GetMethod.ReturnType.AsType(genericTypeParameters), + propertySymbol.GetMethod.Parameters.Select( + (p) => p.Type.AsType(genericTypeParameters)).ToArray()); BuildSymbolicParameters(getMethodBuilder, propertySymbol.GetMethod.Parameters); if (propertySymbol.IsStatic) getMethodBuilder.GetILGenerator().Emit(OpCodes.Ret); propertyBuilder.SetGetMethod(getMethodBuilder); @@ -404,8 +501,9 @@ private static void BuildSymbolicProperty( propertySymbol.SetMethod.Name, attributes, propertySymbol.IsStatic ? CallingConventions.Standard : CallingConventions.HasThis, - propertySymbol.SetMethod.ReturnType.AsType(), - propertySymbol.SetMethod.Parameters.Select((p) => p.Type.AsType()).ToArray()); + propertySymbol.SetMethod.ReturnType.AsType(genericTypeParameters), + propertySymbol.SetMethod.Parameters.Select( + (p) => p.Type.AsType(genericTypeParameters)).ToArray()); BuildSymbolicParameters(setMethodBuilder, propertySymbol.SetMethod.Parameters); if (propertySymbol.IsStatic) setMethodBuilder.GetILGenerator().Emit(OpCodes.Ret); propertyBuilder.SetSetMethod(setMethodBuilder); @@ -440,6 +538,13 @@ public static ConstructorInfo AsConstructorInfo(this IMethodSymbol methodSymbol) } Type type = methodSymbol.ContainingType.AsType(); + + // Ensure constructor parameter types are built. + foreach (IParameterSymbol parameter in methodSymbol.Parameters) + { + parameter.Type.AsType(type.GenericTypeArguments, buildType: true); + } + ConstructorInfo? constructorInfo = type.GetConstructor( methodSymbol.Parameters.Select((p) => p.Type.AsType()).ToArray()); return constructorInfo ?? throw new InvalidOperationException( @@ -452,6 +557,18 @@ public static ConstructorInfo AsConstructorInfo(this IMethodSymbol methodSymbol) public static MethodInfo AsMethodInfo(this IMethodSymbol methodSymbol) { Type type = methodSymbol.ContainingType.AsType(); + + // Ensure method parameter and return types are built. + Type[] typeParameters = type.GetGenericArguments(); + foreach (IParameterSymbol parameter in methodSymbol.Parameters) + { + IEnumerable methodTypeParameters = + methodSymbol.TypeParameters.Select((t) => t.AsType(typeParameters)); + parameter.Type.AsType( + typeParameters.Concat(methodTypeParameters).ToArray(), buildType: true); + } + methodSymbol.ReturnType.AsType(type.GenericTypeArguments, buildType: true); + BindingFlags bindingFlags = BindingFlags.Public | (methodSymbol.IsStatic ? BindingFlags.Static : BindingFlags.Instance); MethodInfo? methodInfo = type.GetMethods(bindingFlags) @@ -468,6 +585,10 @@ public static MethodInfo AsMethodInfo(this IMethodSymbol methodSymbol) public static PropertyInfo AsPropertyInfo(this IPropertySymbol propertySymbol) { Type type = propertySymbol.ContainingType.AsType(); + + // Ensure the property type is built. + propertySymbol.Type.AsType(type.GenericTypeArguments, buildType: true); + BindingFlags bindingFlags = BindingFlags.Public | (propertySymbol.IsStatic ? BindingFlags.Static : BindingFlags.Instance); PropertyInfo? propertyInfo = type.GetProperty(propertySymbol.Name, bindingFlags); diff --git a/src/NodeApi.Generator/TypeDefinitionsGenerator.cs b/src/NodeApi.Generator/TypeDefinitionsGenerator.cs index 4c562898..096393c0 100644 --- a/src/NodeApi.Generator/TypeDefinitionsGenerator.cs +++ b/src/NodeApi.Generator/TypeDefinitionsGenerator.cs @@ -145,8 +145,7 @@ public static void GenerateTypeDefinitions( // Drop reference assemblies that are already in any system ref assembly directories. // (They would only support older framework versions.) referenceAssemblyPaths = referenceAssemblyPaths.Where( - (r) => Path.GetFileNameWithoutExtension(r).Equals("WindowsBase") || - !systemAssemblies.Any((a) => Path.GetFileName(a).Equals( + (r) => !systemAssemblies.Any((a) => Path.GetFileName(a).Equals( Path.GetFileName(r), StringComparison.OrdinalIgnoreCase))); PathAssemblyResolver assemblyResolver = new(