diff --git a/examples/winappsdk/WinAppSdkExample.csproj b/examples/winappsdk/WinAppSdkExample.csproj
new file mode 100644
index 00000000..92c01a5f
--- /dev/null
+++ b/examples/winappsdk/WinAppSdkExample.csproj
@@ -0,0 +1,37 @@
+
+
+
+ net8.0-windows10.0.19041.0
+ 10.0.17763.0
+ bin
+ x86;x64;ARM64
+ win-x86;win-x64;win-arm64
+ CommonJs
+
+
+ ABI.*;WinRT.IInspectable+Vftbl*;WinRT.Interop.IUnknownVftbl*
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/examples/winappsdk/example.js b/examples/winappsdk/example.js
new file mode 100644
index 00000000..9b6bd40f
--- /dev/null
+++ b/examples/winappsdk/example.js
@@ -0,0 +1,20 @@
+
+
+const dotnet = require('node-api-dotnet');
+
+require('./bin/Microsoft.Windows.SDK.NET.js');
+require('./bin/Microsoft.WindowsAppRuntime.Bootstrap.Net.js');
+require('./bin/Microsoft.InteractiveExperiences.Projection.js');
+require('./bin/Microsoft.Windows.ApplicationModel.WindowsAppRuntime.Projection.js');
+require('./bin/Microsoft.Windows.AI.Text.Projection.js');
+require('./bin/Microsoft.Windows.AI.ContentSafety.Projection.js');
+require('./bin/Microsoft.Windows.AppNotifications.Projection.js');
+require('./bin/Microsoft.Windows.AppNotifications.Builder.Projection.js');
+
+const majorVersion = 1;
+const minorVersion = 8;
+
+console.log("Attempt to initialize the WindowsAppRuntime Bootstrapper. (This requires the WindowsAppRuntime " + majorVersion + "." + minorVersion + " to be installed on the system.)");
+const fullVersion = (majorVersion << 16) | minorVersion;
+dotnet.Microsoft.Windows.ApplicationModel.DynamicDependency.Bootstrap.Initialize(fullVersion);
+console.log("Initialized Bootstraper. WindowsAppRuntime is now available.");
diff --git a/examples/winappsdk/package.json b/examples/winappsdk/package.json
new file mode 100644
index 00000000..e628bc64
--- /dev/null
+++ b/examples/winappsdk/package.json
@@ -0,0 +1,6 @@
+{
+ "name": "node-api-dotnet-examples-winappsdk",
+ "dependencies": {
+ "node-api-dotnet": "file:../../out/pkg/node-api-dotnet"
+ }
+}
diff --git a/src/NodeApi.Generator/NodeApi.Generator.targets b/src/NodeApi.Generator/NodeApi.Generator.targets
index 66e9ce62..3c572c62 100644
--- a/src/NodeApi.Generator/NodeApi.Generator.targets
+++ b/src/NodeApi.Generator/NodeApi.Generator.targets
@@ -71,6 +71,7 @@
+
@@ -171,6 +172,7 @@
+
diff --git a/src/NodeApi.Generator/Program.cs b/src/NodeApi.Generator/Program.cs
index bc4b7a7b..c41f8039 100644
--- a/src/NodeApi.Generator/Program.cs
+++ b/src/NodeApi.Generator/Program.cs
@@ -39,6 +39,7 @@ public static class Program
private static readonly List s_typeDefinitionsPaths = new();
private static readonly HashSet s_systemAssemblyIndexes = new();
private static readonly List s_moduleTypes = new();
+ private static readonly List s_excludePatterns = new();
private static bool s_suppressWarnings;
public static int Main(string[] args)
@@ -56,6 +57,7 @@ public static int Main(string[] args)
-t --typedefs Path to output type definitions file (required)
-m --module Generate JS module(s) alongside typedefs (optional, multiple)
Value: 'commonjs', 'esm', or path to package.json with "type"
+ -e --exclude Exclude types matching wildcard patterns from metadata parsing (optional)
--nowarn Suppress warnings
-? -h --help Show this help message
@ Read response file for more options
@@ -108,7 +110,8 @@ public static int Main(string[] args)
modulePaths,
s_targetFramework,
isSystemAssembly: s_systemAssemblyIndexes.Contains(i),
- s_suppressWarnings);
+ s_suppressWarnings,
+ s_excludePatterns);
}
return 0;
@@ -206,6 +209,11 @@ void AddItems(List list, string items)
}
break;
+ case "-e":
+ case "--exclude":
+ AddItems(s_excludePatterns, args[++i]);
+ break;
+
case "--nowarn":
s_suppressWarnings = true;
break;
diff --git a/src/NodeApi.Generator/TypeDefinitionsGenerator.cs b/src/NodeApi.Generator/TypeDefinitionsGenerator.cs
index 80e4405d..f4498064 100644
--- a/src/NodeApi.Generator/TypeDefinitionsGenerator.cs
+++ b/src/NodeApi.Generator/TypeDefinitionsGenerator.cs
@@ -218,7 +218,8 @@ public static void GenerateTypeDefinitions(
IDictionary modulePaths,
string? targetFramework = null,
bool isSystemAssembly = false,
- bool suppressWarnings = false)
+ bool suppressWarnings = false,
+ IEnumerable? excludePatterns = null)
{
if (string.IsNullOrEmpty(assemblyPath))
{
@@ -282,6 +283,7 @@ public static void GenerateTypeDefinitions(
SuppressWarnings = suppressWarnings,
ExportAll = assemblyExportAttribute != null &&
GetExportAttributeValue(assemblyExportAttribute),
+ ExcludePatterns = excludePatterns?.ToList() ?? new List(),
};
generator.LoadAssemblyDocs();
@@ -380,6 +382,8 @@ public TypeDefinitionsGenerator(
public bool SuppressWarnings { get; set; }
+ public List ExcludePatterns { get; set; } = new();
+
public override void ReportDiagnostic(Diagnostic diagnostic)
{
if (SuppressWarnings && diagnostic.Severity == DiagnosticSeverity.Warning)
@@ -1260,8 +1264,23 @@ private void EndNamespace(ref SourceBuilder s, Type type)
}
}
- private static bool IsExcluded(MemberInfo member)
+ private bool IsExcluded(MemberInfo member)
{
+ Type type = member as Type ?? member.DeclaringType!;
+
+ // Check user-provided exclude patterns first
+ if (ExcludePatterns.Count > 0)
+ {
+ string typeFullName = type.FullName ?? type.Name;
+ foreach (string pattern in ExcludePatterns)
+ {
+ if (IsWildcardMatch(typeFullName, pattern))
+ {
+ return true;
+ }
+ }
+ }
+
if (member is PropertyInfo property)
{
return IsExcluded(property);
@@ -1271,8 +1290,6 @@ private static bool IsExcluded(MemberInfo member)
return IsExcluded(method);
}
- Type type = member as Type ?? member.DeclaringType!;
-
if (type.BaseType != null && IsExcluded(type.BaseType))
{
return true;
@@ -1301,14 +1318,41 @@ private static bool IsExcluded(MemberInfo member)
};
}
- private static bool IsExcluded(PropertyInfo property)
+ ///
+ /// Checks if a string matches a wildcard pattern. Supports * (any characters) and ? (single character).
+ ///
+ private static bool IsWildcardMatch(string input, string pattern)
{
- if (property.PropertyType.IsPointer)
+ if (string.IsNullOrEmpty(pattern)) return false;
+ if (string.IsNullOrEmpty(input)) return false;
+
+ // Convert wildcard pattern to regex pattern
+ string regexPattern = "^" + Regex.Escape(pattern)
+ .Replace(@"\*", ".*")
+ .Replace(@"\?", ".") + "$";
+
+ return Regex.IsMatch(input, regexPattern, RegexOptions.IgnoreCase);
+ }
+
+ private bool IsExcluded(PropertyInfo property)
+ {
+ Type propertyType;
+ try
+ {
+ propertyType = property.PropertyType;
+ }
+ catch (System.NotSupportedException e)
+ {
+ Console.WriteLine($"Error: Property {property.DeclaringType!.FullName}.{property.Name} could not be analyzed. ({e.GetType().Name})");
+ throw;
+ }
+
+ if (propertyType.IsPointer)
{
return true;
}
- if (IsExcluded(property.PropertyType))
+ if (IsExcluded(propertyType))
{
return true;
}
@@ -1324,11 +1368,22 @@ private bool IsExcluded(MethodBase method)
return true;
}
+ System.Reflection.ParameterInfo[] methodParams;
+ try
+ {
+ methodParams = method.GetParameters();
+ }
+ catch (System.NotSupportedException e)
+ {
+ Console.WriteLine($"Error: Method {method.DeclaringType!.FullName}.{method.Name}() could not be analyzed. ({e.GetType().Name})");
+ throw;
+ }
+
// Exclude old style Begin/End async methods, as they always have Task-based alternatives.
if ((method.Name.StartsWith("Begin") &&
(method as MethodInfo)?.ReturnType.FullName == typeof(IAsyncResult).FullName) ||
- (method.Name.StartsWith("End") && method.GetParameters().Length == 1 &&
- method.GetParameters()[0].ParameterType.FullName == typeof(IAsyncResult).FullName))
+ (method.Name.StartsWith("End") && methodParams.Length == 1 &&
+ methodParams[0].ParameterType.FullName == typeof(IAsyncResult).FullName))
{
return true;
}